Use ⟹ instead of &&?

I find the && notation a bit weird for shortening ifs - it works, but it feels like a hack. A nicer format might be:

(condition) ⟹ do_something

It would be cool if there is a way to already define \implies to do so, but I guess there is the point of not using notation that nobody else does…

2 Likes

Hi Sariel, welcome to the community. I used to struggle with accepting the code like x && println("OK") too (where x is either true or false). But the section in the manual explains it nicely, I guess. I am now at peace with the notation. In fact, it then also makes perfect sense to write x || println("OK") instead of !x && println("OK").

6 Likes

I got used to it, but it feels like a lost opportunity for clarity ;).

I mostly avoid && and || in package code, because it decreases the granularity of code coverage. And of error message location info in general. Related issue:

9 Likes

You could always write a macro to replace by &&. E.g.

julia> macro ⟹(expr)
           replace_implies_call_with_short_circuiting_and!(expr)
           return expr
       end;

julia> function replace_implies_call_with_short_circuiting_and!(expr)
           if expr.head === :call && expr.args[1] === :⟹
               expr.head = :&&
               popfirst!(expr.args)
               for a in expr.args
                   a isa Expr && replace_implies_call_with_short_circuiting_and!(a)
               end
           end
       end;

julia> @⟹ true ⟹ println("Will print");
Will print

julia> @⟹ false ⟹ println("Won't print");
4 Likes

Logically x ⟹ y is !x || y and could thus be used to the desired effect. Unfortunately, short-circuiting requires delayed evaluation which requires macros in Julia and infix macros cannot be defined. A combination of and a macro could be used:

⟹(x, y) = !x || y()
macro dø(x); :(() -> $(esc(x))) end
julia> true ⟹  @dø error(:ha)
ERROR: ha

julia> false ⟹  @dø error(:ha)
true
3 Likes

I thought that Match.jl might be what you want, but then I realized it might be an overkill for your situation, where you’re most likely trying to make a one-liner code.
Although you can make an one-liner code using Match.jl, it will error if the condition is not met since you can’t exhaust all the possible conditions, and that won’t be something you’d want.

It does feel like a hack because && is primarily a boolean AND. This notation => makes more sense for a conditional, but I’d rather not add more syntax when the typical if (condition) do_something end already works and makes as much sense. Then again, I am biased because I prefer if end one-liners over && to begin with.

It’s also worth pointing out that short-circuiting semantics are not strict like other arithmetic operators (which act only after all inputs are evaluated), so they are formally control flow as well, which is why they are used as such in many languages for over half a century. A few dynamically typed languages, like Julia, lean into it by conditionally returning the 2nd expression, which may not be boolean. This is also a reason I personally prefer if end; on the occasion I assign the value of a conditional statement, I intend a potentially stable type from the branches or nothing, not a particular value of the condition depending on the operator.

2 Likes

Couldn’t agree more. Makes code less readable. Would be happy to never see it again.

3 Likes

I have an feature request (that would be breaking, and shouldn’t be seriously considered for a very long time) for backwards-looking macros. This seems like a scenario where they might be useful.

Related github issue for one-line if statements:

1 Like

I don’t like the lack of mandatory end or some other block delimiter. It’d be fine if it were restricted to a binary operation, but the conversation naturally involved else (which has historically been the case), and that runs into the long-solved dangling else problem:

if a then if b then x else y
could be
if a then (if b then x) else y # y depends on a
or 
if a then (if b then x else y) # y depends on b

Julia’s end-less syntax must actually disambiguate that example with || vs ?: for if-then vs if-then-else respectively, but I go through a similar struggle for:

a, b, x, y = true, false, "hello", "world"
a ? b : z ? x : y
#=
Is it
(a ? b : z) ? x : y
or
a ? b : (z ? x : y)
=#

I struggle with the precedence of binary operators as is, I can’t see it’s the latter, whether intuitively or with the operator precedence table on hand. Granted, the equivalent if a b elseif z x else y end statement is hard to read, but at least I can tell where the branches are.

PS you can fake then right now with if a #=then=# b end. If that seems a tad long, there’s the even shorter if (a) b end or if a; b end to explicitly divide the condition and branch.

4 Likes

Have come to like

condition || error

which runs through if the condition holds true and reads like condition ORELSE error.

2 Likes