Search code examples
if-statementvariableslualogical-operatorsevaluation

Defining logical operator implies in lua


print("i", "j", "i & j")
for i = 0,1 do
   for j=0,1 do
   print(i, j, i & j)
   end
end

The above code works fine in Lua. It gives the following output.

i   j   i & j
0   0   0
0   1   0
1   0   0
1   1   1

I want to define another operator implies. It is conditional logical operator. The result p implies q is false only when p is true and q is false.

print("i", "j", "i implies j")
for i = 0,1 do
   for j=0,1 do
   print(i, j, i implies j)
   end
end

I want the above code to work. It should output the following.

i   j   i implies j
    0   0   1
    0   1   1
    1   0   0
    1   1   1

So far I have succeeded in defining implies function but that doesn't seem to be of great use. Of course I can write implies(i,j) but I want operator similar to & or | that I can use. So basic question is to replicate definition of logical operator & or | from lua. Here is the code for implies function (not operator).

function implies(a,b)
   if a then
        return b
    else
        return True
  end
end

Solution

  • OOP-style calls

    This gets rather hacky if it must truly be infix. What about simply using metatables for OOP-ish object:method(params) syntax?

    Assuming implies is defined as

    local function _implies(a, b)
        if a ~= 0 then return b end
        return 1
    end
    

    this would be implemented as

    debug.setmetatable(0, {__index = {
        implies = _implies
    }})
    

    and used as

    i:implies(j)
    

    if you want you a simpler dot syntax instead, consider using currying:

    debug.setmetatable(0, {__index = {
        implies = function(a) return function(b) return _implies(a, b) end end
    }})
    

    used as

    i.implies(j)
    

    A more "infix-ish" notation might be achieved by using the __call metamethod, and passing a function name:

    debug.setmetatable(0, {__index = {
        implies = function(a) return function(b) return _implies(a, b) end end
    }, __call = function(self, index) return self[index] end})
    
    (i)("implies")(j)
    

    If implies = "implies", this can even be shortened to (i)(implies)(j).

    Hacky, "truly" infix notation

    If infix notation is of utmost importance, yet you can't use the available metamethods as they are limited and rather unreadable, you can use even more currying and workarounds to use other metamethods to create operators that seemingly turn your function (at least syntactically) into an infix operator using a "proxy" metatable instead of the function. You can use whatever operators you like; just make sure they suit your needs in terms of associativity and precedency:

    local implies_right_hand_mt = {
        __mul = function(self, b)
            return _implies(self.a, b)
        end
    }
    local implies_left_hand = setmetatable({}, {
        __mul = function(a)
            return setmetatable({a = a}, implies_right_hand_mt)
        end
    })
    implies = implies_left_hand
    

    The syntax would then be i *implies* j or "i *implies* j" in your example (using the multiplication operator). You could however use all other operators (except the comparison operators) as well, including other arithmetic operators, as implies is not a number but rather a table and may thus override number metamethods.

    Side note

    Consider using booleans instead of numbers. You'd get two advantages:

    1. Most metamethods aren't used by booleans (especially all arithmetic & bitwise metamethods);
    2. The logic operators not, and and or work; you don't have to use bitwise operators (although those work fine as well).