This first edition was written for Lua 5.0. While still largely relevant for later versions, there are some differences.
The fourth edition targets Lua 5.3 and is available at Amazon and other bookstores.
By buying the book, you also help to support the Lua project.


13.2 – Relational Metamethods

Metatables also allow us to give meaning to the relational operators, through the metamethods __eq (equality), __lt (less than), and __le (less or equal). There are no separate metamethods for the other three relational operators, as Lua translates a ~= b to not (a == b), a > b to b < a, and a >= b to b <= a.

(Big parentheses: Until Lua 4.0, all order operators were translated to a single one, by translating a <= b to not (b < a). However, this translation is incorrect when we have a partial order, that is, when not all elements in our type are properly ordered. For instance, floating-point numbers are not totally ordered in most machines, because of the value Not a Number (NaN). According to the IEEE 754 standard, currently adopted by virtually every hardware, NaN represents undefined values, such as the result of 0/0. The standard specifies that any comparison that involves NaN should result in false. That means that NaN <= x is always false, but x < NaN is also false. That implies that the translation from a <= b to not (b < a) is not valid in this case.)

In our example with sets, we have a similar problem. An obvious (and useful) meaning for <= in sets is set containment: a <= b means that a is a subset of b. With that meaning, again it is possible that both a <= b and b < a are false; therefore, we need separate implementations for __le (less or equal) and __lt (less than):

    Set.mt.__le = function (a,b)    -- set containment
      for k in pairs(a) do
        if not b[k] then return false end
      end
      return true
    end
    
    Set.mt.__lt = function (a,b)
      return a <= b and not (b <= a)
    end
Finally, we can define set equality through set containment:
    Set.mt.__eq = function (a,b)
      return a <= b and b <= a
    end
After those definitions, we are now ready to compare sets:
    s1 = Set.new{2, 4}
    s2 = Set.new{4, 10, 2}
    print(s1 <= s2)       --> true
    print(s1 < s2)        --> true
    print(s1 >= s1)       --> true
    print(s1 > s1)        --> false
    print(s1 == s2 * s1)  --> true

Unlike arithmetic metamethods, relational metamethods do not support mixed types. Their behavior for mixed types mimics the common behavior of these operators in Lua. If you try to compare a string with a number for order, Lua raises an error. Similarly, if you try to compare two objects with different metamethods for order, Lua raises an error.

An equality comparison never raises an error, but if two objects have different metamethods, the equality operation results in false, without even calling any metamethod. Again, this behavior mimics the common behavior of Lua, which always classifies strings as different from numbers, regardless of their values. Lua calls the equality metamethod only when the two objects being compared share this metamethod.