Search code examples
lua

How to write an if statement in ternary form?


The commented code with the if statement does work correctly. However, the ternary does not? Why is this or how can I implement this in one simple line?


local function dontwork()
  local args = ""
  local cmd_tb = {}
  cmd_tb.args = ""

  -- dont work
  args = (#cmd_tb.args == 0 and false or cmd_tb.args)
  -- dont work
  -- args = (cmd_tb.args == "" and false or cmd_tb.args)

-- works correctly - empty
  -- if #cmd_tb.args == 0 then
  --   args = false
  -- else
  --   args = cmd_tb.args
  -- end

  if args then
    print("yes:",args)
  else
    print("empty")
  end
end

-- outputs 'yes'
dontwork()



Solution

  • This is a common pitfall.

    cond and a or b is not a ternary. It's just (cond and a) or b, which, due to lazy evaluation, behaves almost like a ternary:

    • If cond is truthy, cond and a evaluates to a. If a is truthy, a or b then evaluates to a.
    • If cond is falsey, cond and a evaluates to cond, so cond or b evaluates to b.

    The problem is that if a is falsey (nil or false), then a or b evaluates to b, when it should evaluate to a.

    In your case, since b is truthy (nil and false don't support #, unless you mess with debug metatables), you could just negate your condition and swap a and b:

    args = #cmd_tb.args ~= 0 and cmd_tb.args or false
    

    The or false is not necessary in this case, since #cmd_tb.args ~= 0 will already evaluate to false. It may be necessary if your condition could be nil; or nil is also sometimes useful when you want nil rather than a boolean, but your condition produces a boolean.

    PS: I would test for the empty string using ~= "" / == "" respectively. It is clearer than comparing #x against 0.