Git Product home page Git Product logo

Comments (12)

catwell avatar catwell commented on August 23, 2024

As written in the source, I took the logic from Penlight.

Comparison of tables with different metatables in Lua is complicated. For instance:

local mt = function(x) return { __eq = function() return x end } end
local foo, bar = {4}, {4}

print(foo == bar, bar == foo) -- false, false

setmetatable(foo, mt(true))
print(foo == bar, bar == foo) -- true, true

setmetatable(bar, mt(false))
print(foo == bar, bar == foo) -- true, false

As you can see, when both members have an __eq metamethod, comparison is no longer symmetric. Mix that with deep comparison and it can quickly become a nightmare.

I think what Penlight is doing for deep comparison (only take the first parameter into account to check if a metatable should be used, i.e. "cast the second parameter to the type of the first one") is a good trade-off.

from luassert.

telemachus avatar telemachus commented on August 23, 2024

@catwell I agree that comparison once metatables are involved gets very complicated. However, that last result really bothers me. That said, I'm not sure of the right alternative. Here's one thought. Change my last attempt above into this:

local mt1 = getmetatable(t1)
local mt2 = getmetatable(t2)
if (mt1 and mt1.__eq) or (mt2 and mt2.__eq) then
  return t1 == t2 and t2 == t1
end

I'm aksing for my parallel method in tapered. At the moment, I'm trying something much more complicated than I like, but I'm trying to catch all possible cases.

from luassert.

catwell avatar catwell commented on August 23, 2024

@telemachus Yes, that makes sense. It can be inefficient, but does it really matter in test helpers?

from luassert.

telemachus avatar telemachus commented on August 23, 2024

@catwell To me that (tiny?) inefficiency is less important than a wrong result. But I can imagine cases where people would vote the other way.

from luassert.

catwell avatar catwell commented on August 23, 2024

Inefficiency is not always that tiny. Think about what happens if you are comparing trees and __eq itself is implemented with deep comparison :)

from luassert.

catwell avatar catwell commented on August 23, 2024

Regarding the luassert code: the real bug IMO is that the code does not do what the comment says. If mt1 exists and has an __eq methamethod but mt1 is not the same as mt2, the two members are compared using mt1.__eq. It can be fixed by calling rawequal() instead of ==.

from luassert.

o-lim avatar o-lim commented on August 23, 2024

I agree with @catwell's last comment, util.deepcompare should be using rawequal() instead of ==.

from luassert.

telemachus avatar telemachus commented on August 23, 2024

@catwell @o-lim I think that there is an important difference here between Lua 5.3 and earlier versions.

In Lua 5.2, a call to == will only use .__eq only if both mt1 and mt2 share the same .__eq function. (Reference: http://www.lua.org/manual/5.2/manual.html#2.4.)

In Lua 5.3, a call to == will only use .__eq if either mt1 or mt2 have a .__eq function. (And apparently without regard to whether these are the same methods. Calls to x == y become order dependent. See @catwell's code in [his earlier comment](a day ago).) (Reference: http://www.lua.org/manual/5.3/manual.html#2.4.)

$ ./lua-5.3.1/lua-5.3.1 catwell.lua 
false   false
true    true
true    false
$ lua-5.2 catwell.lua 
false   false
false   false
false   false
false

So perhaps luassert is relying on t1 == t2 to do the right thing (and skip .__eq) because that's the default behavior on Lua before 5.3?

from luassert.

catwell avatar catwell commented on August 23, 2024

Right. To be honest I had to check with an interpreter, and I had not realized this had changed in 5.3. I almost never overload operators in my own code.

from luassert.

telemachus avatar telemachus commented on August 23, 2024

A reason given on the lua mailing list for not required equality of metatables is that it rules out (some kinds of) inheritance. I'm not sure how relevant that is to anyone's choices in this thread, but it makes sense to me as one concern that some people may have.

Reference: http://listmaster.pepperfish.net/cgi-bin/mailman/private/lua-l-lists.lua.org/2015-July/049132.html

from luassert.

o-lim avatar o-lim commented on August 23, 2024

In either case, I think be using rawequal instead of == is the right thing to do here. However, in the case of using .__eq, maybe the right thing to do is to check that mt1 and mt2 share the same .__eq function, instead of checking mt1 == mt2, before using .__eq to evaluate equality? And maybe assert.is_equal should also do the same to maintain consistency between different versions of Lua?

from luassert.

telemachus avatar telemachus commented on August 23, 2024

In either case, I think be using rawequal instead of == is the right thing to do here.

I agree with you about the change you've proposed as a PR.

However, in the case of using .__eq, maybe the right thing to do is to check that mt1 and mt2 share the same .__eq function, instead of checking mt1 == mt2, before using .__eq to evaluate equality? And maybe assert.is_equal should also do the same to maintain consistency between different versions of Lua?

I had written a version of tapered (my little testing library) that did this, but now I'm thinking of going back to the way that penlight, underscore and cwtest handle things. In other words:

if mt and mt.__eq then return t1 == t2 end

On the lua mailing list, Roberto's attitude to my worries about noncommutative __eq methods was basically, "If someone writes such methods, they get what they deserve." As a language designer, I think he wanted to change away from testing that both __eq methods are identical (in order to make a certain form of inheritance possible and avoid a certain kind of indeterminate behavior). If that's the case, I'm thinking I may just follow the language's lead.

from luassert.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.