Working through the Lua exercises in 7 More Languages in 7 Weeks and getting caught on a metatables problem.
The challenge is to overload the + operator in order to be able to concatenate tables (as though they were arrays).
{ 1, 2, 3 } + { 4, 5, 6 } -- { 1, 2, 3, 4, 5, 6 }
So I tried to use the __add metamethod and created a metatable to host it.
local mt = {
__add = function(lhs, rhs)
return concatenate(lhs, rhs)
done
}
I mistakenly tried to set this metatable on the global table, but this obviously doesn't propagate down to all other tables.
In an attempt to add it to all created tables, I created a separate metatable on _G which would use the __newindex metamethod, then set the original metatable every __newindex was activated.
setmetatable(_G, {
__newindex = function(array, index)
setmetatable(array, mt)
end
})
However, this didn't work:
a1 = { 1, 2, 3 }
a2 = { 4, 5, 6 }
a1 + a2
And resulted in the following error: attempt to perform arithmetic on global 'a1' (a nil value)
So I threw a print statement into the global metatable to see whether it was actually being called:
setmetatable(_G, {
__newindex = function(array, index)
print('new table ' .. index)
setmetatable(array, mt)
end
})
This only ever prints the first table to be created:
a1 = { 1, 2, 3 }
a2 = { 4, 5, 6 }
a3 = { 4, 5, 6 }
a4 = { 4, 5, 6 }
Results in:
new table a1
I expect I am accidentally overriding something, because when I remove the call to setmetatable
setmetatable(_G, {
__newindex = function(array, index)
print('new table ' .. index)
--setmetatable(array, mt)
end
})
It prints all entries as expected.
setmetatable(_G, {
__newindex = function(array, index)
print('new table ' .. index)
setmetatable(array, mt)
end
})
When you do a1 = { 1, 2, 3 } then __newindex is going to be called with with array being _G and index being 'a1'. Then calling setmetatable(array, mt) is going to change the metatable of _G, undoing the effect of the original setmetatable call.
You probably want something more like:
setmetatable(_G, {
__newindex = function(array, index)
setmetatable(array[index], mt)
end
})
But there's another problem because now the original effect of the assignment no longer takes place. __newindex is called instead, so a1 remains a nil value.
You could try array[index] = value within the __newindex function but this will call the same __newindex again. Instead, use rawset:
setmetatable(_G, {
__newindex = function(array, index, value)
rawset(array, index, value)
setmetatable(array[index], mt)
end
})
Note that __newindex takes 3 parameters.
Now, what happens when you assign a non-table to a global like b = 1?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With