The index metamethod can be set equal to tables. From what I can tell
foo.__index = function(self, k)
return bar[k]
end
and
foo.__index = bar
are the same. Why is declaring functions this way allowed in this situation?
This isn't a function declaration - assigning a table to __index is just a shortcut for using the function that you described.
From Programming in Lua (for Lua 5.0, but this part of the language hasn't changed):
The use of the __index metamethod for inheritance is so common that
Lua provides a shortcut. Despite the name, the __index metamethod does
not need to be a function: It can be a table, instead. When it is a
function, Lua calls it with the table and the absent key as its
arguments. When it is a table, Lua redoes the access in that table.
It's not like the table that you assign magically becomes a function. type(foo.__index) will still return table, and you can still do things with it that you can do with other tables, like using pairs and next, etc.
Related
Lua doesn't offer a unique way to OOP.
With setmetatable many alternatives are possible.
Here's what I tried:
Person={}
function Person.__call(cls,name)
return setmetatable({name=name},cls)
end
function Person:say(what)
print(self.name..'> '..what)
end
setmetatable(Person,Person)
p=Person('Fred')
p:say('hello') -- 18
which gives the error:
18: attempt to call a nil value (method 'say')
I can add:
function Person.__index(cls,k)
return Person[k]
end
and then the above code works correctly, however I do not understand why the method is not found when Person is already metatable of itself.
You need to implement the __index metavalue in order to relay indexing access operations. Having a metatable alone is not sufficient.
Also note that it is recommended to implement all meta methods befor using a table as a metatable.
Refer to the Lua 5.4 Reference Manual 2.4 Metatables and Metamethods
__index: The indexing access operation table[key]. This event happens when table is not a table or when key is not present in table. The
metavalue is looked up in the metatable of table.
The metavalue for this event can be either a function, a table, or any
value with an __index metavalue. If it is a function, it is called
with table and key as arguments, and the result of the call (adjusted
to one value) is the result of the operation. Otherwise, the final
result is the result of indexing this metavalue with key. This
indexing is regular, not raw, and therefore can trigger another
__index metavalue.
__index is that metavalue. So if you don't provide that metavalue, what should Lua do?
In the following example that metavalue is Person.
So when I call a:sayName(), Lua will find that a.sayName is nil. It will check if in a's metatable Person there is a __index metavalue. There is and in this case it's a table named Person so it will index that person with key "sayName" which results in the following function call: Person["sayName"](a)
local Person= {}
Person.__index = Person
setmetatable(Person, {
__call = function (cls, ...)
return cls:_init(...)
end,
})
function Person:_init(name)
local o= setmetatable({}, self)
o.name = name
return o
end
function Person:sayName()
print(self.name)
end
local a = Person("Lisa")
a:sayName()
The Lua 5.3 Reference Manual (in this part, scroll down) says:
__newindex: The indexing assignment table[key] = value. Like the index event, this event happens when table is not a table or when key is not present in table. The metamethod is looked up in table.
However I don't understand that the metamethod __newindex happens when table is not a table. What does that mean? I did try to re-assign a local with nil, but it didn't work (yes, I know it doesn't make sense to re-assign the table, but this would help it to be garbage-collected).
local v = {};
setmetatable(v, {
__newindex = function(t,k,v)
print("Aaahhh...!");
end
});
v = nil;
I'm using this online compiler to test it.
From the page you cited:
You can replace the metatable of tables using the setmetatable
function. You cannot change the metatable of other types from Lua code
(except by using the debug library (ยง6.10)); you should use the C API
for that.
You can't use setmetatable to change the metatable for something that's not a table, so you won't be able to verify what you expect (that the __newindex method is called when you index into something that's not a table).
Your code runs because you are setting the metatable for a table. (local v = {} creates a table.)
But reassigning the variable v to be something else means you no longer have a way to get to the table you created. If your last line were v[5] = 'Hello', then you would see that your metamethod is invoked.
EDIT
Reading your edit, it looks like you expected __newindex to get called when the table was garbage collected? I think you misunderstood the statement that __newindex is called "when table is not a table." That means if you did something like this:
local v = 5
print(v[3]) -- indexing into something that's not a table
__newindex would be called (because in the expression table[key], which here is v[3], table is not a table). But you can't actually set up __newindex via the setmetatable method, because that method only works on tables.
I am confused of the following two syntaxes using "."
From what I understand, __index is called when a key doesn't exist in a table but exists in its metatable. So why does the list table call __index and then assign itself to list.__index?
list = {}
list.__index = list
setmetatable(list, { __call = function(_, ...)
local t = setmetatable({length = 0}, list)
for _, v in ipairs{...} do t:push(v) end
return t
end })
function list:push(t)
if self.last then
self.last._next = t
t._prev = self.last
self.last = t
else
self.first = t
self.last = t
end
self.length = self.length + 1
end
.
.
.
local l = list({ 2 }, {3}, {4}, { 5 })
Does Window.mt simply create a table? Why do we need Window = {} as a namespace here?
Window = {} -- create a namespace
Window.mt = {} -- create a metatable
Window.prototype = {x=0, y=0, width=100, height=100, }
function Window.new (o)
setmetatable(o, Window.mt)
return o
end
Window.mt.__index = function (table, key)
return Window.prototype[key]
end
w = Window.new{x=10, y=20}
print(w.width) --> 100
So why does the list table call __index and then assign itself to list.__index?
Nowhere in your code does the list table call __index. The assignment part however is a common Lua idiom (aka. hack) to save some memory. Conceptually there are 4 different kinds of tables involved:
list objects (the tables created via {length=0} in your code)
a metatable (containing an __index field) that modifies the behavior of list objects when you try to access non-existing fields in the object
the list class, which holds all the methods for list objects (like the push method), and also serves as a constructor for list objects
a metatable (containing a __call field) for the list class, so that you can call the list table as if it were a function
As metatable fields always start with two underscores (__), and normal methods usually don't, you can put metatable fields and normal methods side by side into a single table without conflict. And this is what happened here. The list class table also serves as metatable for list objects. So using this trick you can save the memory you would normally need for the separate metatable (the size in bytes for Lua 5.2 on an x86-64 Linux is shown in square brackets in the table title bars, btw.):
Does Window.mt simply create a table?
No, {} creates a table. However, this new table is saved under key "mt" in the Window table, probably to give users of this Window "class" direct access to the metatable that is used for window objects. Given only the code you showed this is not strictly necessary, and you could have used a local variable instead.
Why do we need Window = {} as a namespace here?
In principle, you could store Window.mt, Window.new, and Window.prototype separately, but that would get cumbersome if you have multiple "classes" like Window. This way you can avoid name clashes, and using the Window "class" looks nicer.
Another reason might be that require can only return a single value from a module definition, and if you want to export multiple values (like new, mt, and prototype) from a module, you need a table to wrap them together (or use global variables, but that is considered bad style).
Rawset function in lua generally is passed table, index and value but I came across this code:
rawset(tbl,name,{})
and
rawset(tbl,name, function() end)
Rawset function returns a table, so what does it mean to have a table or function in rawset function in place for value ?
Lua tables can hold values of all types, including tables and functions, and they can be heterogeneous: not all values need to be of the same type.
See http://www.lua.org/manual/5.2/manual.html#2.1.
From reference manual:
rawset (table, index, value): Sets the real value of table[index] to value, without invoking any metamethod. table must be a table, index any value different from nil, and value any Lua value.
What this means:
table's metatable is not used: that's why it is "raw" set, the field is added directly; without raw, the table's metatable will be used to handle the "set" action;
index any value different from nil: in Lua, this really means any type of Lua object other than nil: a number, a function, another table, etc (Lua ref manual lists all types);
value any Lua value: same as previous, but can even be nil: if set to nil, effectively removes item from table.
So index being name just indicates the table is an associative array (unless name is a number but that would be misleading), in first case the associated value is another table, in the second case it is a Lua function.
__index = function(tbl, key)
local a = tbl[key]
if a <=0 then a = 0 end
if a > 5 then a = 0 end
return a
end
Book says:
Though the preceding code looks very innocent and tries to keep the value of the element in the table within a range, this code will cause problems and circular references. The first line in the function, a = tbl[key], will actually trigger another index function call, and that in turn will invoke another, and so on.
But how is a = tbl[key] calling another index at every call ?
That is a bit wierd thing to do. Lua triggers __index metamethod only if it can't find a field in the table. Therefore, using tbl[key] inside it makes absolutely no sense at all. Unless tbl is not a table.
Anyhow, if you want to access a table's field from within __index, use rawget. That will ensure that no metamethods are called.
EDIT:
Let me explain how __index lookup works:
Let's assume the table has a metatable with __index defined.
If Lua can't find the key in the table, it looks for the __index field in the metatable. It does not look for the key itself. Then, if the __index is a table, it looks for the key in that table (not the metatable, although it is common to associate the metatable itself with its __index field). If it is a function, it is invoked with two parameters: table (initial table, not the metatable) and the key.
So if the __index metamethod is called, you can be certain that the initial table does not have that field defined. Therefore, when you try to index it again (as the first argument is the original table that triggered the index lookup), the story begins anew -> Lua can't find the key, it invokes the __index and so on.