Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Trying to understand this lua snippet

Tags:

lua

I am trying to understand what this function does. Can anyone explain this to me?

function newInstance (class)
    local o = {}
    setmetatable (o, class)
    class.__index = class
    return o
end

It is called like this:

self = newInstance (self)
like image 768
chchrist Avatar asked Mar 08 '12 11:03

chchrist


3 Answers

This function apparently serves to provide a variant of OOP in Lua (a bit sloppy in my opinion).

This is a factory for a class.

It may be rewritten as follows, for clarity:

C = { }

C.foo = function(self) -- just some method, so class would not be empty
  print("foo method called", tostring(self))
end

C.__index = C -- (A)

function newInstance(class)
  return setmetatable({ }, class) -- (B)
end

Now if we create two new instances of C, we clearly see that both have a method foo(), but different self:

o1 = newInstance(C)
o1:foo() --> foo method called  table: 0x7fb3ea408ce0

o2 = newInstance(C)
o2:foo() --> foo method called  table: 0x7fb3ea4072f0

The foo methods are the same:

print(o1.foo, o2.foo, o1.foo == o2.foo and "equal" or "different")
--> function: 0x7fb3ea410760    function: 0x7fb3ea410760    equal

This is because table C (the "class") is an __index ((A) above) of metatable of o1 and o2, set in (B). But o1 and o2 are actually two different tables, created at (B) (the "class instances" or "objects").

Note: we set C.__index to be equal to C itself at (A) so we would reuse one table. Following has the same effect:

prototype = { }

prototype.foo = function(self) -- just some method, so class would not be empty
  print("foo method called", tostring(self))
end

C = { __index = prototype }

function newInstance(class)
  return setmetatable({ }, class)
end

o = newInstance(C)
like image 163
Alexander Gladysh Avatar answered Oct 02 '22 11:10

Alexander Gladysh


This is used to achieve object-oriented behavior in Lua. Lua uses prototype-based inheritance (similar to Javascript) instead of class-based inheritance (like Java or C++). The idea is that all class methods are implemented as part of a prototype object and inheriting objects delegate to the prototype upon call.

For example, assume you want a class myClass providing a method getMagicNumber:

local myClass = { 
     getMagicNumber = function(self) 
         return self.theNumber;
     end };

local obj = newInstance(myClass);
obj.theNumber = 42;
print(obj:getMagicNumber()); -- supposed to return 42

It's kind of a trivial example, but I hope you get the idea. What happens is this: newInstance creates a new table with its metatable pointing to myClass and also ensuring that the __index field of the metatable points to myClass as well. As the Lua manual on __index describes, when you now attempt to call getMagicNumber on that new table, the following happens: First Lua attempts to find the field getMagicNumber in the obj table. Since that table is empty though, it now searches its __index table (myClass) for that field. Since the field is found there, it now calls the function found in myClass.

Programming in Lua discusses this mechanism in great detail if you need to know more.

like image 44
ComicSansMS Avatar answered Oct 02 '22 10:10

ComicSansMS


You're not alone it took me a fair bit of time to grok this code. Here's my interpretation

each table can have a netatable, the metatable can store amongst other things functions indexed by name, and setmetatable sets the value of the metatable naturally enough.

The line class._index = .. is where the magic happens.

When you try to call a function using self.fn1(), Then lua looks up in the self a name fn1. If it can't find it then it looks in self's metatable for a table __index, and looks for a 'fn1' in the table stored there.

Now the metatable for self in your example is class, so lua looks for an __index entry in the metatable (class) to see if there's a function named fn1 in that table, and there is, so it's returned. You need to set the __index for class back to itself so that lua will look in the same metatable - confusing hey.

(as an aside the assignment to __index only needs to happen once, but for some reason it's always done in the new operator in all the lua examples - I take it outside of the new in my classes - saves a few cycles)

You then return the new table o, lua is garbage collected so returning a local creates a new table each time.

function newInstance (class)
    local o = {}
    setmetatable (o, class)
    class.__index = class
    return o
end

hth

like image 21
daven11 Avatar answered Oct 02 '22 12:10

daven11