No classes? How do you guys survive?
Yes, there is no class
statement in Lua, but doing OOP in Lua is not difficult. For a simple approach to single inheritance, see SimpleLuaClasses; for an overview, see ObjectOrientedProgramming. As a general piece of advice, pick a scheme, keep it simple, and be consistent.
Here is a sample from the first reference:
-- animal.lua require 'class'
Animal = class(function(a,name)
a.name = name end)
function Animal:__tostring()
return self.name..': '..self:speak()
end Dog = class(Animal)
function Dog:speak()
return 'bark'
end Cat = class(Animal, function(c,name,breed)
Animal.init(c,name) -- must init base!
c.breed = breed
end)
function Cat:speak()
return 'meow'
end Lion = class(Cat)
function Lion:speak()
return 'roar'
end fido = Dog('Fido')
felix = Cat('Felix','Tabby')
leo = Lion('Leo','African')
D:\Downloads\func>lua -i animal.lua
> = fido,felix,leo Fido: bark Felix: meow Leo: roar
> = leo:is_a(Animal)
true
> = leo:is_a(Dog)
false
> = leo:is_a(Cat)
true
Some method names are special, like __tostring
("metamethods"). Defining this function controls how our objects are represented as strings.
The function TABLE:METHOD(args)
form is another little bit of syntactic sugar, it is entirely equivalent to function TABLE.METHOD(self,args)
. That is, these are the same method definitions:
function Animal.feed(self,food)
...
end function Animal:feed(food)
...
end
The beauty of Lua is that you have freedom to choose the notation you are comfortable with. For instance, we could define this syntax:
class 'Lion': inherit 'Cat' {
speak = function(self)
return 'roar'
end
}
This seems like magic at first, but the first line is really class('Lion'):inherit('Cat'):({
, so it cleverly uses the fact that Lua will accept single function arguments without parentheses if the argument is a string or a table. A 'class' object is of course not a string, so the convention here is that classes are kept in the current module context (which would usually be the global table). One then has the opportunity to name the classes, which is a useful aid to debugging - for instance, it is then easy to give such classes a default __tostring
which prints out their address and name.
Multiple Inheritance can be implemented, but there is a run-time cost compared to simple schemes.
On the other hand, here is an argument against needing inheritance in the first place: "In fully dynamic languages, where there's no compile-time type checking, there's no need to assure any pre-set commonality structure between similar objects. A given function can receive any kind of object, as long as it implements this and that methods".
The problem with not having a 'canonical' OOP scheme comes when integrating Lua code that uses an incompatible scheme. Then all you can assume is that an object can be called with a:f()
notation. Re-use of classes via inheritance is only possible if you know how that class is structured. This can be considered a problem that hinders adoption of classic OOP style in Lua.
If inheritance is not an issue, the following pattern is a quick way to create 'fat objects':
function MyObject(n)
local name -- this is our field
local obj = {} -- this is our object
function obj:setName(n)
name = n
end
function obj:getName()
return name
end
return obj end
...
> o = MyObject 'fido'
> = o:getName()
fido
> o:setName 'bonzo'
> = o:getName()
bonzo
The actual table returned only contains the two methods; the state of the object is entirely encapsulated in the closures. The non-local variable name
(or 'upvalue') represents the object state.
Note that if this code was rewritten so that '.' was used instead of ':', then you can get dot notation for these objects, that is, o.getName()
. This is however not a good idea, since Lua programmers expect to use ':' and you may wish to change the implementation later.
Generally, this approach gives the fastest method dispatch at the cost of the method closures and their table per object. If there are only a few such objects, this cost may be worth paying.