Hello:
I just went through a Lua tutorial to refresh my memory on how the syntax works and the method of how functions and tables specifically act in that. So, I've decided to share some of these more Lua-specific ideals here, since many are just learning the language due to a recent spike in CX programming activity in the TI community. So, since many only know the basics at the moment, I've decided to do my part and educate the masses of some of Lua's beautiful more advanced workings
TablesYou can think of a table as the only real class-like variable container in the Lua language (Lua can be expanded to work with OOP, but in my experience it is a very weak implementation and I'm not going to explain it in detail here). Tables can be thought of as the 'universal container' since they can hold anything inside of themselves. Tables can in a way be thought of as Structs in C, as they hold memebers in a group without class-like properties. The difference however, is that tables are beautiful in the fact that they can hold just about anything you can think of. They can hold variables, functions, and even other tables. This programming style is a bit harder to grasp for people used to OOP programming, but I'll try to make it simple enough to change your view so you can grasp it
Here is an example of a table:
Table = {Apple = "Macintosh", Int_thing = 55, function foo() print("Hello") end, Letters = {a = "a", b = "b"}}
This is all valid Lua code when instantiating a Table. Like structs, members of these Tables (the things inside of them) can be called pretty easily:
print(Table.Apple) -- prints "Macintosh"
And similarly, when it contains other tables or functions, it can be chained like this:
print(Table.Letters.a) -- prints "a"
Table.foo() -- prints "Hello"
As you can see, this can be extremely useful and is in many ways similar to objects in OOP. However, some differences from classes is that each time you activate one, it's an actual variable and is not treated like a "Class", since it's just a container and doesn't have finalization or instantiation methods or other things classes have. It's also much more flexible with how things work. HOWEVER, if you DO want to pretend it is OOP, you can fake it in a way by making it look Ruby Style. In the Ruby Style of Lua, things are very much like Ruby in how tables are treated like classes and objects. Below is an example of how a mainframe 'Class' table is made and how a new 'Instance' table is derived from that:
String_thing = { -- our class
value = "__empty__",
function print_value()
print(self.value)
end
function new(new_table_name, new_string) -- 'copy'-constructor
new_table_name = self
new_table_name.value = new_string
end
}
String_thing.new(String_inst = {null}, "I'm a new string!") -- the actual instantiation
-- Now String_inst is now == to the 'class' String_thing and is a new instance
Anonimity in FunctionsAnonymous functions are the pinnacle of functional programming and can be extremely useful in many cases. Before I go into detail, I want you to know the difference between a normal function and an anonymous one. A normal one can return a value in many cases and may execute code as well. These are treated as functions as you normally are used to with Lua. However, Anonymous functions are much different. They are made to represent values, though with possible code to determine their value based on inputs. In many languages like Python and Ruby, these are called either Lambdas or Procs. In Ruby, it would be this (I'll use Ruby as an example as the Lua version is a bit harder to follow and can't be used as easily in this type of example):
Foo = lambda {|value,root| value**root}
# the value of Foo is a lambda that equates to the input value to the root's.. root
Foo.call(2,16) # Foo is now equal to 65536, which is 2**16
It's a bit less straightfoward in Lua, and I won't show a Lua example until I can get into the next fray: closures.
ClosuresClosures in Lua are a bit like they are in other languages. In Ruby (and maybe Python) they are called Procs -- they are basically embedded lambdas that serve no other purpose than returning a value that is not specifically attached to a variable at all. An example is when you embed one into an actual function:
function example(cells, generation)
return cells + (function ()
return generation % cells
end
end
In this case, we would be calling a lambda form within a function, making it a Proc on the spot. In this, it simply returns (Cells + (Generation % Cells)) -- not a useful example, but a decently explaining one overall. When you embed Procs inside of Procs, you are then fulfilling a convention called
Currying:
function example(cells, generation, apple)
return cells + (function ()
return generation % (function ()
apple - cells
end
end
end
Curry can be very useful but can also really make your code hard to follow.
On a side not, non-anonmymous functions can be used in an anonymous form, though what I mean by this is completely different in function (no pun intended) than what I said before. Therefore, I'll just call them "namable" functions. What am I talking about? I'm talking about a powerful example of the flexibility of Lua function-calling conventions:
apple = {show_good_stuff = print}
apple.show_good_stuff("Hello, world!) -- does print("Hello, world!)
While using Namable functions like that isn't nearly as useful as other possible conventions, it can be very useful if you either want to rename a function or even a variable OR if you want to index functions in a table for quick access:
function_list = {i1 = print, i2 = reverse, i3 = input}
function_list.i1("Hello World!")
Polymorphic Class TablesThis is a broad set of concepts that I'm condensing down to only a small section. You see, in Java and the like, you have things such as instanceof, interfaces, and more. How do these work in Lua?
In Lua, determining types of instances is actually quite easy, but requires one more variable in each class:
function instanceof(instance,class)
return (instance.type == class.type) ? true : false
end
String_Class = {
function new(new_inst, new_value)
self.value = new_value
return self
end
type = String_Class
}
new_string = String_Class.new()
instanceof(new_String,String_Class) -- returns true
As you can see, that's actually quite simple
However, interfaces are a bit different. Interfaces work as modeling classes, so in a way they are 'classes of classes'. Here is an example of setting up a class in sync with an interface:
function SetupClassToInt(class, interface)
for k,v in pairs(second_table) do first_table[k] = v end
end
Int_Thanks = {
function new()
print "Hello!"
end
partof = "Int_Thanks"
}
function _partof_(inter,class)
return (class.partof == inter.partof) ? true : false
end
Class = {null}
SetupClassToInt(Class,Int_Thanks)
_partof_(Class,Int_Thanks) -- returns true
It's simple once you get into the swing of things
Thanks for putting up with me for now! But, don't think I'm gone yet, I've only gone over a few cool little things can in Lua -- I plan on ading more later! Things to stay tuned for:
- Polymorphic Class Tables
- Inheritance with Table-Based OOP
- Tricks with Lua glitches
- And more! Suggest what you want, I'll post it! For now, your Lua friend,
~ Ashbad