How "self" gets to access the key of a local table? - lua
I am new to Lua, I am studying this video. At 38:56, a code is presented as below:
Sequence = {}
function Sequence:new()
local new_seq = {last_num = 0}
self.__index = self
return setmetatable(new_seq, self)
end
function Sequence:next()
print(self.last_num)
end
My understanding is that self is equivalent to Sequence, and self is set as a metatable of new_seq, and this metatable's __index is also self.
And last_num is one key of table new_seq but not one key of self, how come in the definition of next function you can write self.last_num as treating last_num is one key of self?
Moreover, before calling setmetatable, there is self.__index = self, I thought only metatable has __index as a special key, but before calling
setmetatable, Sequence is just a regular table and it is not a metatable yet, how come it has __index?
Short version:
My understanding is that self is equivalent to Sequence…
self is an implicit argument resulting from the method-style function definition. Within the function, it will refer to whatever value gets passed in as the first argument to that function.
(The rest of your questions around self arise from that same confusion.)
I thought only metatable has __index as a special key…
A metatable is just a table. __index is just a key like any other, you can define a field with that name on any table. Only when a lookup on a table fails and Lua notices that this table has an attached metatable, the metatable's field named __index has a special meaning – because that's where Lua will look for a handler.
__index containing a table is just another special case of a handler (because it's so common), __index = some_other_table is roughly equivalent to __index = function( table, key ) return some_other_table[key] end – i.e. "go look over there in some_other_table if table[key] was empty". (It may help to use the long version and print something from there if you have trouble following what happens.)
Long version, de-sugaring the code and walking through the details:
A definition function foo:bar( ... ) is the same as function foo.bar( self, ... ) (the name self is automatically chosen, roughly like this in other languages). Additionally, function foo.bar( ... ) is the same as foo.bar = function( ... ). Which means the above code is the same as…
Sequence = {}
Sequence.new = function( self )
local new_seq = { last_num = 0 }
self.__index = self
return setmetatable( new_seq, self )
end
Sequence.next = function( self )
print( self.last_num )
end
…which is equivalent to…
Sequence = {
new = function( self )
local new_seq = { last_num = 0 }
self.__index = self
return setmetatable( new_seq, self )
end,
next = function( self )
print( self.last_num )
end,
}
So, in essence, what this defines is a table that contains two functions, each taking a single parameter. The second of the two functions, next, is pretty simple: it just prints the content of the field last_num of whatever table it's passed (using the name self to refer to it).
Now, just as for definitions, there's some :-syntax sugar for calls. A call foo:bar( ... ) translates to foo.bar( foo, ... ), so when you have some_sequence and say some_sequence:next( ), what happens is a call some_sequence.next( some_sequence ) – the :-syntax for definitions introduces an extra hidden parameter and the :-syntax for calls fills in that extra parameter. In this way, the function that you're treating as a method has access to the table that you're treating as an object and everything works out nicely.
The new function is a bit more involved -- I'll rewrite it into yet another equivalent form to make it easier to read:
function Sequence.new( self )
self.__index = self
return setmetatable( { last_num = 0 }, self )
end
So for whatever table gets passed in, it assigns that table to the field __index of that same table and returns a new table with that old table set as the metatable. (Yes, this thing is confusing… don't worry, just keep reading.) To see why and how this works, here's an example:
If you say some_sequence = Sequence:new( ), you'll have the following structure:
some_sequence = { last_num = 0 } -- metatable:-> Sequence
Sequence = { new = (func...), next = (func...), __index = Sequence }
Now, when you say some_sequence:next( ), this translates to the call some_sequence.next( some_sequence ). But some_sequence doesn't have a field next! Because some_sequence has a metatable, Lua goes and looks at that – in this case, the metatable is Sequence. As a lookup (or "index") operation "failed" (it would have returned nil), Lua looks for a handler in the metatable's field __index, finds a table (Sequence again) and re-tries the lookup on that one instead (finding the next function we defined).
Which means in this case we could have equivalently written Sequence.next( some_sequence ) (but in general you don't want to – or can't – manually resolve these references). As described above, next just prints the value of the field last_num of the table it received -- in this case it got some_sequence. Again, everything works out nicely.
Some more remarks (and yet another example):
For an introductory example, the code is much more mind-bending and brittle than necessary. Here's yet another version (that's not identical and actually behaves differently, but should be easier to understand):
Sequence = { }
Sequence.__index = Sequence
function Sequence.new( )
return setmetatable( { last_num = 0 }, Sequence )
end
function Sequence:next( )
print( self.last_num )
end
Both the version that you have and this version will print 0 when you run the following:
some_sequence = Sequence:new( )
some_sequence:next( )
(I've described above what happens under the hood when you do this for your code, compare and try to figure out what happens with my version before reading on.)
This will also print 0 for both versions:
sequences = { [0] = Sequence }
for i = 1, 10 do
local current = sequences[#sequences]
sequences[#sequences+1] = current:new( )
end
local last = sequences[#sequences]
last:next( )
What happens under the hood differs significantly for both versions. This is what sequences will look like for your code:
sequences[0] = Sequence -- with __index = Sequence
sequences[1] = { last_num = 0, __index = sequences[1] } -- metatable:->Sequence
sequences[2] = { last_num = 0, __index = sequences[2] } -- metatable:->sequences[1]
sequences[3] = { last_num = 0, __index = sequences[3] } -- metatable:->sequences[2]
...
and this is what it will look like with my version:
sequences[0] = Sequence -- __index = Sequence, as set at the start
sequences[1] = { last_num = 0 } -- metatable:->Sequence
sequences[2] = { last_num = 0 } -- metatable:->Sequence
sequences[3] = { last_num = 0 } -- metatable:->Sequence
...
(If you'd instead say sequences[#sequences+1] = Sequence:new( ) in the loop above, your code would also produce this.)
With my version, the call last:next( ) fails to find next, looks at the metatable (Sequence), finds an __index field (again, Sequence) and finds next, then proceeds to call it as described above.
With your version, the call last:next( ) fails to find next, looks at the metatable (sequences[9]), finds an __index field (sequences[9]), fails to find next and therefore looks at the metatable (of sequences[9], which is sequences[8]), finds an __index field (sequences[8]), fails to find next and therefore looks at the metatable ... (until we reach sequences[1]) ... fails to find next, looks at the metatable (Sequence), finds an __index field (Sequence) and finally finds next, then proceeds with the call. (This is why I said it's quite hard to follow...)
The code that you have implements prototype-based OOP, with all the pros and cons. As you've seen, the lookup traverses the whole chain, which means that you could define a function sequences[5].next to do something else and subsequently sequences[5] through sequences[10] would find that other function. This can be really useful – no need for all the boilerplate to define a new class to change some functionality, just adjust one object and use it like a class. (This can also be annoying if you accidentally do this.)
My version implements something a bit closer to the class-based OOP seen in many other languages. (You can't accidentally override methods for more than one object at once.) What both of these (and many other approaches to OOP in Lua) have in common is that defining a field of an object with the same name as a method will hide that method and make it inaccessible. (If you define some_sequence.next, saying some_sequence:next( ) or some_sequence.next( some_sequence ) will immediately find the next you defined and Lua won't bother to look at the metatable and so on.)
Related
lua metatables - first parameter in __index function
I'm trying to learn metatables in Lua and I came across the following example: - local my_metatable = {} local my_tab = {} setmetatable(my_tab, my_metatable) -- Set the __index metamethod: my_metatable.__index = function (tab, key) print("Hello, " .. key) return "cruel world" end -- Trigger the __index metamethod: print("Goodbye, " .. my_tab["world"]) The result is:- Hello, world Goodbye, cruel world My question is - what does the variable tab do, in my_metatable.__index = function (tab, key). I can change it to anything and it doesn't affect the program in any way. Thanks! ;^) Zalokin
The tab parameter is passed an argument of the table itself. For example, given your code my_tab["world"], the parameters tab and key will be passed the arguments my_tab and "world" respectively. Because you didn't use the table in your __index function, it didn't affect anything. Here is a basic example of what it might be used for. Let us consider a special Array table that acts like an array but has some additional information: Array = { length = 0, array = {} } mt = { __index = function(tab, index) return tab.array[index] end } setmetatable(t, mt) --now when Array[3] is written, what will actually be returned is Array.array[3] print(Array[3]) --will actually print Array.array[3] This isn't actually the best way to implement this functionality, but hopefully this gives you an idea of why the tab parameter exists and what __index can be used for as a result.
Re-initialize table without losing references
I'd like to re-initialize a table without losing references to it. What I want to achieve is defining tables in files, and when a file is changed (with a text editor) the file is reloaded, changing the table. Of course this doesn't change the table but creates a new instance, old references will still point to the old table. Any suggestions? EDIT: I want to elaborate on what I want to achieve. An example with game characters and weapons. I want to modify the weapons.lua and so affect the characters. -- weapons.lua sword = { damage = 3 } -- characters.lua character = { weapon = sword } Adding a level of indirection (putting "sword" inside "weapons") like suggested by JWT doesn't help, unless I split character into { weaponTable = weapons, weaponKey = "sword" } but I don't see this as an option.
Anchor everything that needs to survive in the global environment. Nesting is fine, and this doesn't have to be your primary reference. (You can still local things, but make sure to initialize those local variables from the global environment and update the global if you change the local.) To initialize the global values, say foo = foo or value -- if foo is always true-ish bar = (bar == nil) and value or bar -- if bar may be `false` To initialize or update tables, you can foo = foo or { } foo.bar = foo.bar or 23 foo.baz = foo.baz or 42 -- and so on... but that's kinda icky, so maybe say function reinit( new, old ) -- (re)initialize one level, prefer old if old == nil then return new end if type( old ) ~= "table" then return old end for k, v in pairs( new ) do if old[k] == nil then old[k] = v end end return old end function reset( new, old ) -- (re)initialize one level, prefer new if old == nil then return new end if type( old ) ~= "table" then return new end for k, v in pairs( new ) do old[k] = v end return old end and then just foo = reinit( { bar = 23, baz = 42 }, foo ) -- only setting if not defined -- or foo = reset( { bar = 23, baz = 42 }, foo ) -- always overriding given fields or maybe make it even more fancy and say function traverse( t, path ) local here, last, lastk, created = t -- follow path of several keys starting from t, creating tables as needed for k in path:gmatch "[^.]+" do k = tonumber( k ) or k -- convert all-number keys to integer (for arrays) local next = here[k] if not next then next, created = { }, true here[k] = next else created = false end lastk, last, here = k, here, next end return here, last, lastk, created end function repopulate( path, value, update ) update = update or reinit -- pass 'reset' as 'update' for the other behavior -- or something entirely different if that's what you need local here, last, lastk, created = traverse( _G, path ) if type( value ) == "table" then update( value, here ) else if created then last[lastk] = nil end -- created one level too much update( { [lastk] = value }, last ) end end and then (with arbitrary nesting) -- No need to create 'state' first if it doesn't exist yet! -- (If it exists, it will get updated, otherwise it's created) repopulate( "state.player.weapon", { kind = "sword", damage = 11 } ) -- Do keep in mind that generally update order is relevant -- you may want to -- give a newly created player a default inventory, but you may not want to -- "refill" the player's inventory on every reload. So generally `repopulate` -- with the parent and all child nodes for from-scratch creation, then -- `repopulate` the child nodes that need to be checked & potentially updated -- as well. -- (So here you'd probably repopulate `state.player` first and then -- `state.player.weapon` or other fields only if they should be updated anyway.) -- e.g.: repopulate( "state.player", { x = 0, y = 0, hp = 100, mp = 100, level = 0, -- and so on weapon = { kind = "sword", damage = 11 }, -- etc. etc. } ) -- on reload always force a sword-kind weapon, leave rest (damage, ...) as-is repopulate( "state.player.weapon", { kind = "sword" }, reset ) -- or alternatively: only if player has no weapon, give them a sword repopulate( "state.player.weapon", { kind = "sword", damage = 3 } ) And you can go further, add metamethods to hide some of that shuffling, define different update policies, ... – you've seen some of the possibilities, now go and build your own version that fits your style and your code. (While you're free to use the above code in any way, please note that it was written ad-hoc in the browser. I did some testing, fixed some glitches, and it seems to work now, but don't be surprised if there's still one or two bugs hiding in there. So play with this, change it, break it (and see how/why it breaks), adapt and extend it, ... – but unless you completely understand what it does and can fix any bugs, I strongly suggest you write your own version, or just stick to the basics. You probably don't need everything that this does, and you're likely to need other things that this doesn't do. As this is a central part of the reloading/live-coding infrastructure and everything has to be adapted to be reload-compatible, any mismatch between your tooling and what you actually need will result in a lot of pain everywhere in your code. So if you need something like this, put in a day or two to make it work the way you need it to, or you will regret it.) (Free bonus warning: If you do OOP, you'll probably have to store and retrieve your classes instead of creating them every time, otherwise old objects from previous iterations will miss code updates and still run their old methods. I've forgotten about that more than just a couple of times and wasted several hours pondering "why isn't it fixed now?!?" after repeatedly re-loading code... So remember to anchor your metatables, anchor your classes!)
You could nest the tables in another table. Before: local a = { 1, 2, 3 } local b = { 7, 8, 9 } print(a[2] + b[2]) -- #=> 10 After: local lookup = { a = { 1, 2, 3 }, b = { 7, 8, 9 } } print(lookup.a[2] + lookup.b[2]) -- #=> 10 Then you can fully replace (or just update) a table in the lookup table and any dependent statements will use that updated value: lookup.a = { 100, 50, 0 } print(lookup.a[2] + lookup.b[2]) -- #=> 58
I don't know if it's exactly what you needed (As an ID is necessary) but I hope it will fit your needs. meta = { tables = {}, __call = function(arg, t) for k, v in pairs(t) do arg[k] = v end end, __bnot = function(arg) return arg.__key end, __newindex = function(arg, key, val) meta.tables[arg.__key][key] = val end, __index = function(arg, key) return meta.tables[arg.__key][key] end } function RefTable(arg) local newtable = {} if arg ~= nil then newtable.__key = arg setmetatable(newtable, meta) if meta.tables[arg] == nil then meta.tables[arg] = {} end else error("RefTable can't have nil key") end return newtable end -- Using the RefTable sword = RefTable("Sword") sword({damage = 3}) sword.cooldown = 10 character = {sword = sword} print("base", sword.damage, sword.cooldown) print("embed", character.sword.damage, character.sword.cooldown) sword = RefTable("Sword") sword({damage = 8, cooldown = 50}) print("embed2", character.sword.damage, character.sword.cooldown) print(sword.__key, sword.cooldown) ref = RefTable("Sword") ref.cooldown = 1000 print(sword.cooldown)
attempt to call method 'func' (a nil value)
No matter how I approach Lua, I run into this error all the time, so I must not understand something inherit to the language: attempt to call method 'func' (a nil value) I've seen the error here a few times as well but the problem doesn't seem clear to me. Here's my module: actor.lua Actor = { x = 0, mt = {}, new = function() local new_actor = {} new_actor.x = Actor.x new_actor.mt = Actor.mt return new_actor end, test = function(self, a, b) print(a, b) end } I'm using Löve. main.lua require "game/actor" local a = Actor:new() --works fine function love.load() a.x = 10 print(a.x) --output: 10 a:test(11, 12) --error: attempt to call method 'test' (a nil value) end I'm also not sure when it's appropriate to use the previous styling over this in a module. Actor = { x = 0 } Actor.mt = {} function Actor.new() print(42) end I'm honestly not sure what is more correct than the other but considering I run into a simple error either way, there's probably something I'm missing entirely?
It looks like you're trying to instance a kind of class made of metatables. You basically need to assign new_actor's metatable with Actor.mt. (Resuming the problem: when you're indexing new_actor you're not indexing Actor in this case) setmetatable(new_actor, Actor.mt); Even if the metatable is being added, it won't work until you put the meta "__index" event to index a table containing your class methods/values, in this case: Actor.mt = { __index = Actor }; I'd suggest moving your class methods/values into a new table, like Actor.prototype, Actor.fn, etc... avoiding conflicts: Actor.fn = { test = function(self, a, b) print(a, b) end }; Actor.mt = { __index = Actor.fn }; More about metatables in Lua 5.3 manual.
Metatables, attempt to call method 'rename' (a nil value)
This is my first time using metatables, I did a simple script to test in Lua demo but it always give me "attempt to call method 'rename' (a nil value)", why? peds = {} function peds.new ( name ) local tb = { name = name } setmetatable ( tb, { __index = peds } ) return tb end function peds.rename ( name ) self.name = name return self.name == name end local ped = peds.new ( "max" ) ped:rename ( "randomname" )
There's two (possible) problems in your code, depending on how you are setting things up. If you are just typing the above into a REPL, then when you declare local ped = ... it immediately goes out of scope and becomes inaccessible. So the expression ped:rename is invalid, although it should report "ped is nil" not "rename is nil". If you are saving the above to a script and loading it using load_file or something, you will still get a problem, because this function signature isn't right: function peds.rename ( name ) should be: function peds.rename ( self, name ) Similar to how it works in C++, in lua, when you make an object method, you have to take the hidden self parameter first, and when you call ped:rename( "random name" ) that's just syntactic sugar for ped.rename(ped, "random_name"). If the self parameter doesn't exist then it's not going to work, or may even say "function not found / rename is nil" because the signatures don't match up.
How does `table.insert` work with custom tables in Lua
I wonder how does table.insert work in lua?! I am asking this because I have tried to use it on a custom table with __newindex metamethod but it seems not to call it. Is there a way to make my custom table functionality to work with table.insert?! From my humble knowledge about the language I would say it uses something like rawset or something maybe I donno. Sample I worked on: do tabl = {1,2,3} local _tabl = tabl tabl = {} local mt = { __newindex = function(t,k,v) print"changing" ;_tabl[k] = v end, __index = _tabl} setmetatable(tabl,mt) end tabl[4] = 4; --prints "changing" table.insert(tabl,5) -- prints nothing!!
There's no such metamethod, table.insert just inserts a new value to a specified table. local myTable = {} table.insert(myTable, "somestring") -- so now myTable has one value, myTable = { "somestring" } It works like: local myTable = {} myTable[#myTable + 1] = "somestring" __newindex metamethod affects only assignment operator "=", table.insert is just a separate function not related with metatables, you can modify the behaviour of this function if you want: _tableinsert = table.insert function table.insert(t, v) -- here your actions, before real function will be used _tableinsert(t, v) end I think that would be possible to make your own metamethod __tableinsert this way.
table.insert does, in fact, use rawset. See the lua 5.1 source here. As indicated if you do the assignment yourself you should be able to get the behavior you want.