if-elseif-else trick

classic Classic list List threaded Threaded
14 messages Options
Reply | Threaded
Open this post in threaded view
|

if-elseif-else trick

Viktor Söderqvist
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
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions
Reply | Threaded
Open this post in threaded view
|

Re: if-elseif-else trick

Andreas Schultz-3
Hi Viktor,

Viktor Söderqvist <[hidden email]> 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
[hidden email]
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:  [hidden email]
GERMANY                             Web:    http://www.travelping.com

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

_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions
Reply | Threaded
Open this post in threaded view
|

Re: if-elseif-else trick

zxq9-2
On 2019年2月18日月曜日 9時02分11秒 JST Andreas Schultz wrote:

> Hi Viktor,
>
> Viktor Söderqvist <[hidden email]> 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
_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions
Reply | Threaded
Open this post in threaded view
|

Re: if-elseif-else trick

Raimo Niskanen-11
On Mon, Feb 18, 2019 at 06:34:11PM +0900, [hidden email] 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
_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions
Reply | Threaded
Open this post in threaded view
|

Re: if-elseif-else trick

andrei zavada
One neat (for some definition of 'neat') trick to do away with `true`
for the semantic `else` is `el/=se` (spotted in riak code).

On Mon, 18 Feb 2019 at 16:36, Raimo Niskanen
<[hidden email]> wrote:

>
> On Mon, Feb 18, 2019 at 06:34:11PM +0900, [hidden email] 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
> _______________________________________________
> erlang-questions mailing list
> [hidden email]
> http://erlang.org/mailman/listinfo/erlang-questions
_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions
Reply | Threaded
Open this post in threaded view
|

Re: if-elseif-else trick

Viktor Söderqvist
In reply to this post by Raimo Niskanen-11
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
_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions
Reply | Threaded
Open this post in threaded view
|

Re: if-elseif-else trick

Peti Gömöri-2
This makes much more sense now :) Just noting that Elixir has the `cond` expression for this use case.

On Tue, Feb 19, 2019 at 8:37 AM Viktor Söderqvist <[hidden email]> wrote:
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
_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions

_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions
Reply | Threaded
Open this post in threaded view
|

Re: if-elseif-else trick

dmkolesnikov
Hello Viktor,

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



Best Regards,
Dmitry


On 19 Feb 2019, at 11.48, Peti Gömöri <[hidden email]> wrote:

This makes much more sense now :) Just noting that Elixir has the `cond` expression for this use case.

On Tue, Feb 19, 2019 at 8:37 AM Viktor Söderqvist <[hidden email]> wrote:
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
_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions
_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions


_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions
Reply | Threaded
Open this post in threaded view
|

Re: if-elseif-else trick

Mikael Pettersson-5
In reply to this post by Viktor Söderqvist
On Tue, Feb 19, 2019 at 8:37 AM Viktor Söderqvist <[hidden email]> 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".
_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions
Reply | Threaded
Open this post in threaded view
|

Re: if-elseif-else trick

Raimo Niskanen-11
In reply to this post by andrei zavada
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).
--

/ Raimo Niskanen, Erlang/OTP, Ericsson AB
_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions
Reply | Threaded
Open this post in threaded view
|

Re: if-elseif-else trick

Donald Steven
In reply to this post by Mikael Pettersson-5
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.")

On 2/19/2019 6.23 AM, Mikael Pettersson wrote:

> On Tue, Feb 19, 2019 at 8:37 AM Viktor Söderqvist <[hidden email]> 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".
> _______________________________________________
> erlang-questions mailing list
> [hidden email]
> http://erlang.org/mailman/listinfo/erlang-questions

_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions
Reply | Threaded
Open this post in threaded view
|

Re: if-elseif-else trick

zxq9-2
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
_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions
Reply | Threaded
Open this post in threaded view
|

Re: if-elseif-else trick

Richard O'Keefe
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.


On Thu, 21 Feb 2019 at 18:43, <[hidden email]> wrote:
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
_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions

_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions
Reply | Threaded
Open this post in threaded view
|

Re: if-elseif-else trick

zxq9-2
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.

-Craig
_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions