using inheritance to compare (__lt) two different types

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
Post Reply
LastNameGames
Prole
Posts: 7
Joined: Tue Mar 10, 2015 8:04 pm

using inheritance to compare (__lt) two different types

Post 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
User avatar
Robin
The Omniscient
Posts: 6506
Joined: Fri Feb 20, 2009 4:29 pm
Location: The Netherlands
Contact:

Re: using inheritance to compare (__lt) two different types

Post 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:

Code: Select all

setmetatable(o, depth)
Bonus: __lt can also be written as:

Code: Select all

function depth.__lt(left, right)
  return left.depth < right.depth
end
Help us help you: attach a .love.
LastNameGames
Prole
Posts: 7
Joined: Tue Mar 10, 2015 8:04 pm

Re: using inheritance to compare (__lt) two different types

Post 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?
User avatar
s-ol
Party member
Posts: 1079
Joined: Mon Sep 15, 2014 7:41 pm
Location: Milan, Italy
Contact:

Re: using inheritance to compare (__lt) two different types

Post 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.

s-ol.nu

Code: Select all

print( type(love) )
if false then
  baby:hurt(me)
end
LastNameGames
Prole
Posts: 7
Joined: Tue Mar 10, 2015 8:04 pm

Re: using inheritance to compare (__lt) two different types

Post 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!
User avatar
s-ol
Party member
Posts: 1079
Joined: Mon Sep 15, 2014 7:41 pm
Location: Milan, Italy
Contact:

Re: using inheritance to compare (__lt) two different types

Post 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.
:crazy:

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.

s-ol.nu

Code: Select all

print( type(love) )
if false then
  baby:hurt(me)
end
LastNameGames
Prole
Posts: 7
Joined: Tue Mar 10, 2015 8:04 pm

Re: using inheritance to compare (__lt) two different types

Post 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?
User avatar
s-ol
Party member
Posts: 1079
Joined: Mon Sep 15, 2014 7:41 pm
Location: Milan, Italy
Contact:

Re: using inheritance to compare (__lt) two different types

Post 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 :P 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.

s-ol.nu

Code: Select all

print( type(love) )
if false then
  baby:hurt(me)
end
Post Reply

Who is online

Users browsing this forum: Amazon [Bot], bankey, Bing [Bot], Google [Bot] and 4 guests