One-line `if ... then ...` syntax

Which of the following two options do you prefer for a one-line if-then syntax?

  • bool && expr
  • if bool then expr

0 voters

Related Github issue:

https://github.com/JuliaLang/julia/issues/16389

Typo FYI: if bool then expr end (you missed end)

Oh I guess you didnā€™t mean that, and I totally missed the then. IMO it would be too weird to allow if without end, though an optional then could be nice

4 Likes

To remove the need for end how about Perlā€™s

expr unless bool

or Pythonā€™s

expr if bool

1 Like

Not a fan, since this seems to imply that expr is evaluated first.

11 Likes

I think a ā€œfamiliar vs unfamiliarā€ poll is usually going to give the ā€œfamiliarā€ answer.

The problem with a && b is that inconsistent with Juliaā€™s normal rules: it looks like an operator expression but it isnā€™t: itā€™s control-flow syntax. That b might not be evaluated is the point of this syntax (vs just using &). IMO special syntax should look like special syntax to avoid misleading readers.

11 Likes

How about scheme/racketā€™s

(when bool expr)

or

(unless bool expr)

without the parens, of course.

ā€¦

What looks like special syntax? Words? The same we use as variable names? && and || are adopted by many languages working this exact way and are far better to identify and write into text than and and or.

4 Likes

Reserved keywords are special syntax for meta-level programming (except for true and false, which are special cased).

This is the list of reserved keywords in Julia: baremodule, begin, break, catch, const, continue, do, else, elseif, end, export, false, finally, for, function, global, if, import, let, local, macro, module, quote, return, struct, true, try, using, while. Those keywords are not allowed to be used as variable names.

To be more evidently syntax, these could have been prefixed with @ such as in @for but this may have been considered too noisy.

1 Like

Exactly my point. The syntax for meta-level programming is just a list of exceptions for the open list of identifiers. I believe it is much more misleading for a novice that an word that could be a variable/function/type/etc name is actually special, and that the only way to know this is knowing the whole list of exceptions. So I do not entirely buy the point that a solution using keywords is better. Finally, in is already an exception to complementary idea that operators are restricted to non-letter symbols.

3 Likes

For simplicity Iā€™d remove the in/isa exceptions to the infix operator policy rather than using the existence of some exceptions to justify others.

Thereā€™s already if a b end and a && b. Unless you are suggesting disallowing the latter (how?) the only possibility is to add a third version to the others, that may or may not supplant them (probably not.)

4 Likes

Just to chime in with an opinion.

I donā€™t always understand the obsession of writing ā€œshorter codeā€, when most of the work is often having to read it multiple times.

To me, if bool then expr is much clearer than bool && expr.

Code readability is often times a good reason to not improve a package by 5% speed, so I would argue the same is the case here, that increasing the length of the statement to make the code easier to re-read is preferred.

Kind regards

17 Likes

Yes I am in favor of removing a && b, a || b in Julia 2.0. Julia has if expressions (rather than statements), which removes the need for such special cases.

The same applies to a ? b : c.

It would be nice to have block delimiters shorter than end, but thatā€™s a broader issue.

4 Likes

if bool expr end is seldom used, because itā€™s even harder to read than bool && expr. I view if bool expr end as more of an artifact of how Julia parsing works than as an actual feature of the language.

a && b is certainly not going anywhere, because we still need it for regular old boolean comparison. The fact that && short-circuits is merely a performance optimization. One of the things I donā€™t like about bool && expr is that it is taking a performance optimization and turning it into semantics.

The other thing I donā€™t like about bool && expr is that it is hard to read. Itā€™s not a matter of getting used to itā€”Iā€™ve seen it plenty of times and I still think itā€™s harder to read than
if bool then expr.

Iā€™ll note that if bool then expr is not quite as easy to read without syntax highlighting as it would be with syntax highlighting. Maybe itā€™s easier to read with real code, like this:

if isempty(x) then error("oops.")

Hereā€™s an example with syntax highlighting (from a different language, Elm)

image

(Elm is a pure functional language, so if ... then statements must include an else.)

10 Likes

I should have specified I favor getting rid of the symbols for short-circuiting control rather than getting rid of boolean operators. Though āˆØ āˆ§ express boolean operations more clearly IMO.

As a side note, AFAIU this work-reducing optimization can sometimes reduce throughput by putting more pressure on the branch predictor. If compute units are available then both branches can be computed simultaneously without extra cost which is helpful if the branch isnā€™t well predicted.

I strongly dispute this claim. The introduction of || and && in The C programming language of K&R just says:

The || operator groups left-to-right. It returns 1 if either of its operands compare unequal to zero, and 0 otherwise. Unlike |, || guarantees left-to-right evaluation: the first operand is evaluated, including all side effects; if it is unequal to 0, the value of the expression is 1.

The wikipedia page on short-circuit evaluation says nothing on this. I have a hard time believing that for combining simple relational operators (<, >, ==, and !=) over primitive data, && and || (which may incur in a jump) are more lightweight than | and & (the simplest possible operations to project in a circuit).

On the contrary, && and || short-circuiting behavior are absolutely about semantics. You can see in the K&R quote, they gave a guarantee. If the whole point of && and || was not to be short-circuiting they would not even have a reason to exist, because in C any integer greater than zero counts as true, together with > 0 you could have used | and & as replacements without loss of anything besides the short circuiting behavior.

&& and || make much easier write code like i <= n && a[i] != x, or s.tag == X && s.union_value, or basically any other condition in which the second expression would lead to an access memory violation or undefined behavior if the first expression did not guarantee that it would not. Lots of code rely on this behavior, and I do not believe this is an accident but the original intent of the short-circuiting && and ||.

I had the same experience with the unless and when of Ruby, I always though they were harder to understand than && and ||. This is anecdotal evidence. Your experience is not universal. But I would argue that && and || have the advantage that you just need to think about the truth table and you remember what they are supposed to be doing. Also anecdotal but not having English as my first language I do have much more love for symbols than words in English, which will are often already sufficiently overloaded just for someone trying to learn English.

Because it is not an work-reducing optimization.

10 Likes

Wow. There will certainly be some strong opposition to that. This would be terrible. I use these all the time.

How will you write

if a && b || c
    expr
end

?
I also love a ? b : c.

Shorter code can be easier to read than longer code in many cases. This is an idiom you get quickly used to.

I just wrote this in one line for convenience. I actually meant

if bool
    expr 
end
6 Likes

While a good discussion is always fun, I think good to point out that this will never actually happen: PSA: Julia is not at that stage of development anymore

There will be a time when we start working on Julia 2.0, which will include some breaking changes, or we wouldnā€™t call it 2.0, we would just call it Julia 1.x for some value of x. At that point in time, we can consider breaking changes. But despite what it sounds like, this is not the time to make all the random changes that anyone might want to. Frankly, that time has passed for Julia and will never come again. There will probably be a few renamings of unfortunately named things. But thatā€™s not what the 2.0 release is really about.

Your proposed changes would create such massive havoc throughout the ecosystem that probably more than 90% of code will stop working. There might not be any major packages left standing, and the reputation of the language would be devastated.

Removing && and || is basically impossible. The best you could do is introduce a more attractive alternative, and hope that it wins out over time. But so far I havenā€™t seen any alternatives I would use.

14 Likes

I agree with your point that shorter code can be easier to work with, but for me that has only been the case when:

  1. I am deepy engrossed in the code at the moment
  2. I understand the ā€œphysicsā€ or the math/main idea behind the code

The moment one has to handle a handover or look at the code a few months down the line, then I find that often I would have to write a lot of comments to remind me about the purpose of variables/functions.

Kind regards

1 Like

I wonā€™t take a side here, but one limitation of the && syntax that I have encountered is when trying to assign default values in a function definition:

function foo(x::AbstractVector, m::Union{Nothing,Int}=nothing)
    if m === nothing
	     m = length(x)
    end
	
    return sum(x[1:m])
end

works fine.

function bar(x::AbstractVector, m::Union{Nothing,Int}=nothing)
    m === nothing && m = length(x)
	
    return sum(x[1:m])
end

is an

ERROR: syntax: invalid assignment location "(m === nothing) && m" around REPL[1]:10

and rightly so! m = length(x) returns length(x) (an Int), not a Bool.

I guess if you really want a one-liner and donā€™t like if a b end you can use the following:

function bar(x::AbstractVector, m::Union{Nothing,Int}=nothing)
    m === nothing ? m = length(x) : nothing
	
    return sum(x[1:m])
end

(Purists may prefer isnothing(m).)