Proposal: `then`, `else` syntax to replace `&&`, `||` short-circuiting


#1

I don’t like short-circuiting. Here’s why:

It is a hack. i.e. It is (ab)using an implementation detail of && or ||, namely using the fact that the second operand is only evaluated if necessary to provide one-liner if pred or if !pred statements.

While I do experience a moment of childlike glee whenever I do something ‘clever’, I don’t consider it responsible language design. I teach maths students to prefer elegance over cleverness.

Consider C++ template metaprogramming. An entire meta-language has arisen from SFINAE – exploiting the fact that Substitution Failure Is Not An Error. It’s incredibly clever. People have got carried away doing absurdly complex operations by exploiting this behaviour. But it is insanely difficult to work with – leading to the poorest productivity I have ever witnessed. Nobody would design this syntax in retrospect.

Ok so I’m using a level 10 awfulness to illustrate a level 1 awfulness. But the point is the same.

My argument is this: short-circuiting creates an extra thing for the new user to have to figure out via RTFM. Also what is written on the page is conceptually far from what is going on so the user has to build brain machinery to handle this. Each time the encounter pred && statement they have to say to themselves ‘if pred, do statement’.

What if we provided the following syntax:

pred then statement
pred else statement

Anyone will be able to see that and instantly understand it and apply it elsewhere. No RTFM is necessary. No extra building of machinery is necessary. It is an effortless brain-scan/parse.

(EDIT: JB has shot a hole through using else here)

Another syntax idea would be extending the ternary operator to make either branch optional:

pred ? stA : stB
pred ? stA
pred ? : stB

While this might require looking up the ? operator, it is still a pleasant scan/parse.

It has been suggested to use pred : stB for the third option, but that currently parses as a range. One solution would be to change the syntax for a range to a..b a la Swift. I would be very much in favour of this as again it maps onto an existing concept: we all understand “a = 1…10” as pseudocode. Also it avoids clashing with Python’s S[a:b] slicing syntax which actually selects elements a through b-1 not b.

I’m writing this in the aftermath of the oh-so-painful RFC: Make and & or aliases for && and ||. #19788 thread. I think it would be a pity to launch a new language that has & bitwise and && sometimes used as straightforward logical and other times in short-circuiting.

I really like the idea of a language that facilitates picking up the syntax just by looking at it.


#2

AFAIK && and || always short circuit, not just sometimes. Also, many languages have them, or equivalent forms. See the table here.

Just out of curiosity, how would you deal with a && b && c && d?


#3

I really dislike the proposed ?: syntax, not only because it is unavailable, but because it is not understandable unless you specifically learn it. By contrast, && and || are present (and short circuiting) in many other languages. Making Julia easy to learn for newcomers is important, but we should not sacrifice making it easy to learn for those coming from a programming background already.

I also dislike the idea of removing the short-circuting logical operators. In my opinion, short circuiting logical operators is a very reasonable default; in most situations these operators are used with pure functions, in which case the fact that it is lazy is merely an optimization. Where laziness is semantically important, I think the short-circuiting operators at least have precedent (especially in languages like Perl or Shell).

But personally, I do not see what’s wrong with the slightly more verbose but very readable

if cond
    dosomething()
end

instead of the syntax proposals here

cond then dosomething()

I realize that many seem to prefer the style

cond && dosomething()

in a lot of Julia code today, but I dislike this style personally. I feel that it is much clearer (and honestly not that much extra typing) just to write out the if.


#4

I agree – this is an issue with the current convention of using foo && bar() for control flow instead of the slightly longer if foo; bar(); end, not with short-circuiting in itself.
I’d not be averse to something like if foo then bar() (à la CoffeeScript), but then again I don’t feel like that really warrants special syntax.


#5

The one-liner form does have one nice side-effect. Your code coverage stats don’t take a hit when you check for an unusual error, because the error branch is on the same line.


#6

I really like the idea of using then to allow if statements to be one-liners and replacing the short-circuiting. It would also solve the issue that @Tamas_Papp points out, in a more readable way than short-circuiting: if aa && b && c then d .
Of course there are more complex uses of that latter type of statement using shortcircuiting (i.e. if c has side effects) but I don’t think that is very clear coding style.


#7

Then, as @pfitzseb mentioned, you could nevertheless use

if cond; dosomething(); end

which is natural. But optimizing the number of lines in an expression for code coverage statistics is in my opinion a weird way to do development.


#8

The syntax

if cond; dosomething(); end

is already a one-liner, and not actually longer than

if cond then dosomething()

Furthermore, ; can be used to make other constructs like while and for one-liners, so it is a more general solution than then. Hence I am not in favour of adding this to the language.


#9

@fengyang.wang I agree with you regarding ?: – it is an extra thing to be learned.

However, please note that my primary proposal is:

cond then dosthg()
cond else dosthg()

The then and else tokens could be swapped out for && and || internally.

I just threw the ?: into the mix for completeness.

Given that branching upon conditions is something we do all the time, I would really like these as one-liners. Having to write out an if ... end block really goes against Julia’s drive towards concision. I will end up using short-circuits rather than this, even though I dislike them!

@Tamas_Papp my meaning was that && is sometimes used as a 1-line if, and other times as a straightforward logical AND. I wasn’t suggesting it behaves differently in different situations, which it doesn’t of course!


#10

Speaking on behalf of the lazy typers: please don’t make me type more for little reason :slight_smile:
I find the && and || exception-throw-trick quite cute and practical. I’ll also be the first to admit that if these little error checks would take three lines instead of this neat and compact one line, I could see myself considering not doing the check just to keep my function pretty. I am only half joking. Testing for edge conditions is not the fun part of coding and should thus probably not be made more tedious to do than it needs to be.


#11

Wait, isn’t the point of code coverage to make sure every part of your code is, well, covered?

I’m not too opposed to short circuiting in general. I like the current PR to replace them with and and or for the purpose of literate programing. However, I’m always puzzled by code which uses && and || as control flow operators when if then (the existing multi-line version) works just as well and is a lot more intuitive in that particular use case.


#12

Actually, that’s more of a bug rather than a feature, and it was discussed back in 2015 to get that fixed,
so that when you have flow control (of any sort) that has different paths on one line, or you have things like && and || that spread to multiple lines, each part would get marked as covered separately. I’d hoped that that had been fixed already, since it leads to misleading coverage results.


#13

Ideally, yes, but there’s a point of diminishing returns. Should I write a test case to generate a corrupt image for my image loading code, just to verify that my assertion works? I’d rather be encouraged to write the assertion in the first place, because that matters more.

The perfect being the enemy of the good, etc.


#14

in my eyes, && is strictly better than these proposed then syntaxes, because it is better syntax highlighted. then blends in too much. i also don’t like if end for single call or return for it takes too many rows. and writing it in one line is just awkward.


#15

What about (and this can be done right now) using @ifthen cond expr and @ifelse cond expr macros, if you want to make it clearer what is happening than && and || when they are used for control flow (i.e. with things like error, throw, return, break, continue) instead of boolean logic where you want to short-circuit for performance (or to avoid something not valid if previous conditions are false/true).


#16

If you remove the short-circuit behavior of &&, then if a && b && c then c may evaluate b or c unnecessarily.


#17

what about: a then b then c then d as a replacement for a && b && c && d?

This example proves the value of a then b vs if a; b; end. In the later case, you would need a lot of traling ends:

if a; if b; if c; if d; d; end; end; end; end


#18

Am I the only one that reads short-circuits as “and” and “or”?

x<0 && error()
x>=0 || error()

I naturally read those as “x negative and error” and “x nonnegative or error”. So if we’re discussing changes, I think you should be allowed to interchange && with and and || with or.


#19

:wink: Well, that was the whole point of @Ismael-VC’s PR.
Given a choice of only && and || , or and and or, I’d stick with the current ones (probably due to too many years with C and C-like languages, I even added the && || syntax to CachéObjectScript!) but I also don’t see having and and or in addition as being a bad thing either.


#20

In this PR https://github.com/JuliaLang/julia/pull/19788 (linked in the OP) they propose specifically that the and as replacement for && should NOT be shortcircuiting - I guess there are lots of ways to think about this.
EDIT: this comment was simultaneous with the above stating the same thing.