[erlang-questions] if-elseif-else trick

20 views
Skip to first unread message

Viktor Söderqvist

unread,
Feb 16, 2019, 1:24:51 PM2/16/19
to erlang-q...@erlang.org
Hey list!

Last night I was thinking that it might be possible to implement
if-elseif-else syntax using macros and a parse transform, so you can
write like this:

-include_lib("elseif/include/elseif.hrl").

f(X) ->
?'if'(X > 0) -> pos
?elseif(X < 0) -> neg
?else -> zero
end.

It's possible. Here is how: https://github.com/zuiderkwast/elseif

Disclaimer: This may be considered macro and parse-transform abuse. You
may get strange syntax error messages if e.g. using ?elseif without
?'if' or if leaving out the mandatory ?else part.

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

Andreas Schultz

unread,
Feb 18, 2019, 3:02:35 AM2/18/19
to Viktor Söderqvist, erlang-q...@erlang.org
Hi Viktor,

Viktor Söderqvist <vik...@zuiderkwast.se> schrieb am Sa., 16. Feb. 2019 um 19:24 Uhr:
Hey list!

Last night I was thinking that it might be possible to implement
if-elseif-else syntax using macros and a parse transform, so you can
write like this:

-include_lib("elseif/include/elseif.hrl").

f(X) ->
    ?'if'(X > 0)   -> pos
    ?elseif(X < 0) -> neg
    ?else          -> zero
    end.

but why would you use such a thing when you can simply write:

if X > 0 -> pos;
   X < 0 -> neg;
   true -> zero
end.

Andreas


It's possible. Here is how: https://github.com/zuiderkwast/elseif

Disclaimer: This may be considered macro and parse-transform abuse. You
may get strange syntax error messages if e.g. using ?elseif without
?'if' or if leaving out the mandatory ?else part.

Viktor
_______________________________________________
erlang-questions mailing list
erlang-q...@erlang.org
http://erlang.org/mailman/listinfo/erlang-questions
--
--
Dipl.-Inform. Andreas Schultz

----------------------- enabling your networks ----------------------
Travelping GmbH                     Phone:  +49-391-81 90 99 0
Roentgenstr. 13                     Fax:    +49-391-81 90 99 299
39108 Magdeburg                     Email:  in...@travelping.com
GERMANY                             Web:    http://www.travelping.com

Company Registration: Amtsgericht Stendal        Reg No.:   HRB 10578
Geschaeftsfuehrer: Holger Winkelmann          VAT ID No.: DE236673780
---------------------------------------------------------------------

zx...@zxq9.com

unread,
Feb 18, 2019, 4:34:39 AM2/18/19
to erlang-q...@erlang.org
On 2019年2月18日月曜日 9時02分11秒 JST Andreas Schultz wrote:
> Hi Viktor,
>
> Viktor Söderqvist <vik...@zuiderkwast.se> schrieb am Sa., 16. Feb. 2019 um
> 19:24 Uhr:
>
> > Hey list!
> >
> > Last night I was thinking that it might be possible to implement
> > if-elseif-else syntax using macros and a parse transform, so you can
> > write like this:
> >
> > -include_lib("elseif/include/elseif.hrl").
> >
> > f(X) ->
> > ?'if'(X > 0) -> pos
> > ?elseif(X < 0) -> neg
> > ?else -> zero
> > end.
> >
>
> but why would you use such a thing when you can simply write:
>
> if X > 0 -> pos;
> X < 0 -> neg;
> true -> zero
> end.


`if ... true` is a code stink.
Be explicit in your range tests -- that is what `if` is actually good at:

if
X > 0 -> pos;
X < 0 -> neg;

X == 0 -> zero
end

If you find yourself *needing* a `true` catchall, you should be writing a function head or `case`.


That said...
The original premise of this thread is based on a misunderstanding of `if` and probably inexperience with `case`.


-Craig

Raimo Niskanen

unread,
Feb 18, 2019, 9:36:43 AM2/18/19
to erlang-q...@erlang.org
On Mon, Feb 18, 2019 at 06:34:11PM +0900, zx...@zxq9.com wrote:
> On 2019年2月18日月曜日 9時02分11秒 JST Andreas Schultz wrote:
> > Hi Viktor,
> >
: :

> > but why would you use such a thing when you can simply write:
> >
> > if X > 0 -> pos;
> > X < 0 -> neg;
> > true -> zero
> > end.
>
>
> `if ... true` is a code stink.

That is a personal opinion.

if ... end selects the clause of the first succesful guard expression,
and 'true' is simply a succesful guard expression.

> Be explicit in your range tests -- that is what `if` is actually good at:
>
> if
> X > 0 -> pos;
> X < 0 -> neg;
> X == 0 -> zero
> end

That is a pretty example that is obviois about which cases it handles,
(note that it also handles non-numbers since all terms have an order)
but sometimes you instead want it to be obvious that you handle all cases.

if
is_integer(X), 0 =< X -> non_negative;
true -> erlang:error(badarg)
end

>
> If you find yourself *needing* a `true` catchall, you should be writing a function head or `case`.

I have no problem using 'if' for that. The 'true' catchall in 'if' is just
as much a catchall as the '_' catchall in 'case'.


>
>
> That said...
> The original premise of this thread is based on a misunderstanding of `if` and probably inexperience with `case`.

Agreed

>
>
> -Craig

--

/ Raimo Niskanen, Erlang/OTP, Ericsson AB

Viktor Söderqvist

unread,
Feb 19, 2019, 2:37:17 AM2/19/19
to erlang-q...@erlang.org
Hi!

My example proved silly. Of course, normal 'if' is fine for guard
expressions and catch-all is to be avoided in general. I agree.

What I failed to mention was that you can use non-guard expressions with
this trick. (The trick's actually rewriting the elseifs to nested case.)

When using if with non-guard expressions, you need to evaluate them in
advance, which you may not want if these are side-effectful or just slow:

f() ->
Cond1 = g1(),
Cond2 = g2(),
Cond3 = g3(),
if Cond1 -> a;
Cond2 -> b;
Cond3 -> c;
true -> d
end.

Therefore, you often see nested case expressions instead:

f() ->
case g1() of
true -> a;
false ->
case g2() of
true -> b;
false ->
case g3() of
true -> c;
false -> d
end
end
end.

... or broken down into multiple functions:

f() ->
case g1() of
true -> a;
false -> f2()
end.
f2() ->
%% You know

There's the throw style too, a slightly silly but a flat style:

f() ->
try
g1() andalso throw(a);
g2() andalso throw(b);
g3() andalso throw(c);
d
catch
X -> X
end.

The point of the ?elseif syntax trick is that it lets you write the
nested case above as the flat structure:

f() ->
?'if'(g1()) -> a;
?elseif(g2()) -> b;
?elseif(g3()) -> c;
?else -> d.

You can even bind variables in the conditions if you would want that,
just to avoid deeply nested code. I would look more like perl than
erlang though, so don't do that.

>> That said...
>> The original premise of this thread is based on a misunderstanding of `if` and probably inexperience with `case`.
>
> Agreed
>

I agree too. :-) Thanks for clarifying!

Btw, I'm sorry for posting without having a real question. I just wanted
to show what can be done using a non-trivial parse transform.

Viktor

Peti Gömöri

unread,
Feb 19, 2019, 4:48:41 AM2/19/19
to Viktor Söderqvist, erlang-questions
This makes much more sense now :) Just noting that Elixir has the `cond` expression for this use case.

Dmitry Kolesnikov

unread,
Feb 19, 2019, 4:51:35 AM2/19/19
to Viktor Söderqvist, erlang-questions
Hello Viktor,

You might look into my solution where this concept is generally solved under the abstraction of category pattern.



Best Regards,
Dmitry

Mikael Pettersson

unread,
Feb 19, 2019, 6:24:19 AM2/19/19
to Viktor Söderqvist, erlang-questions
On Tue, Feb 19, 2019 at 8:37 AM Viktor Söderqvist <vik...@zuiderkwast.se> wrote:
> The point of the ?elseif syntax trick is that it lets you write the
> nested case above as the flat structure:
>
> f() ->
> ?'if'(g1()) -> a;
> ?elseif(g2()) -> b;
> ?elseif(g3()) -> c;
> ?else -> d.

So you've reimplemented LISP's COND. Please just do it properly in
the Erlang parser (I belive 'cond' is already a reserved word), and
write an EEP for it.

FWIW, my code tends to be CASE heavy largely to avoid the inherent
problems of Erlang's limited guard expressions and butt-ugly "if".

andrei zavada

unread,
Feb 20, 2019, 2:03:54 AM2/20/19
to erlang-q...@erlang.org
One neat (for some definition of 'neat') trick to do away with `true`
for the semantic `else` is `el/=se` (spotted in riak code).

Raimo Niskanen

unread,
Feb 20, 2019, 6:01:56 AM2/20/19
to erlang-q...@erlang.org
On Mon, Feb 18, 2019 at 08:20:33PM +0200, andrei zavada wrote:
> One neat (for some definition of 'neat') trick to do away with `true`
> for the semantic `else` is `el/=se` (spotted in riak code).
>

Oh dear! That's creative, actually quite readable, and should compile to
literal 'true'.

You can also use a macro: -define(else, true).

Donald Steven

unread,
Feb 20, 2019, 8:17:01 AM2/20/19
to Mikael Pettersson, Erlang
I don't think:

-define(true,else).

yielding ?else ->

is much of an improvement.

Why not just allow the keyword else as a synonym for true when its usage
would improve the readability? (At times, "true" reminds me of trying to
read (SAT-like) sentences like: "Whenever he couldn't think of whether
or not he might have forgotten to turn the light on or off, or not.")

zx...@zxq9.com

unread,
Feb 21, 2019, 12:43:37 AM2/21/19
to erlang-q...@erlang.org
On 2019年2月20日水曜日 8時16分41秒 JST Donald Steven wrote:
> I don't think:
>
> -define(true,else).
>
> yielding ?else ->
>
> is much of an improvement.
>
> Why not just allow the keyword else as a synonym for true when its usage
> would improve the readability? (At times, "true" reminds me of trying to
> read (SAT-like) sentences like: "Whenever he couldn't think of whether
> or not he might have forgotten to turn the light on or off, or not.")

Why not just use `if` where it fits semantically?

These periodic discussions of how to use silly cheats to avoid proper
code layout always puzzle me.

-Craig

Richard O'Keefe

unread,
Feb 21, 2019, 2:29:14 AM2/21/19
to Craig Everett, Erlang Questions
Everyone wants to be a language designer.
If we're lucky, we get Elixir or LFE.
Usually we get a bit of fiddling around
the edges, which is a good thing if it
lets us express something more clearly and
with fewer mistakes than before.  Things
like el/=se just increase the burden on
the reader.  I for one would rather read
    for (;;) { ... }
than
    #define FOREVER for (;;)  in some header
    ...
    FOREVER { ... }

This whole thing with 'if' has simmered along
for years.  There was a proposal for 'cond'.
Perhaps it's time to dust it off and make it
an official part of the language.  Or perhaps
not: there's a message in my mailbox from a
software engineering list with the title "Try programming without 'if' statements" which I
have not read yet...

Seriously, though, when you find yourself
struggling with syntax in Erlang, you probably
need to break your code up into smaller
functions.

zx...@zxq9.com

unread,
Feb 21, 2019, 2:34:23 AM2/21/19
to Erlang Questions
On 2019年2月21日木曜日 20時28分09秒 JST Richard O'Keefe wrote:
> Seriously, though, when you find yourself
> struggling with syntax in Erlang, you probably
> need to break your code up into smaller
> functions.

Worth extracting as its own quote.

Reply all
Reply to author
Forward
0 new messages