As a complete Lua newbie, it took me a while to figure out @Yu Hao's answer, so I will try to add some details for other newbies. Please correct me if something is wrong.
As far as I can see, a call like x:someFunc()
works if [*]:
x
has metal- and metatable has an
__index
field - which points to a table containing
someFunc
function.
As Yu Hao pointed out, strings automatically get a meta, pointing to a string
table, for example:
th> s = 'test' th> getmetatable(s) { __mod : function: 0x40c3cd30 __index : { upper : function: builtin
So, in this case s:myFunc()
works automatically. To use the colon syntax for table
, we can manually set its metatable:
th> function enableColonForTable(t) ..> meta = {__index = table} ..> setmetatable(t, meta) ..> end th> t = {} th> enableColonForTable(t) th> t:insert(1) -- works now!
Another note is that it doesn't really matter if __index
to a table with exactly the same name as the type. Instead of meta = {__index = table}
we could also do:
th> arbitraryScope = {} th> function arbitraryScope:test() return "something" end th> t = {} th> setmetatable(t, {__index = arbitraryScope}) {} th> t:test() something
This is also the key difference from the number
case. Although there are existing tables named string
and table
, there is no existing table named number
. That is why even the definition, for example, function number:abs()
failed. But we can still make it work:
th> number = {} th> function number:abs() return math.abs(self) end th> x = -123 th> debug.setmetatable(x, {__index = number}) -123 th> x:abs() 123
Note that we had to use debug.setmetatable
instead of setmetatable
here. The difference between the two seems to be that setmetatable
sets the metatable only for this instance, and debug.setmetatable
sets the metatable for the whole type. Obviously, setting an individual metatet for numbers is prohibited (and does not make any sense in any case). This means that (unlike tables), new numbers now have a default meta-furniture, so this works:
th> y = -42 th> y:abs() 42
[*] Update
As Tom Blodget noted, x:someFunc()
also works if x
itself serves as a namespace, i.e. This is a table with the someFunc
method field. For example, you can do table:insert(1)
. But now the namespace (a table called table
) is passed as self
, and you would add data to the namespace:
th> print(getmetatable(table)) -- note: "table" does not have a metatable nil th> table:insert(1) -- yet a colon syntax call works th> table { prune : function: 0x4156bde0 getn : function: 0x41eb0720 maxn : function: builtin#90 remove : function: 0x41eb08c8 foreachi : function: 0x41eb05b8 sort : function: builtin#93 concat : function: builtin#92 unpack : function: builtin#16 splice : function: 0x4156bdc0 foreach : function: 0x41eb0688 1 : 1 pack : function: builtin#94 insert : function: builtin#91 }