Page 1 of 1
using inheritance to compare (__lt) two different types
Posted: Tue Mar 17, 2015 4:09 pm
by LastNameGames
I've been exploring the skip list data structure to aid me in setting up a drawing order for objects, but I've realized that it may be difficult if I want to make a skip list containing multiple types of objects (like a player type and an enemy type), because Lua can't use relational operators on objects of different types. I thought it might be possible to have all of these objects inherit from a big parent object called "depth," and tried writing the following code:
Code: Select all
depth = {depth = 0}
depth.__lt = function (left, right)
if left.depth < right.depth then
return true
else
return false
end
end
character1 = {}
function character1:new()
local o = {}
setmetatable(o,{__index = depth})
o.dummy1 = 10
return o
end
character2 = {}
function character2:new()
local o = {}
setmetatable(o,{__index = depth})
o.dummy2 = 'hello'
return o
end
c1 = character1:new()
c2 = character2:new()
print(c1 < c2)
When I run it, however, I get an error message:
/Applications/ZeroBraneStudio.app/Contents/ZeroBraneStudio/bin/lua.app/Contents/MacOS/lua53: /Users/Nick/Desktop/main.lua:29: attempt to compare two table values
stack traceback:
/Users/Nick/Desktop/main.lua:29: in main chunk
[C]: in ?
Can anybody give a breakdown of what this error message means, and what steps I might take to overcome it? I'm still very new to Lua, so please don't be afraid to be patronizing.
Thanks in advance,
Nicholas
Re: using inheritance to compare (__lt) two different types
Posted: Tue Mar 17, 2015 4:43 pm
by Robin
The error message means that the table's metatable doesn't define __lt. And it doesn't, it only defines __index. So you want the metatable to be
depth.
The solution is:
Bonus: __lt can also be written as:
Code: Select all
function depth.__lt(left, right)
return left.depth < right.depth
end
Re: using inheritance to compare (__lt) two different types
Posted: Tue Mar 17, 2015 5:56 pm
by LastNameGames
ah, okay. That worked, but now depth isn't being set by default. I have to set the depth values for both objects manually before I can call the function.
Shouldn't calling setmetatable(o,depth) set it so that an object of type o will look for undefined values in depth?
Re: using inheritance to compare (__lt) two different types
Posted: Tue Mar 17, 2015 6:35 pm
by s-ol
because Lua can't use relational operators on objects of different types
there are no types or objects in lua. Comparing two
tables compares there identity, so unless they are exactly the same, how could lua know whether they should be equal?
LastNameGames wrote:Shouldn't calling setmetatable(o,depth) set it so that an object of type o will look for undefined values in depth?
The
metatable isn't where lua looks for missing stuff, it's where lua looks for
metainformation. The
__index key
in the metatable is the table that will be searched for missing keys. We often set the "__index" key of the metatable to be itself so we don't use two tables for one "class":
Code: Select all
local Player = { draw = function () ... end }
Player.__index = Player
local newplayer = setmetatable({x=2, y=3}, Player)
-- same functionality as:
local PlayerDefinition = { draw= function () ... end }
local PlayerMT = { __index = PlayerDefinition }
local newplayer = setmetatable({x=2, y=3}, PlayerMT)
There are many more "special" metatble keys that can alter a table's behaviour, like for example "__lt".
I think this is what you want to do:
Code: Select all
local Depth = {} -- uppercase to indicate this is something you can think of as a "type" - not needed
Depth.__index = Depth
function Depth:__lt( left, right )
return left.depth < right.depth
end
local Player1 = setmetatable({}, Depth) -- because of this dependenceis will "decend" into Depth
Player1.__index = Player1 -- look in Player1 for methods
function Player1:new(x, y)
local o = setmetatable({}, Player1)
end
--analogous for Player2
In your code example the definition of character1/2 is unnecessary, they do not (and can not) serve any relevant purpose.
I am not sure why you would really need a
__lt in the first place though, you can just replace "a < b" with "a.depth < b.depth" in the skiplist code.
Re: using inheritance to compare (__lt) two different types
Posted: Tue Mar 17, 2015 7:07 pm
by LastNameGames
aaaaah, okay. I think I understand it, now.
I kind of thought that a table's __index metamethod defaulted to itself, so I felt it was unnecessary to set.
I probably could have just changed the skip list code like you said, but I hadn't read through it fully, and I also felt it would be worthwhile to get a stronger understanding of OOP in Lua, anyways.
Thanks!
Re: using inheritance to compare (__lt) two different types
Posted: Tue Mar 17, 2015 8:04 pm
by s-ol
LastNameGames wrote:
I probably could have just changed the skip list code like you said, but I hadn't read through it fully, and I also felt it would be worthwhile to get a stronger understanding of OOP tables in Lua, anyways.
unless you mean understanding of how OOP is mimiced in Lua. Either way: understanding those things is probably the most important thing to learn to get up to speed with LÖVE.
Once again I would warn you though, "picture-book" (can you say this in english?) OOP in lua is
mostly a waste of time. Many of the same concepts
behind OOP and design patterns apply (abstraction obviously) but doing everything the OOP way os going to cost you more time and waste more keystrokes than it is going to help you get an overview. For example in lua there is absolutely no reason to build a common Entity "type" for players and enemies, just have both have a :draw() and :update(dt) function, be proud of remembering the fact that they both share these and move on.
Re: using inheritance to compare (__lt) two different types
Posted: Wed Mar 18, 2015 1:11 am
by LastNameGames
I'm not 100% sure I follow what you're saying. Are you suggesting that it would be easiest to just have a create_enemy() function that returns a table containing the fields and functions that an enemy has, instead of having a prototype table that enemies set their metatable to?
If so, then when (if at all) does this business of metatables and OOP mimicry come in handy?
Re: using inheritance to compare (__lt) two different types
Posted: Wed Mar 18, 2015 8:27 am
by s-ol
LastNameGames wrote:I'm not 100% sure I follow what you're saying. Are you suggesting that it would be easiest to just have a create_enemy() function that returns a table containing the fields and functions that an enemy has, instead of having a prototype table that enemies set their metatable to?
If so, then when (if at all) does this business of metatables and OOP mimicry come in handy?
No, metatablea are a very useful tool in Lua and should be used whenever it makes sense (for __index that mostly is having a lot of objects that share some properties). In your code so far everything is okay.
I am trying to warn you not to mess about too much with abstract programming concepts in lua (for the future). For example "abstract" classes are completely useless. A lot of the OOP concepts are just waste of writing and code maintenance time, sometimes code that's a little "dirtier" is the better choice.
All of this depends on the size of the project, your project structure etc. - don't think about what I am saying too much either

instead keep it in the back of your head, and the next time you find yourself wondering how to do some of the OOP stuff in Lua, ask yourself whether it's even needed.
I'm saying all this because judging from your OP, I'd guess that you tend to overcomplicate/overengineer things (like I myself do a lot of the time). It's a common problem and when you come from a really OOP-world (CS education and Java for example) it's bound to occur.