How is Julia parsing `<condition> ? return <value1> : return <value2>`?

What is wrong with the third syntax? Is #1 or #2 better/preferred?

# 1
julia> function isone(a)
              return a==1 ? true : false
       end
isone (generic function with 1 method)

# 2
julia> function isone(a)
              a==1 ? (return true) : (return false)
       end
isone (generic function with 1 method)

# 3
julia> function isone(a)
              a==1 ? return true : return false
       end
ERROR: syntax: colon expected in "?" expression

why not

isone(a) = a==1

Syntax #1 is preferred (assuming this is a simplified example — otherwise indeed @uniment’s is best!). Syntax #3 is parsed as

a==1 ? return (true : return false)

i.e., with : as a range, which is then a syntax error because the ternary operator ? doesn’t have its other half (:).

1 Like

@uniment , this was a simplified example just to understand how Julia is interpreting the syntax. @Jollywatt answered my questions. Thank you.

I keep falling into these traps. Maybe this sort of (? :) syntactic sugar is advertised too heavily? Less experienced users like myself can easily end up writing wrong code. And it would be hard to debug. It makes me want to stick to the more verbose syntax. This works just as I expect:

function isone(a)
       if a==1 return true else return false end
end
function isone(a)
    a == 1 && return true
    return false
end

Yes, I also tend to use if in one-liners, i.e., why do we need ? : when if is an expression already? Indeed, many functional languages don’t have that operator.

On the other hand if has also some pitfalls:

julia> a = 1
1

julia> if a == 1 :a else :b end
:b

julia> if a == 1; :a else :b end
:a

julia> if a == 1
           :a
       else
           :b
       end
:a

Also let is white-space sensitive in this fashion and needs a ; from time to time.

2 Likes

I like the suggestion that operators require symmetric whitespace, so this one would fail

if a == 1 :a else :b end
1 Like

It is also quite hard to read, relative its simple content, and would never pass review at work or in my public code. In my opinion

  • Always linebreak and indent if expressions.
  • Short circuit conditionals (&& and ||) are great for one liners in conjunction with return or when throwing errors.
  • Ternary expressions are fine if simple enough, but don’t be shy to parenthesize the sub-expressions, especially the conditional, if it helps to make the code structure clearer.
1 Like

@bertschi , I wonder if Julia is interpreting one of the : in if a == 1 :a else :b end as a range operator, like @Jollywatt suggested.

@GunnarFarneback , I agree that the if/else one-liner is harder to read. I was just giving an example for comparison. The ? : syntax is easier to read for one-liners, but it seems it’s too ambiguous without ( ). I’m starting to appreciate short-circuit conditionals more now.

It’s rarely ambiguous. Example:

abs(x) = x<0 ? -x : x

or if you prefer longform function style (I don’t, since I like conciseness):

function abs(x)
    return x<0 ? -x : x
end

In contrast, with if usually such a thing would be written:

function abs(x)
    if x < 0
        return -x
    else
        return x
    end
end

on occasion I’ll make one-liner ifs or do something like this, when it feels right:

abs(x) =
    if x < 0;  -x 
    else  x 
    end

The times I run into ambiguity with ? : usually arise from some other expression being combined with the ternary, in which case parentheses become necessary, e.g.:

x + (d > 0 ? d : zero(d))

or, when the returned expression is a range:

true ? (1:2) : (2:3)

but I’ve never seen somebody put return statements inside the ? : sub-expressions, and I don’t see any reason to. Sometimes I might desire to make an assignment in the sub-expression, in which case parenthesizing it has seemed fairly obvious.

By the way, I love one-liner short-circuit && and || for returning from a function early, but for simple ternary expressions like this I prefer the ? : ternary syntax (or sometimes if statements, depending on mood).

On the topic of one-liner if statements (and loops), I’ve adopted a habit of placing a semicolon ; after the condition. Like @bertschi demonstrated, if you don’t, : gets interpreted as a range operator. Additionally there’s this:

julia> if cond (:a) else (:b) end
ERROR: syntax: space before "(" not allowed in "cond (" at REPL[1]:1
julia> if cond [1,2,3] else [4,5,6] end
ERROR: syntax: space before "[" not allowed in "cond [" at REPL[1]:1
julia> var = :x
       :( if cond $var=1 else $var=2 end )
ERROR: syntax: unexpected "="

The last one is most likely to happen when interpolating variables into the @btime macro call.

So, since one-liner let, do, and try..catch have trained me to add ; anyway, I do the same after the condition for one-liner if, for, and while to avoid surprise.

1 Like

Maybe it’s just me, but i really think the ternary operator is less readable in julia code, since : is used all the time to construct ranges. Probably this will feel very strange to others, but i would prefer an else instead of :, like this:

condition? a else b
1 Like