[erlang-questions] The If expression

67 views
Skip to first unread message

Henning Diedrich

unread,
Apr 21, 2010, 7:10:14 PM4/21/10
to Erlang Questions
Did anything come from the discussion about the if expression (
http://www.erlang.org/pipermail/erlang-questions/2009-January/040808.html )?

I remember reading that one-branched ifs shouldn't be necessary. And
that if they are needed it's a sign of bad structure / code in the first
place. I'm a serial offender.

Is there a specific tip on how to think differently when one runs into
frequently using one-armed ifs? I.e., writing a lot of true -> nil for
the empty second branch?

Thanks!
Henning

--
You received this message because you are subscribed to the Google Groups "Erlang Programming" group.
To post to this group, send email to erlang-pr...@googlegroups.com.
To unsubscribe from this group, send email to erlang-programm...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/erlang-programming?hl=en.

Richard O'Keefe

unread,
Apr 21, 2010, 7:24:41 PM4/21/10
to Henning Diedrich, Erlang Questions

On Apr 22, 2010, at 11:10 AM, Henning Diedrich wrote:

> Did anything come from the discussion about the if expression ( http://www.erlang.org/pipermail/erlang-questions/2009-January/040808.html
> )?

(1) The "undocumented?" comment in that article is just plain wrong.
This is a property of all guards and is documented.

(2) As for
if Expression -> Body else -> ElseBody end,

(A) It is just _too_ confusing to have 'if <guard>' and
'if <expression>' in the same language.

(B) General Erlang "style" for 'else' is '; true ->'

(C) It has been argued at great length over many years in the
Software Engineering community that 'boolean' is almost always
the wrong type to use. In too many situations, it is not clear
which convention is followed: does 'true' mean connected or
disconnected? In too many other situations, there are more
than
two possibilities. In this mailing list, the latter case has
very often been substantiated.

>
> I remember reading that one-branched ifs shouldn't be necessary.

(3) It's not that they SHOULDN'T be necessary.
It's not even the obvious fact that they AREN'T necessary.
The point is (2,C) that they are often WRONG.

> And that if they are needed it's a sign of bad structure / code in
> the first place.

Not "if they are NEEDED", because they never are.
"If they are USED".

>
> Is there a specific tip on how to think differently when one runs
> into frequently using one-armed ifs? I.e., writing a lot of true ->
> nil for the empty second branch?

For one thing, this is a clear sign of writing imperative code.
Pure functional languages like Haskell and Clean don't have one-armed
ifs because every expression has to have a value, and both arms of an
if have to have values of the same type.

The best thing to do is to show us some code and invite us to refactor
it.



________________________________________________________________
erlang-questions (at) erlang.org mailing list.
See http://www.erlang.org/faq.html
To unsubscribe; mailto:erlang-questio...@erlang.org

David N Murray

unread,
Apr 21, 2010, 7:48:13 PM4/21/10
to erlang-q...@erlang.org
On Apr 22, Richard O'Keefe scribed:

>
>
> The best thing to do is to show us some code and invite us to refactor
> it.
>

I'm coming from an imperative background and constantly run into one-armed
ifs, or more usually, one-armed cases:

init([]) ->
case cfg:get(ibr_connect) of
ok -> gen_server:cast(self(), connect);
_ -> ok
end,
{ok, {init, [], []}}. % State, MsgId, Buf

When this gen_server is started, I have other housekeeping to do before it
can actually start doing it's thing, and the supervisor casts a connect
message when it's ok to connect. When I process the connect in this
server, I store {ibr_connect, ok} in the cfg module. This is my
breadcrumb that I've been here before and everything is good to go. If
the gen_server gets restarted, I want it to connect right away and not
wait for someone to tell it to (automatic reconnect logic, the module is a
tcp client to another server).

Usually, my tests are "has this happened, or is this a that" and I really
don't care about the "false" side of that test. I'm having a hard time
getting my head around this.

TIA,
Dave

Henning Diedrich

unread,
Apr 21, 2010, 9:08:53 PM4/21/10
to Richard O'Keefe, Erlang Questions
Hi Richard,

thanks a lot for the lightning-speed & sorrow reply!

Please be gentle, my eyes are sore already :-D

I also realize I was somewhat hysteric, maybe.

Looking for examples now, I found 5 true->nil in a thousands lines. So
maybe that absolves a bit of my guilt of doing imperative code. No
matter, the question remains as sincere as before. That was exactly what
I wanted to address and what the 5 places - given below - totally look like.
>
>> Did anything come from the discussion about the if expression (
>> http://www.erlang.org/pipermail/erlang-questions/2009-January/040808.html )?
>>
>
> (1) The "undocumented?" comment in that article is just plain wrong.
I meant to basically give the head of that related thread.
> (B) General Erlang "style" for 'else' is '; true ->'
It's not hard to get used to. I wasn't going to complain about that
oddity actually, but wondered what to do different, to not have to write
it 'empty' now and then.

Regarding 'have', see below please.
> (C) It has been argued at great length over many years in the
> Software Engineering community that 'boolean' is almost always
> the wrong type to use. In too many situations, it is not clear
> which convention is followed: does 'true' mean connected or
> disconnected? In too many other situations, there are more than
> two possibilities. In this mailing list, the latter case has
> very often been substantiated.
I think it's exactly the (few) cases where 'yes or no' is what matters,
see samples at bottom, thanks.

The rest of the paragraph, I won't dispute. AND I'd happily avoid the
(few) cases left.
>> I remember reading that one-branched ifs shouldn't be necessary.
> (3) It's not that they SHOULDN'T be necessary.
> It's not even the obvious fact that they AREN'T necessary.
> The point is (2,C) that they are often WRONG.
Right, it sure looks like they /are/ necessary, to me. Which /is/ why I
started wondering if I am doing sth. wrong here.
>>
>> Is there a specific tip on how to think differently when one runs
>> into frequently using one-armed ifs? I.e., writing a lot of true ->
>> nil for the empty second branch?
>
> For one thing, this is a clear sign of writing imperative code.
Exactly, that's why I am asking about /thinking/ differently.
> Pure functional languages like Haskell and Clean don't have one-armed
> ifs because every expression has to have a value, and both arms of an
> if have to have values of the same type.
It's funny you say that because it seems like /all/ spots where I ran
into this issue are one of

* output
* message sending
* global names registration

... David's example adds

* creating a connection

... also an external state issue.

All samples seem to be about /creating a side effect/ --- or, under
certain conditions, not creating it.

Is that a legitimate opening for a one-branched if? Exactly for the less
functionally-clean stuff, on Erlang's more practical leanings?

As I said, the rest of the 1000 lines, with ~15 more ifs, didn't need
empty 'else' clauses. Way rare enough to be a non-issue as matter of
style, typing or readability. But for the principle.
>
> The best thing to do is to show us some code and invite us to refactor
> it.
Thanks a lot for the offer! With a tad of context. Sorry if there should
be a typo, I sanitized them to make them more concise.

(1)

print ->
if
% suppress printing the default suite when unused
>>> (Nom == "Default") and (Passed+Failed+Crashed == 0) ->
>>> nil;

% print all other
true ->
echo("~s~n~s ~s - Tests: ~p, Passed: ~p,
Failed: ~p, Crashes: ~p~n~s",
[Line, Nom, Verdict, Passed+Failed,
Passed, Failed, Crashed, Line])
end,

main ! { printed, self() },

suite_loop(Nom, Caller, Sub, Passed, Failed, Crashed);

(2)


verbose_echo(Verbose, Format, Para) ->

% Don't print if verbosity is off (false).
>>> if Verbose -> echo(Format, Para); true -> nil end.

(3)


% If exists, drop old before adding new element
Prev = gl:get(suite, Suite),
>>> if Prev /= undefined -> gl:drop(suite, Suite); true ->
nil end,
gl:add(suite, Suite),

(4)


glist() ->

spawn(fun() -> glist_loop([], nil, nil) end).

glist_loop(List, PrevCaller, Return) ->

% call back to originally caller, to deliver result.
% (so: not on very first call, if no Caller is given, or it
died (?) in the meantime.
if
is_pid(PrevCaller) ->
vecho(?D4, "call ~p ~p", [PrevCaller, Return]),
PrevCaller ! Return;
>>> true -> nil
end,

receive
{ add, Element, Caller } -> glist_loop([Element | List],
Caller, ok);
{ drop, Key, Caller } ->
glist_loop(lists:keydelete(Key,1,List), Caller, ok);
{ get, Key, Caller } -> glist_loop(List, Caller,
lists:keyfind(Key,1,List));
E -> throw(E)
end.

(5)


safe_unregister(Name) ->

Registered = whereis(Name) /= undefined,

>>> if Registered -> unregister(Name); true -> nil end.


Thanks you very much for looking at this!

Henning

Robert Virding

unread,
Apr 21, 2010, 9:39:12 PM4/21/10
to Henning Diedrich, Richard O'Keefe, Erlang Questions
On 22 April 2010 03:08, Henning Diedrich <hd2...@eonblast.com> wrote:
>>
>> Pure functional languages like Haskell and Clean don't have one-armed
>> ifs because every expression has to have a value, and both arms of an
>> if have to have values of the same type.
>
> It's funny you say that because it seems like /all/ spots where I ran into
> this issue are one of
>
>   * output
>   * message sending
>   * global names registration
>
> ... David's example adds
>
>   * creating a connection
>
> ... also an external state issue.
>
> All samples seem to be about /creating a side effect/ ---  or, under certain
> conditions, not creating it.
>
> Is that a legitimate opening for a one-branched if? Exactly for the less
> functionally-clean stuff, on Erlang's more practical leanings?

The problem is that however you choose to use 'if', or any other
construction for that matter, Erlang is a functional language and
everything returns, and must return, a value. So even if 'if' didn't
need to have an explicit default or else case then it would still have
to return a value. We would never be able agree on a default value
anyway. :-) 'nil' is not a good value here as it has no inherent
meaning in Erlang as it does in Lisp.

It would also mean that 'if' would not behave consistently with 'case'
and function clauses which generate an error if there is no matching
clause. This I think would be a serious mistake.

Robert

________________________________________________________________
erlang-questions (at) erlang.org mailing list.
See http://www.erlang.org/faq.html
To unsubscribe; mailto:erlang-questio...@erlang.org

Jay Nelson

unread,
Apr 22, 2010, 12:59:46 AM4/22/10
to Erlang Questions
Henning Deidrich offered one-armed ifs to rewrite:

I never use the if statement. It doesn't even occur to me to use it,
although writing Java or something else I use it all the time
naturally. Here's the style I would use for some of your cases (some
of the others may not have enough context to be good examples for
rewriting):

> (2)
> verbose_echo(Verbose, Format, Para) ->
>
> % Don't print if verbosity is off (false).
> >>> if Verbose -> echo(Format, Para); true -> nil end.

verbose_echo(true, Format, Para) ->
echo(Format, Para);
verbose_echo(_, _Format, _Para) ->
nil.

It doesn't do what the comment says, and you have to consider whether
nil is the value you want back. It depends on what echo/2 returns as
to what is appropriate (and whether the caller even cares about the
return value). With this approach it is easier to test and easier to
add new cases (verbosity levels) or change the existing one.


> (3)
> % If exists, drop old before adding new element
> Prev = gl:get(suite, Suite),
> >>> if Prev /= undefined -> gl:drop(suite, Suite); true ->
> nil end,
> gl:add(suite, Suite),

Prev = case gl:get(suite, Suite) ->
undefined -> nil;
Defined -> gl:drop(suite, Suite), Defined
end,
gl:add(suite, Suite),


Here I made the assumption that you needed the value of Prev later.
If you don't, you can drop it out and not care what the return value
of the case statement is.

This is actually a common code smell to watch for. You are setting a
variable, then testing for a negative value using /= constant.
Instead, use case and make the first clause the constant you want to
exclude, everything else will match the catch-all clause.

Even when I have case ... of true -> ...; false -> ... end I find
it more comforting in erlang to know I covered the cases I care
about. You can even use a single armed case if you want a freak
value to crash your process.

> (5)
> safe_unregister(Name) ->
> Registered = whereis(Name) /= undefined,
> >>> if Registered -> unregister(Name); true -> nil end.

safe_unregister(Name) ->
case whereis(Name) of
undefined -> ok;
Other -> unregister(Name), ok
end.

Another instance of #3. You can easily fix the return values to
different ones, or have a return value after the case ... end. If
you need to know the value of whereis, you can modify it to this:

safe_unregister(Name) ->
Loc = case whereis(Name) of
undefined -> undefined;
Other -> unregister(Name), Other
end,
... computations involving Loc ...
%% or even return it...
Loc.


Any time you find yourself binding a variable, and then soon after
wanting to modify it or do an if based on a small set of values,
stop, back up and try using a case statement. It has the advantage
of giving you multiple variables with delayed binding of the correct
one as your chosen value:

Chosen = case multi_var_fun() of
Val1 -> Val1;
Val2 -> f(Val2);
Val3 when Val3 > 0, Val3 < 5 -> g(Val3);
Other -> h(Other)
end,

Here Chosen is a late-bound variable with several values visited as
intermediate bindings before arriving at a final choice. An
imperative programmer tries to do something like:

Chosen = multi_var_fun(),
if (Chosen == Val1) ...
else ...

The erlang way is to avoid binding the variable you care about until
you are absolutely sure you have the _value_ you care about, rather
than modifying the value along the way. A one-armed if is
semantically like saying I want Val1 most of the time, and in a few
cases I want to change it to a different value.

jay

Jay Nelson

unread,
Apr 22, 2010, 1:06:31 AM4/22/10
to Erlang Questions
Henning Deidrich offered one-armed ifs to rewrite:

> (4)
> glist() ->
> spawn(fun() -> glist_loop([], nil, nil) end).
>
> glist_loop(List, PrevCaller, Return) ->
>
> % call back to originally caller, to deliver result.
> % (so: not on very first call, if no Caller is given,
or it
> % died (?) in the meantime.
> if
> is_pid(PrevCaller) ->
> vecho(?D4, "call ~p ~p", [PrevCaller, Return]),
> PrevCaller ! Return;
> >>> true -> nil
> end,
>
> receive
> { add, Element, Caller } -> glist_loop([Element |
List],
> Caller, ok);
> { drop, Key, Caller } ->
> glist_loop(lists:keydelete(Key,1,List), Caller, ok);
> { get, Key, Caller } -> glist_loop(List, Caller,
> lists:keyfind(Key,1,List));
> E -> throw(E)
> end.


glist_loop(List, PrevCaller, Return) when is_pid(PrevCaller) ->
vecho(?D4, ...),
PrevCaller ! Return,
glist_response(List, PrevCaller, Return);
glist_loop(_List, PrevCaller, _Return) ->
{error, {not_a_pid, PrevCaller}}.

Your drop through branch looks like it gets caught in a receive
statement with no timeout, here I explicitly return an error value.
You could choose to do something different.

glist_response(List, PrevCaller, Return) ->
receive
... same as above ...
end.


Sometimes factoring out a different function gives you the pattern
match that you need to break out the cases. Don't forget that when
clauses can be as useful as argument values to differentiate the cases.

Richard O'Keefe

unread,
Apr 22, 2010, 1:56:59 AM4/22/10
to Jay Nelson, Erlang Questions

On Apr 22, 2010, at 4:59 PM, Jay Nelson wrote:

> Henning Deidrich offered one-armed ifs to rewrite:

[I haven't seen that one yet]

> > (2)
> > verbose_echo(Verbose, Format, Para) ->
> >
> > % Don't print if verbosity is off (false).
> > >>> if Verbose -> echo(Format, Para); true -> nil end.
>
> verbose_echo(true, Format, Para) ->
> echo(Format, Para);
> verbose_echo(_, _Format, _Para) ->
> nil.

This is a TEXTBOOK example of where using Boolean is wrong.
It is normal to have more than two levels of verbosity.
syslog has
Emergency (level 0)
Alert (level 1)
Critical (level 2)
Error (level 3)
Warning (level 4)
Notice (level 5)
Info (level 6)
Debug (level 7)

Which subset of these corresponds to 'true' and which to 'false'?
If you aren't using all of the syslog levels now, you will very
likely be using more than two of them in the not too distant future.

There is also a classic trap: you talk about one of the verbosity
levels being "off". Sooner or later, someone who reads about verbosity
being "off" will try verbose_echo(off, Format, Para) or perhaps
verbose_echo(on, Format, Para). So why not use 'off' and 'on' as the
levels in the first place? Better still, why not use level names like
'verbose' and 'laconic'? And in the spirit of making broken code crash
it would be even better to write

verbose_echo(verbose, Format, Para) ->
echo(Format, Para);
verbose_echo(laconic, _Format, _Para) ->
nil.

James Aimonetti

unread,
Apr 22, 2010, 10:28:03 AM4/22/10
to Jay Nelson, Erlang Questions
This thread is well-timed as I have an issue I wanted some help on.

I have two lists, one of keys and one of values. I want to zip them
together, but I have one specific key/value combo that I want to exclude
from the zipped list. For context, this is creating an appropriate
{proplist} for couchbeam.

to_couchbeam(Fields, Values) ->
{ lists:flatten( lists:zipwith(fun to_couchbeam_zipper/2, Fields,
Values) ) }.

to_couchbeam_zipper(F, V) ->
K = to_couchbeam_key(F),
Val = to_couchbeam_value(V),
case Key == <<"_rev">> andalso Val == undefined of
true -> [];
false -> {K, Val}
end.

to_couchbeam_* converts various terms to formats suitable for couchbeam
to save the document. I only want to remove the '_rev' key when it's
value is undefined, otherwise couchbeam fails to save the document. If
'_rev' has a defined value, great, keep it in the generated proplist.

I refactored according to some of the advice earlier, so now
to_couchbeam_zipper is

to_couchbeam_zipper('_rev', undefined) -> [];
to_couchbeam_zipper(F, V) -> { to_couchbeam_key(F), to_couchbeam_value(V) }.

So I may have answered my own question here, as concerns the case
statement. Is there a cleaner way to zip two lists in this fashion
without resorting to inserting an empty list for unwanted k/v pairs and
flattening the resulting proplist?

Thanks,

James
James Aimonetti
mobile: 314.809.6307
work: 540.459.2220
email: james.a...@gmail.com
website: http://jamesaimonetti.com

Anders Dahlin

unread,
Apr 22, 2010, 10:54:21 AM4/22/10
to erlang-q...@erlang.org
Alt using lists:foldl/3

to_couchbeam(Fields, Values) ->
Fun = fun ('_rev', {[undefined| Vs], Acc}) ->
{Vs, Acc};
(F, {[V| Vs], Acc}) ->
{Vs, Acc ++ [{to_couchbeam_key(F),
to_couchbeam_value(V)}]}
end,
{_, Res} = lists:foldl(Fun, {Values, []}, Fields),
Res.


Alt using functions:

to_couchbeam(Fields, Values) ->
to_couchbeam(Fields, Values, []).

to_couchbeam([], [], Acc) ->
Acc;
to_couchbeam(['_rev'| Fields], [undefined| Values], Acc) ->
to_couchbeam(Fields, Values, Acc);
to_couchbeam([F| Fields], [V| Values], Acc) ->
to_couchbeam(Fields, Values,
Acc ++ [{to_couchbeam_key(F), to_couchbeam_value(V)}]).


Completely untested...

Jay Nelson

unread,
Apr 22, 2010, 1:25:02 PM4/22/10
to Erlang Questions, Richard O'Keefe
>> > (2)
>> > verbose_echo(Verbose, Format, Para) ->
>> >
>> > % Don't print if verbosity is off (false).
>> > >>> if Verbose -> echo(Format, Para); true -> nil end.
>>
>> verbose_echo(true, Format, Para) ->
>> echo(Format, Para);
>> verbose_echo(_, _Format, _Para) ->
>> nil.
>
> This is a TEXTBOOK example of where using Boolean is wrong.

When using the 'if' statement, the issue wasn't quite so apparent.

Using the function clause approach it sticks you in the eye, because
you have to write a function that matches something (_ instead of
true) and returns something. Your hand hovers over the keyboard
wondering what to type.

Another code smell is a two branched function with true and _:

foo(true) -> ...;
foo(_) -> ...

Why _? Why not just let it crash? If there is only one value, why
is it true?

jay

Richard O'Keefe

unread,
Apr 22, 2010, 6:14:40 PM4/22/10
to James Aimonetti, Jay Nelson, Erlang Questions

On Apr 23, 2010, at 2:28 AM, James Aimonetti wrote:

> This thread is well-timed as I have an issue I wanted some help on.
>
> I have two lists, one of keys and one of values. I want to zip them
> together, but I have one specific key/value combo that I want to
> exclude from the zipped list. For context, this is creating an
> appropriate {proplist} for couchbeam.
>
> to_couchbeam(Fields, Values) ->
> { lists:flatten( lists:zipwith(fun to_couchbeam_zipper/2, Fields,
> Values) ) }.
>
> to_couchbeam_zipper(F, V) ->
> K = to_couchbeam_key(F),
> Val = to_couchbeam_value(V),
> case Key == <<"_rev">> andalso Val == undefined of
> true -> [];
> false -> {K, Val}
> end.

to_couchbeam([], []) ->
[];
to_couchbeam([Field|Fields], [Value|Values]) ->
Key = to_couchbeam_key(Field),
Val = to_couchbeam_value(Value),
Tail = to_couchbeam(Fields, Values)
case {Key,Val}
of {<<"_rev">>, undefined} -> Tail
; Pair -> [Pair|Tail]
end.

What have 'true' or 'false' to do with it?
It's <<"_rev">> and 'undefined' you want to watch out
for, so it's they that belong in the case.
Why build a list including stuff you don't want,
and then strip it out?

What we really want here is EEP 19
(http://www.erlang.org/eeps/eep-0019.html)

to_couchbeam(Fields, Values) ->
[Pair || F <- Fields && V <- Values,
Pair = {to_couchbeam_key(F), to_couchbeam_value(V)},
Pair =/= {<<"_rev">>, undefined}].

(THAT IS NOT LEGAL ERLANG RIGHT NOW.)

If you are willing to build a list and then squish out the
element you don't want, then

to_couchbeam(Fields, Values) ->
P = fun (K,V) -> {to_couchbeam_key(K), to_couchbeam_value(V)} end,
lists:delete({<<"_rev">>,undefined}, lists:zipwith(P, Fields,
Values)).

seems like the best that can be done.

James Aimonetti

unread,
Apr 22, 2010, 7:25:16 PM4/22/10
to Richard O'Keefe, Jay Nelson, Erlang Questions
Thanks Richard, the feedback is much appreciated. I have a couple
different places where your multi-generator syntax would indeed be
useful.

James

Henning Diedrich

unread,
May 27, 2010, 9:20:40 PM5/27/10
to Erlang Questions, Robert Virding
I think I found my one-armed-if at long last!

Shame on everybody standing by who knew it all along and chuckled with glee:

Index /= null orelse throw(index_null).


*love it* *beautiful* *ouch* *ey! stop that!*

I am still trying hard to avoid the need for a one armed if as per the
reasons brought up in this thread.

Many times I succeed and the extra effort pays off in a yet better
understanding of the FP ways.

However, whenever the desire is for more informative error messages ---
over 'just let it crash' --- anything else clutters the code and
one-armed ifs *are* the right thing to use there because I don't care
for all the other cases. In such cases I am not interested in what value
may be returned because I will break away anyway.

And better error messages are a legitimate constraint sometimes. I
understand it's not in Erlang's heritage in some way.

Message passing and IO are other areas where side effects are the focus,
not return values. And where therefore one-armed ifs could be a honest
thing.

From all the help, which I want to say *thank you* for, the following
had been the strongest advice that stuck with me in its simplicity and
fair abstraction:

Robert Virding wrote:
> ... however you choose to use 'if', or any other


> construction for that matter, Erlang is a functional language and
> everything returns, and must return, a value.

Thanks, Robert, this reminder helped to get into the right thinking
often times!


> So even if 'if' didn't
> need to have an explicit default or else case then it would still have
> to return a value. We would never be able agree on a default value
> anyway. :-) 'nil' is not a good value here as it has no inherent
> meaning in Erlang as it does in Lisp.
>

It probably held true 80% of the times when I ran into a subjective (<-
qualifier inserted for Rich!) need of a one-armed-if.

But whenever exceptions come into play, the return value argument can
become mute I found, in FP. As suggested by the fact that the result of
the after-body of try-catch-after constructs are discarded as well.
Currently I'd argue that in an analogous way the second arm of 'case' or
'if' is not necessarily-necessary when the only arm that matters is
ending on throw/erlang:error/exit. If I wisen up, I promise I'll retract.

I did find very many occasions where instead of a 'case', the correct
pattern matching in the function head made all the difference, or a
'case' structure could with some deliberation be rewritten to have a
sensical second arm.

But especially when I need to throw an error /from/ a place 'higher up'
in the call stack to put more information with it, I couldn't always
find a sensible way to use the second arm of and 'if' or 'case'. (Re
throwing from higher up: e.g. a list may have been consumed in its
entirety 'lower down' when I realize a dead end is hit. And so I can't
show it in the error message if it's thrown from there. Unless I add a
parameter.)

Thank you again for all your thoughts, they are very much appreciated.

Henning

Richard O'Keefe

unread,
May 28, 2010, 1:16:03 AM5/28/10
to Erlang Questions, Henning Diedrich
On May 28, 2010, at 1:20 PM, Henning Diedrich wrote:

> I think I found my one-armed-if at long last!
>
> Shame on everybody standing by who knew it all along and chuckled
> with glee:
>
> Index /= null orelse throw(index_null).

Sounds like you've rediscovered Perl's

<condition> || die <message>;

idiom. It's generally recommended in Erlang to use 'error'
for errors, not 'throw'.

It's not really clear to me how much of an improvement this
is over
if <guard> -> ok end

>
> But whenever exceptions come into play, the return value argument
> can become mute I found, in FP.

I think you mean "moot" (mOOt, subject to debate)
not "mute" (mEWt, silent).

> As suggested by the fact that the result of the after-body of try-
> catch-after constructs are discarded as well. Currently I'd argue
> that in an analogous way the second arm of 'case' or 'if' is not
> necessarily-necessary when the only arm that matters is ending on
> throw/erlang:error/exit. If I wisen up, I promise I'll retract.

I don't think anybody was arguing against

if <all is well> -> <good result> end

when the *intention* is that the process should crash/raise an exception
on failure. Indeed, no second arm is required in this case. But then,
the programmer's intentions are much more clearly stated if you write

?define(ASSERT(Guard), if Guard -> ok end).
...
?ASSERT(<all is well>),
<good result>

> But especially when I need to throw an error /from/ a place 'higher
> up' in the call stack to put more information with it, I couldn't
> always find a sensible way to use the second arm of and 'if' or
> 'case'.

Give an example.

With exception handling there are at least two separate issues:
- unwind-protect; ensuring that actions are properly undone when
an exception occurs
- recovery and resumption.
The big issue about exception handling is that the exception has
to be *recognised*, *understood*, and *recovered from*.
One would have to see the context, but things like 'index_null' have
a nasty habit of referring to data other than stuff the original
caller passed in, and might not even be able to name.

It really is important to see a real example here.

asso

John Hughes

unread,
May 28, 2010, 6:06:55 AM5/28/10
to Henning Diedrich, Erlang Questions, Robert Virding
I use a list comprehension when I need a one armed if with a side
effect.

[io:format("Values: ~p\n",[Vs]) || Vs/=[]]

Looks funny at first but you soon get used to it.

John

Sent from my iPhone

________________________________________________________________

Reply all
Reply to author
Forward
0 new messages