Metamethods being inherited but not called

I am trying to make a role system using inheritance, so all roles inherit from RoleBase. RoleBase has an __eq metamethod to compare if 2 role objects are the same role. I have a demonstration of what I am trying to do:

-- role base
local RoleBase = {}
RoleBase.__eq = function(a, b)
    print("Called Metamethod with ", a, b)
    return a.id == b.id
end

RoleBase.__index = RoleBase


function RoleBase.new()
    
    local self  = setmetatable({}, RoleBase)

    return self
    
end

-- end role base
-- dead
local Dead = {}
setmetatable(Dead, RoleBase)
Dead.__index = Dead
Dead.id = 1

function Dead.new()

    local self = setmetatable(RoleBase.new(), Dead)

    return self

end

-- end dead
-- testing

local a = Dead.new()

print(RoleBase.__eq)
print(Dead.__eq)
print(a.__eq)
print(a == Dead)

The prints at the end confirm that RoleBase, Dead, and a share the same __eq metamethod but it is not called when the code is ran.

function: 0x0000000000034340
function: 0x0000000000034340
function: 0x0000000000034340
false

What is the cause of this? I would like to note that __eq is called in online lua compilers, but not online luau compilers.

2 Likes

uhh, was this intentionally deleted? seems strange

Why is RoleBase a class if it is never created? Shouldn’t it assign the metamethods in the constructor function?

RoleBase is created in the constructor for Dead
local self = setmetatable(RoleBase.new(), Dead)
in the actual code, the RoleBase constructor actually does stuff

What is the meta table of each object? That is what matters when trying to compare with ==.

E.g. getmetatable(a).__eq and getmetatable(RoleBase).__eq

I remember reading somewhere about meta methods not being inherited like other keys but can’t find the source for that atm.

That is not enough, what if they are both nil?


Do this:

print(getmetatable(a).__eq)

image

1 Like

could someone make a post about this in #bug-reports? im not allowed to post there

Is that what happens in studio too? I do think it’s a case of the meta method having to be directly in the meta table still.

E.g. if you added Dead.__eq = RoleBase.__eq I think it would start working.

Sort of defeats the purpose of trying to inherit from the base class though which isn’t ideal.

1 Like

The __eq metamethod has this requirement, which is annotated in the metatables documentation.


Since you are assigning different metatables for each inherited class, they will not work with the __eq metamethod. I would just use a regular .equals() method instead.

if i do Dead.__eq = RoleBase.__eq, it fixes it, even though Dead.__eq and RoleBase.__eq are already equal and the metatables are not.


im almost positive this is a luau bug at this point

yeah it does fix it but youre right, it defeats the purpose.

It really sucks that you cannot use the colon operator for something like this.

I would recommend making it an inheritable function from RoleBase. Might not be ideal, but it would work.

i had a :Equals method in rolebase because of this but it really bothers me that this equality check stands out like a sore thumb when everything else uses a == operator. itll be fine ig lol

It is not a compiler bug. As @Quantum_Maniac mentioned both tables should have the same metatable, but in your case they do not.

Simply this style of implementing OOP will not allow you to do that. What you can do is to use another style. For example:

-- role base
local RoleBase = {}
local meta = {}
meta.__eq = function(a, b)
    print("Called Metamethod with ", a, b)
    return a.id == b.id
end

function RoleBase.new()
    local self  = {}
    return self
end
-- end role base

-- dead
local Dead = {}
setmetatable(Dead, meta)
local deadId = 1
Dead.id = deadId

function Dead.new()
    local self = setmetatable(RoleBase.new(), meta)
    self.id = deadId
    return self
end
-- end dead

-- testing
local a = Dead.new()
print(a == Dead)

why does this work?

Ok. apparently it is a reminiscence that luau has, inherited from lua5.1. In lua5.1 it is only required that the metamethods are the same. in its documentation it is explained in detail. But this is not consistent with the luau documentation mentioned above.
That’s why it still works.