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
> 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
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
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).
Obviously not, because
> 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
if Guard -> Body end
is already legal Erlang syntax.
Dijkstra's "A Discipline of Programming" gives arguments against
> Which might be useful in some cases, e.g.
>
> if Logging -> log:write("Aiee") end,
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.
The rule of least surprise has a corollary:
> 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...
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
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
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.
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.