[erlang-questions] The If expression

1 view
Skip to first unread message

Fredrik Svahn

unread,
Jan 1, 2009, 3:14:46 PM1/1/09
to Erlang mailing list
I would like to hear your views and proposals for an alternative
syntax for the 'if' expression. If the majority thinks that the result
of the discussion in this thread is interesting enough I will sum it
up and submit it as an Erlang Enhancement Proposal.

The background is the intensive discussion on this mailing list back
in March. There has also been quite a number of questions to the
mailing list on how to use the 'if'-expression, generally leading to a
recommendation to use the 'case'-expression instead of 'if'. Some have
even argued that the 'if' expression should be retired/obsoleted since
it is useless in its current form. I find myself almost never ever
using the 'if'-expression due to the limitations listed below. At the
same time I feel that the 'case'-expression is to "heavy-weight" or
clumsy when used for branching on bools.

Today an 'if'-expression is defined as:

if GuardSeq1 -> Body1;
...;
GuardSeqN -> BodyN
end

With the added (undocumented?) twist that if a guard throws an
exception the guard simply evaluates to false.

In addition to the above (which is kept for backwards compatibility) I
suggest adding an alternative form with the following syntax:

if Expression -> Body else -> ElseBody end

which should act as syntactical sugar for the more clumsy and unintuitive:

case Expression of
true -> Body;
_Else -> ElseBody
end

My hope is that with this improvement 'if'-statements would be the
recommended choice for expressions evaluating to a bool() and
'case'-statements the natural choice for all other expressions. This
proposal is in no way competing with other recently proposed control
flow improvements, such as using the already reserved word 'cond' and
"unnesting case statements".

I think this would be rather simple to implement. I believe it could
for instance be done with just a 4-5 lines in the parser. It would
also be "almost 100%" backwards compatible ('else' would need to
become a reserved word, but this could very well be needed already by
earlier enhancement proposals, e.g. by EEP-25).

The proposal would address two of the problems with the current syntax:

1. Today only guards are allowed, not full expressions. For instance,
today it is NOT ok to write:

if erlang:system_info(smp_support) -> init_smp();
true -> init()
end.

which many believe is a rather serious limitation of the usefulness.

2. At least of of the guards have to evaluate to true, otherwise a
runtime error will occur. This leads to unintuitive constructs like:

if Bool -> do_true();
true -> do_false() end.

Which could be replaced by a more intuitive:

if Bool -> do_true() else -> do_false() end.


An open question is if it should also be allowed to have expressions
without an 'else' clause, as proposed by Damien Katz, i.e.:

if Expression -> Body end

Which might be useful in some cases, e.g.

if Logging -> log:write("Aiee") end,

This could be handled by introducing a default return value for the
else clause in the odd cases where we might sometimes care about the
value of the "if"-expression, i.e.:

LoggingSucceded = if Logging -> log:write("Aiee") end

In this particular case returning false might seem like a good idea,
but in other cases e.g. an empty list might be just as natural. I am
just not sure how to apply the rule of least surprise to this...

Another proposal might be to allow "if"-expressions without an else
clause only when it is obvious to the compiler that the return value
will be discarded. The second example would thus result in a
"LoggingSucceded is unsafe in 'if' (line x)" error when compiling
(similar to what exists for e.g. case clauses today).

BR /Fredrik
_______________________________________________
erlang-questions mailing list
erlang-q...@erlang.org
http://www.erlang.org/mailman/listinfo/erlang-questions

Hakan Mattsson

unread,
Jan 2, 2009, 6:05:32 AM1/2/09
to Fredrik Svahn, Erlang mailing list
On Thu, 1 Jan 2009, Fredrik Svahn wrote:

> if Expression -> Body else -> ElseBody end
>
> which should act as syntactical sugar for the more clumsy and unintuitive:
>
> case Expression of
> true -> Body;
> _Else -> ElseBody
> end
>
> My hope is that with this improvement 'if'-statements would be the
> recommended choice for expressions evaluating to a bool() and
> 'case'-statements the natural choice for all other expressions. This
> proposal is in no way competing with other recently proposed control
> flow improvements, such as using the already reserved word 'cond' and
> "unnesting case statements".

I do not fancy any of these two code examples as they promote a sloppy
programming style. In my opinion, a boolean expression should be a
boolean expression and nothing else. If a boolean expression evaluates
to anything else than 'true' or 'false', the evaluation should fail in
the normal case. Sometimes catch-all-clauses are useful, but in many
cases they may delay detection of bugs.

> 2. At least of of the guards have to evaluate to true, otherwise a
> runtime error will occur. This leads to unintuitive constructs like:
>
> if Bool -> do_true();
> true -> do_false() end.
> Which could be replaced by a more intuitive:
>
> if Bool -> do_true() else -> do_false() end.

How realistic is it to expect that the common form of future
if-statements are written as one-liners?

If the if-statement does not fit in one line, I think that the the new
proposed syntax and the classic case-statement are very similar:

if
Bool -> do_true()
else -> do_false()
end.

case Bool of
true -> do_true();
false -> do_false()
end.

They are in fact so similar so that I see no point in adding a new
language construct.

You argued that the following code is unintuitive

if
Bool -> do_true();
true -> do_false()
end.

while the following is intuitive

if
Bool -> do_true()
else -> do_false()
end.

I find the new proposed syntax to be very similar to the old
if-statement. The old syntax may be a little bit uglier than the new
syntax, but that is not the same as unintuitive. I definitely prefer
the case-syntax, for if-then-else statements.

> An open question is if it should also be allowed to have expressions
> without an 'else' clause, as proposed by Damien Katz, i.e.:
>
> if Expression -> Body end
>
> Which might be useful in some cases, e.g.
>
> if Logging -> log:write("Aiee") end,

This would be an incompatible language change that breaks lots of
code. I frequently write code that is intended to provoke a crash (for
example by not having a catch-all-clause in if-statements) in those
cases when some variable has an unexpected value.



> This could be handled by introducing a default return value for the
> else clause in the odd cases where we might sometimes care about the
> value of the "if"-expression, i.e.:
>
> LoggingSucceded = if Logging -> log:write("Aiee") end
>
> In this particular case returning false might seem like a good idea,
> but in other cases e.g. an empty list might be just as natural. I am
> just not sure how to apply the rule of least surprise to this...
>
> Another proposal might be to allow "if"-expressions without an else
> clause only when it is obvious to the compiler that the return value
> will be discarded. The second example would thus result in a
> "LoggingSucceded is unsafe in 'if' (line x)" error when compiling
> (similar to what exists for e.g. case clauses today).

Is this what you mean with intuitive? ;-)

/Håkan
---
Håkan Mattsson (uabhams)
Erlang/OTP, Ericsson AB

Fredrik Svahn

unread,
Jan 2, 2009, 7:54:56 AM1/2/09
to Hakan Mattsson, Erlang mailing list
> I do not fancy any of these two code examples as they promote a sloppy
> programming style. In my opinion, a boolean expression should be a
> boolean expression and nothing else. If a boolean expression evaluates
> to anything else than 'true' or 'false', the evaluation should fail in
> the normal case. Sometimes catch-all-clauses are useful, but in many
> cases they may delay detection of bugs.

You are right, of course. The catch-all should be replaced with a false-clause.

> I find the new proposed syntax to be very similar to the old
> if-statement. The old syntax may be a little bit uglier than the new
> syntax, but that is not the same as unintuitive.

The first unintuitive part about the current "if" (at least for
beginners) is that the true clause in my example is evaluated when the
Bool evaluates to false. This problem does not exist with an else
clause. The second unintuitive part is that exceptions are silently
swallowed.

if length({}) == 0 -> its_empty; true -> its_not_empty end.


By the way, just found this: http://www.trapexit.org/If_Then

Richard O'Keefe

unread,
Jan 22, 2009, 9:54:35 PM1/22/09
to Fredrik Svahn, Erlang mailing list

On 2 Jan 2009, at 9:14 am, Fredrik Svahn wrote:
> In addition to the above (which is kept for backwards compatibility) I
> suggest adding an alternative form with the following syntax:
>
> if Expression -> Body else -> ElseBody end
>
> which should act as syntactical sugar for the more clumsy and
> unintuitive:
>
> case Expression of
> true -> Body;
> _Else -> ElseBody
> end
>
I'm sorry for the late reply, but December/January is the
long holiday in New Zealand, and I only got back yesterday.

This is a very bad idea because
- It would give us two very *different* conditional structures
with the *same* keyword, where it is hard to tell which one
was intended until several lines later.

- 'if' isn't actually all that rare. In some code I wrote last
year, I find one 'if' for every four 'case's. The OTP sources
have 1 if/10 case, which is less common, but not _that_ rare.
If the old sense of 'if' were rare, we could probably live with
the confusion.

- The new syntax is not consistent with Erlang "style", which
expects multiple arms as a matter of routine (like the Fortran,
Ada, Eiffel, Algol 68 "block if" constructs, but without an "else").

- As a result the new syntax is rather inconvenient. The majority
of my existing uses of 'if' have more than one arm, so would need
nested new-'if's. It's hard to see that as an improvement.


> My hope is that with this improvement 'if'-statements would be the
> recommended choice for expressions evaluating to a bool()

It would not be an improvement.
Such expressions should be avoided, not canonised.


> The proposal would address two of the problems with the current
> syntax:
>
> 1. Today only guards are allowed, not full expressions. For instance,
> today it is NOT ok to write:
>
> if erlang:system_info(smp_support) -> init_smp();
> true -> init()
> end.
>
> which many believe is a rather serious limitation of the usefulness.

And many don't. In fact this is an excellent example of what's
wrong with Boolean functions. The interface *should* have been
something like

case erlang:system_info(smp)
of not_supported -> ...
; supported -> ...
end

and other values are imaginable: smp support might have been compiled
in, but not actually useful because only one core is available. So
maybe the interface should have been

case erlang:system_info(smp)
of not_supported -> ...
; interfaces_supported -> ...
; functional_and_useful -> ...
end

Indeed, if I wanted to know whether to set up for SMP, I would
probably use system_info(multi_scheduling) instead:

case erlang:system_info(multi_scheduling)
of disabled -> no SMP or only one scheduler
; blocked -> there are multiple schedulers and all
but one of them are blocked
; enabled -> multiple schedulers are really available
end

About 40 years ago when enumeration types were introduced it
was pointed out that people over-used Boolean. I've been guilty
of that myself. In Erlang, our aim is to write reliable and
maintainable software. We shouldn't regard it as a PROBLEM
that Boolean expressions are hard to use, we should regard it
as a heaven-sent OPPORTUNITY to critically, and I really do mean
SERIOUSLY critically review all uses of Boolean to see whether
they are really appropriate. My own experience is that in the
majority of cases they are not. (My use of 'if' is almost
entirely arithmetic comparisons.)

> 2. At least of of the guards have to evaluate to true, otherwise a
> runtime error will occur. This leads to unintuitive constructs like:
>
> if Bool -> do_true();
> true -> do_false() end.
>
> Which could be replaced by a more intuitive:
>
> if Bool -> do_true() else -> do_false() end.

It may be more FAMILIAR, but that doesn't mean 'else' is a good
thing. I know that writing '; true ->' is a very easy way to get
'else' in Erlang, but we have a couple of decades of psychology-
of-programming results to show that it's a bad idea. I have
started to replace by

if X > Y -> a() if X > Y -> a()
; true -> b() ; X =< Y -> b()
end end

if X > Y -> a() if X > Y -> a()
; X < Y -> b() ; X < Y -> b()
; true -> c() ; X ==Y -> c()
end end

which I find mildly annoying when _writing_ the code
but enormously helpful when _reading_ it.

This is one reason why I regard a proposal to add 'else'
to Erlang as a bad idea.

> An open question is if it should also be allowed to have expressions
> without an 'else' clause, as proposed by Damien Katz, i.e.:
>
> if Expression -> Body end

Obviously not, because

if Guard -> Body end

is already legal Erlang syntax.

> Which might be useful in some cases, e.g.
>
> if Logging -> log:write("Aiee") end,

Dijkstra's "A Discipline of Programming" gives arguments against
using one-armed ifs in imperative code. In a mostly functional
language it's even worse. Erlang gets this *right*: if you don't
say what to do in some case, then a situation has arisen that
your program does not handle, and the right answer is an exception.

> In this particular case returning false might seem like a good idea,
> but in other cases e.g. an empty list might be just as natural. I am
> just not sure how to apply the rule of least surprise to this...

The rule of least surprise has a corollary:
if there ISN'T a least surprising result,
there is no result (that is, there is an exception).

Hynek Vychodil

unread,
Jan 23, 2009, 5:02:57 AM1/23/09
to Richard O'Keefe, Erlang mailing list

Nice point. Really nice Erlangish way.

> An open question is if it should also be allowed to have expressions
> without an 'else' clause, as proposed by Damien Katz, i.e.:
>
>  if Expression -> Body end

Obviously not, because

    if Guard -> Body end

is already legal Erlang syntax.

> Which might be useful in some cases, e.g.
>
>  if Logging -> log:write("Aiee") end,

Dijkstra's "A Discipline of Programming" gives arguments against
using one-armed ifs in imperative code.  In a mostly functional
language it's even worse.  Erlang gets this *right*: if you don't
say what to do in some case, then a situation has arisen that
your program does not handle, and the right answer is an exception.

> In this particular case returning false might seem like a good idea,
> but in other cases e.g. an empty list might be just as natural. I am
> just not sure how to apply the rule of least surprise to this...

The rule of least surprise has a corollary:
  if there ISN'T a least surprising result,
  there is no result (that is, there is an exception).


_______________________________________________
erlang-questions mailing list
erlang-q...@erlang.org
http://www.erlang.org/mailman/listinfo/erlang-questions



--
--Hynek (Pichi) Vychodil

Analyze your data in minutes. Share your insights instantly. Thrill your boss.  Be a data hero!
Try Good Data now for free: www.gooddata.com

Fredrik Svahn

unread,
Jan 23, 2009, 5:35:52 PM1/23/09
to Richard O'Keefe, Erlang mailing list
On Fri, Jan 23, 2009 at 3:54 AM, Richard O'Keefe <o...@cs.otago.ac.nz> wrote:
>
> On 2 Jan 2009, at 9:14 am, Fredrik Svahn wrote:
> I'm sorry for the late reply, but December/January is the
> long holiday in New Zealand, and I only got back yesterday.

Thanks for the reply. To be honest I had put my plans to write an EEP
on hold because I only got one reply which was mostly disagreeing.
Using expressions in 'if'-expressions is more of a nice-to-have for
me, so I will just keep on using 'case'.

A few comments below...

> - It would give us two very *different* conditional structures
> with the *same* keyword, where it is hard to tell which one
> was intended until several lines later.

I agree that this is a disadvantage with the proposal. I also agree
with your statement below that if the old sense of 'if' is rare (or
even discouraged), we can probably live with the confusion.

>- 'if' isn't actually all that rare. In some code I wrote last
> year, I find one 'if' for every four 'case's. The OTP sources
> have 1 if/10 case, which is less common, but not _that_ rare.
> If the old sense of 'if' were rare, we could probably live with
> the confusion.

Newcomers to this mailing list asking how to use "if" are invariably
recommended to use "case" instead. Combined with the fact that you can
only use guards it is almost FAQ material. Considering this and all
the beating the 'if' expression has gotten over the years on the
mailing list my assumption was that the use of 'if' was really rather
infrequent even outside of OTP. I might be wrong.

(Hmmm, I just checked, and there actually is a chapter about 'if':s in
the FAQ. It is recommending the use of 'case' and even states that
"case is used much more frequently").

> - The new syntax is not consistent with Erlang "style", which
> expects multiple arms as a matter of routine (like the Fortran,
> Ada, Eiffel, Algol 68 "block if" constructs, but without an "else").

Just out of curiosity, is there any other programming language out
there which has an 'if' and which does not have an 'else' apart from
the Commodore 64 BASIC? I am not saying Erlang should have because
everyone else does. I am just wondering out of curiosity.

Even Haskell has an if ... else construct (which like this proposal is
syntactic sugar for (or equivalent with) a case construct).

> - As a result the new syntax is rather inconvenient. The majority
> of my existing uses of 'if' have more than one arm, so would need
> nested new-'if's. It's hard to see that as an improvement.

The proposal is backwards compatible (with the obvious exception that
'else' would need to become a keyword) so there is no need for you to
change your coding style.

> About 40 years ago when enumeration types were introduced it
> was pointed out that people over-used Boolean. I've been guilty
> of that myself. In Erlang, our aim is to write reliable and
> maintainable software. We shouldn't regard it as a PROBLEM
> that Boolean expressions are hard to use, we should regard it
> as a heaven-sent OPPORTUNITY to critically, and I really do mean
> SERIOUSLY critically review all uses of Boolean to see whether
> they are really appropriate. My own experience is that in the
> majority of cases they are not.

There is a lot of code out there to critically review, then. In fact
'true' is the second most popular atom in the OTP code, and 'false' is
the fourth most popular.

percent count atom
7.749% 15074 ok
7.489% 14568 true
4.908% 9547 error
4.895% 9523 false
1.574% 3061 mandatory
1.025% 1994 undefined
0.912% 1774 EXIT
0.904% 1758 reply
0.870% 1693 value

> (My use of 'if' is almost entirely arithmetic comparisons.)

What else can it really be used for? Ok, there are a couple of guard
bifs, but that is basically it, isn't it?

BR /Fredrik

Zvi

unread,
Jan 25, 2009, 6:01:42 AM1/25/09
to erlang-q...@erlang.org

1. I'm in favor of deperectaing if and using some LISP-like cond mechanism
(cond even reserved word), i.e.:

cond
BoolExpr1 [when Guard1] -> Val1;
BoolExpr2 [when Guard2] -> Val2;
...
BoolExprN [when GuardN] -> ValN;
[true -> ValOtherwise]
end

there might be some syntactic sugar for OTHERWISE clause (but better not,
see below).
Also might needed some mechanism for combining several BoolExpr/Guard
clauses with the same Val expression.

2. I miss C/C++/Java like conditional if expresssion:

Cond ? Expr1 : Expr2

it's much clearer than Erlang's:

case Cond of
true -> Expr1 ;
false -> Expr2
end

I using macro like:

-define(IF(cond,e1,e2), (case (cond) of true -> (e1); false -> (e2) end) ).

but syntactic sugar at the language level would be much better.

3. With all the multicore buzz, I that new speculative parallel control flow
mechanisms are needed, like pcase and pcond:

pcase Expr of
Pattern1 [when Guard1] -> Expr1;
Pattern2 [when Guard2] -> Expr2;
...
PatternN [when GuardN] -> ExprN
end

pcond
Cond1 [when Guard1] -> Expr1;
Cond2 [when Guard2] -> Expr2;
...
CondN [when GuardN] -> ExprN
end

Here conditions/patterns aren't calculated/matched in the serial orded from
top down, but in parallel and when the first is true or matched it's
corresponding expression is evaluated, thus "else"/"otherwise/->true" clause
is must be avoided. Thus Richard's examples are already multicore
future-proof ;)

pcond
X > Y -> a();


X =< Y -> b()
end

pcond


X > Y -> a();
X < Y -> b();

X ==Y -> c();
end

pcase math:sign(X-Y) of
1 -> a();
-1 -> b();
0 -> c();
end

The next part is speculative execution of expressions themselves, but before
there are need to be mechanism of specifing pure functions.

Zvi


--
View this message in context: http://www.nabble.com/The-If-expression-tp21244494p21650458.html
Sent from the Erlang Questions mailing list archive at Nabble.com.

Richard O'Keefe

unread,
Jan 25, 2009, 10:50:13 PM1/25/09
to Fredrik Svahn, Erlang mailing list

On 24 Jan 2009, at 11:35 am, Fredrik Svahn wrote:
>> - 'if' isn't actually all that rare. In some code I wrote last
>> year, I find one 'if' for every four 'case's. The OTP sources
>> have 1 if/10 case, which is less common, but not _that_ rare.
>> If the old sense of 'if' were rare, we could probably live with
>> the confusion.
>
> Newcomers to this mailing list asking how to use "if" are invariably
> recommended to use "case" instead. Combined with the fact that you can
> only use guards it is almost FAQ material. Considering this and all
> the beating the 'if' expression has gotten over the years on the
> mailing list my assumption was that the use of 'if' was really rather
> infrequent even outside of OTP. I might be wrong.
>
> (Hmmm, I just checked, and there actually is a chapter about 'if':s in
> the FAQ. It is recommending the use of 'case' and even states that
> "case is used much more frequently").

Ten to one *is* "much more frequently".
However, one in ten is not a negligible proportion.

> Just out of curiosity, is there any other programming language out
> there which has an 'if' and which does not have an 'else' apart from
> the Commodore 64 BASIC? I am not saying Erlang should have because
> everyone else does. I am just wondering out of curiosity.

Dijkstra's notation, Promela, classic Lisp (to this day, if you want
an "else" in a COND, you have to use the equivalent of "; true ->"),
Occam.

- As a result the new syntax is rather inconvenient. The majority
>
>> of my existing uses of 'if' have more than one arm, so would need
>> nested new-'if's. It's hard to see that as an improvement.
>
> The proposal is backwards compatible (with the obvious exception that
> 'else' would need to become a keyword) so there is no need for you to
> change your coding style.

That much was obvious. My point is that common things like


if X < Y -> a()
; X > Y -> b()

; X ==Y -> c()
end
have to be rewritten as


if X < Y -> a()

else
if Y > Y -> b()
else
c()
end
end
so there isn't much *point* in changing coding style either.


>
> There is a lot of code out there to critically review, then.

Yes, there is. In fact, looking for 'true' and 'false' is a good
way to find code that could be clearer.

> In fact
> 'true' is the second most popular atom in the OTP code, and 'false' is
> the fourth most popular.

No dispute there. Oh, that it were not so!


>
> What else can it really be used for? Ok, there are a couple of guard
> bifs, but that is basically it, isn't it?

Type tests.

Reply all
Reply to author
Forward
0 new messages