[erlang-questions] conditional expressions

84 views
Skip to first unread message

damien morton

unread,
Nov 14, 2008, 7:38:47 PM11/14/08
to erlang-q...@erlang.org
I keep wanting to have python-like boolean expression evaluation, in which a value of nil, 0, an empty list or an empty tuple are considered to be false.

It would be great to have a consise way of saying "the value of this expression is expr1 if expr1 evaluates to a non-false value, or expr2 otherwise".

In python, you would say "X = foo() or bar()", which would evaluate foo(), and if that evaluates to one of None,False,0,[],(),{}, then evaluate bar()

I cant for the life of me figure out what the most concise way of stating that is in erlang.

perhaps
 X = if (T1=foo()) =/= [] -> T1, false -> bar() end

it would nice to be able to say something like
 X = foo() otherwise bar().

or somesuch

Roger Critchlow

unread,
Nov 14, 2008, 8:13:40 PM11/14/08
to damien morton, erlang-q...@erlang.org
I think you're looking for the andalso and orelse operators.  You say:

  X = foo() orelse bar(),

and bar() only gets called if the value of foo() is false.

-- rec --

2008/11/14 damien morton <dmo...@bitfurnace.com>
_______________________________________________
erlang-questions mailing list
erlang-q...@erlang.org
http://www.erlang.org/mailman/listinfo/erlang-questions

damien morton

unread,
Nov 15, 2008, 1:17:52 PM11/15/08
to mats cronqvist, erlang-q...@erlang.org
1> case [] of [] -> X = aaa; X -> ok end.
aaa
2> case bbb of [] -> X = aaa; X -> ok end.
** exception error: no case clause matching bbb

am I missing something?

On Sun, Nov 16, 2008 at 4:42 AM, mats cronqvist <ma...@kreditor.se> wrote:

 strangely, this also works;

case foo() of
 [] -> X = bar();
 X -> ok
end

 as long as X is bound in each clause, you're golden.

 this might qualify as a gotcha.

 mats

mats cronqvist

unread,
Nov 15, 2008, 12:42:53 PM11/15/08
to Richard Carlsson, erlang-q...@erlang.org
Richard Carlsson <rich...@it.uu.se> writes:

>
> X = case foo() of
> [] -> bar();
> X1 -> X1
> end

strangely, this also works;

case foo() of
[] -> X = bar();
X -> ok
end

as long as X is bound in each clause, you're golden.

this might qualify as a gotcha.

mats

Vance Shipley

unread,
Nov 15, 2008, 2:35:22 PM11/15/08
to damien morton, erlang-q...@erlang.org
On Sun, Nov 16, 2008 at 05:17:52AM +1100, damien morton wrote:
} am I missing something?

Just the variable scope. Once you have run:

1> case [] of [] -> X = aaa; X -> ok end.
aaa

The variable X is now bound:

2> X.
aaa

If you forget it first:

3> f(X).
ok

Your second example will work:

4> case bbb of [] -> X = aaa; X -> ok end.
ok


-Vance

Michael McDaniel

unread,
Nov 15, 2008, 1:40:53 PM11/15/08
to erlang-q...@erlang.org
On Sun, Nov 16, 2008 at 05:17:52AM +1100, damien morton wrote:
> 1> case [] of [] -> X = aaa; X -> ok end.
>
> aaa
>
> 2> case bbb of [] -> X = aaa; X -> ok end.
>
> ** exception error: no case clause matching bbb
>
> am I missing something?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

From section '6.8 Case' of the Erlang Reference Manual (Version 5.6.5)

"If there is no matching pattern with a true guard sequence, a case_clause run-time error will occur."

bbb does not match anything in the second case above; you could substitute
aaa for X in the second case and it would result the same.

3> X.
aaa


~Michael


>
> On Sun, Nov 16, 2008 at 4:42 AM, mats cronqvist <[1]ma...@kreditor.se>


> wrote:
>
> strangely, this also works;
> case foo() of
> [] -> X = bar();
> X -> ok
> end
> as long as X is bound in each clause, you're golden.
> this might qualify as a gotcha.
> mats
>

> References
>
> 1. mailto:ma...@kreditor.se

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

--
Michael McDaniel
Portland, Oregon, USA
http://autosys.us

Richard Carlsson

unread,
Nov 15, 2008, 6:49:58 AM11/15/08
to damien morton, erlang-q...@erlang.org
damien morton wrote:
> I keep wanting to have python-like boolean expression evaluation, in
> which a value of nil, 0, an empty list or an empty tuple are considered
> to be false.
>
> It would be great to have a consise way of saying "the value of this
> expression is expr1 if expr1 evaluates to a non-false value, or expr2
> otherwise".
>
> In python, you would say "X = foo() or bar()", which would evaluate
> foo(), and if that evaluates to one of None,False,0,[],(),{}, then
> evaluate bar()

Well, andalso/orelse (as someone suggested) don't work since they
require that expr1 and expr2 evaluate to booleans and nothing else.

> I cant for the life of me figure out what the most concise way of
> stating that is in erlang.
>
> perhaps
> X = if (T1=foo()) =/= [] -> T1, false -> bar() end
>
> it would nice to be able to say something like
> X = foo() otherwise bar().

X = case foo() of


[] -> bar();
X1 -> X1
end

If you need to check for other values as well, replace '[] ->' with
'X when X =:= [] ; X =:= 0 ; ... ->'

But I've always felt that this feature of Python/Perl/... boils down
to sloppy programming style. It basically means that the caller hopes
that the "empty or failure" case is signalled by one of the values
reconized as pseudo-booleans by the language (the programmer might
not actually know the exact interface of the called function, but
guessed that this would work), and the resulting code says nothing to
the reader about the actual set of return values. Furthermore, the
code might do the wrong thing if the function tries to return e.g. '0'
or '{}' on success (as opposed to False or None or whatever it usually
uses for failure). It simply makes the code a lot less tight than it
ought to be. And then, you still can't use the same idiom on abstract
data types to treat e.g. an empty set as "false".

/Richard

Hynek Vychodil

unread,
Nov 15, 2008, 1:37:39 PM11/15/08
to damien morton, erlang-q...@erlang.org, mats cronqvist


2008/11/15 damien morton <dmo...@bitfurnace.com>

1> case [] of [] -> X = aaa; X -> ok end.
aaa
2> case bbb of [] -> X = aaa; X -> ok end.
** exception error: no case clause matching bbb

am I missing something?

Yes, you missed that shell binds X to aaa in previous expression ;-)

But I think, less magic will be

X = case foo() of
      [] -> bar();
      Y -> Y
  end


On Sun, Nov 16, 2008 at 4:42 AM, mats cronqvist <ma...@kreditor.se> wrote:

 strangely, this also works;

case foo() of
 [] -> X = bar();
 X -> ok
end

 as long as X is bound in each clause, you're golden.

 this might qualify as a gotcha.

 mats


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



--
--Hynek (Pichi) Vychodil

damien morton

unread,
Nov 15, 2008, 10:09:43 AM11/15/08
to Richard Carlsson, Erlang Questions


On Sun, Nov 16, 2008 at 1:48 AM, Richard Carlsson <rich...@it.uu.se> wrote:
damien morton wrote:

Still, what strikes me about the erlang libraries is the tremendous variety of techniques used to signal the return of a value or not.

Sometimes nil/Value, sometimes false/Value, sometimes []/[Value], sometimes false/{value,Value}


I've tried to promote {value,X}/none as a standard maybe-type, but it's
hard to do much about all the existing code out there.


For a function that can return 0 or 1 answers, I personally like the []/[Value] approach.

[]/[X] is ok for "searches" as in mnesia, where in some cases you can
get several elements as a result, but as a general mechanism it always
makes a reader think "so, can I ever get more than one value here?", so
it's not good for clarity.

Would {}/{X} be more clear for 0 or 1 answers, and []/[...] for 0 or more answers?

Not too keen on the none/{value,Value} approach - it feels irregular somehow.

Robert Virding

unread,
Nov 15, 2008, 4:42:46 PM11/15/08
to damien morton, erlang-q...@erlang.org, mats cronqvist
You are evaluating these in the shell. In the first case X is bound to aaa, which means that when you evaluate the second case X is already bound. Which means that there is no matching clause. If you do:


1> case [] of [] -> X = aaa; X -> ok end.
aaa
2> case bbb of [] -> Y = aaa; Y -> ok end.
ok
3> {X,Y}.
{aaa,bbb}

it works.

Robert

2008/11/15 damien morton <dmo...@bitfurnace.com>

Fredrik Svahn

unread,
Nov 15, 2008, 4:22:04 PM11/15/08
to damien morton, erlang-q...@erlang.org
X is bound to aaa in the first line you enter in the shell, so your second line is really "case bbb of [] -> aaa = aaa; aaa -> ok end.".

You can unbind/forget X in the shell with f() or f(X), see help().


1> case [] of [] -> X = aaa; X -> ok end.
aaa
2> X.
aaa
3> case bbb of [] -> X = aaa; X -> ok end.

** exception error: no case clause matching bbb
4> X.
aaa
5> f(X).
ok
6> X.  
* 1: variable 'X' is unbound
7> case bbb of [] -> X = aaa; X -> ok end.
ok
8> X.
bbb

2008/11/15 damien morton <dmo...@bitfurnace.com>

damien morton

unread,
Nov 15, 2008, 9:22:03 AM11/15/08
to Erlang Questions


---------- Forwarded message ----------
From: damien morton <dmo...@bitfurnace.com>
Date: Sun, Nov 16, 2008 at 1:21 AM
Subject: Re: [erlang-questions] conditional expressions
To: Richard Carlsson <rich...@it.uu.se>




On Sat, Nov 15, 2008 at 10:49 PM, Richard Carlsson <rich...@it.uu.se> wrote:
damien morton wrote:
...

I cant for the life of me figure out what the most concise way of stating that is in erlang.

perhaps
 X = if (T1=foo()) =/= [] -> T1, false -> bar() end

it would nice to be able to say something like
 X = foo() otherwise bar().

X = case foo() of
     [] -> bar();
     X1 -> X1
   end

If you need to check for other values as well, replace '[] ->' with
'X when X =:= [] ; X =:= 0 ; ... ->'

Ok, that works and is reasonably concise.
 


But I've always felt that this feature of Python/Perl/... boils down
to sloppy programming style. It basically means that the caller hopes
that the "empty or failure" case is signalled by one of the values
reconized as pseudo-booleans by the language (the programmer might
not actually know the exact interface of the called function, but
guessed that this would work), and the resulting code says nothing to
the reader about the actual set of return values. Furthermore, the
code might do the wrong thing if the function tries to return e.g. '0'
or '{}' on success (as opposed to False or None or whatever it usually
uses for failure). It simply makes the code a lot less tight than it
ought to be. And then, you still can't use the same idiom on abstract
data types to treat e.g. an empty set as "false".

Well, Python does have a way of determining if an abstract data type is considered true or false - there's a method the ADT can implement for that. 

Still, what strikes me about the erlang libraries is the tremendous variety of techniques used to signal the return of a value or not.

Sometimes nil/Value, sometimes false/Value, sometimes []/[Value], sometimes false/{value,Value}

Hynek Vychodil

unread,
Nov 16, 2008, 8:19:27 AM11/16/08
to damien morton, Erlang Questions


2008/11/15 damien morton <dmo...@bitfurnace.com>

It happen because there is not pattern matching in python, perl, ruby, ... but you don't need it in erlang since there is pattern matching in erlang.


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



--
--Hynek (Pichi) Vychodil

Richard O'Keefe

unread,
Nov 17, 2008, 10:20:55 PM11/17/08
to damien morton, erlang-q...@erlang.org

On 15 Nov 2008, at 1:38 pm, damien morton wrote:

> I keep wanting to have python-like boolean expression evaluation, in
> which a value of nil, 0, an empty list or an empty tuple are
> considered to be false.

You could do it via a macro that expanded
?PY_OR(e1, e2)
to
case py_val(e1)
of {X} -> X
; false -> e2
end

where
py_val(undef) -> false; % Closer match for Undef than nil is
py_val(0) -> false;
py_val(0.0) -> false;
py_val([]) -> false;
py_val({}) -> false;
py_val(false) -> false;
py_val(X) -> {X}.

However, this really is not very Erlang-like.
(For one thing, it's not very Dialyser-friendly.)

Lisp had a single value used to represent logical falsehood (NIL),
the empty list ('() => NIL), and a three-letter atom ('NIL => NIL).
Scheme found it very useful to separate them (#f, '(), and 'NIL
are all different in Scheme). Erlang is closer to Scheme here.

It may be that you are trying to write Python in Erlang,
which isn't the best way to use Erlang.
It may also be that you don't have (m)any cases where
ALL of the 'false' values listed above are actually possible,
just many cases where one of them is possible and you would
like to use the same operator for all of them. Well, 'case'
is definitely the construct you want.

Can you provide some examples?

Richard O'Keefe

unread,
Nov 17, 2008, 10:24:10 PM11/17/08
to Roger Critchlow, erlang-q...@erlang.org

On 15 Nov 2008, at 2:13 pm, Roger Critchlow wrote:

> I think you're looking for the andalso and orelse operators. You say:
>
> X = foo() orelse bar(),
>
> and bar() only gets called if the value of foo() is false.

However there is a strong restriction in Erlang that does not
exist in Lisp, Scheme, Smalltalk, &c and does not exist in
Python.
False or 42 => 42 in Python
42 or False => 42 in Python
1 == 2 orelse 42 => ERROR! in Erlang
42 orelse 1 == 2 => ERROR! in Erlang

In Erlang, both operands of an andalso or orelse operator
must return 'true' or 'false'. I strongly suspect that the
OP wants a non-empty list or tuple to count as true, rather
than as an error.

Serge Aleynikov

unread,
Nov 17, 2008, 10:38:49 PM11/17/08
to Richard O'Keefe, erlang-q...@erlang.org
Your 'must' statement is not entirely accurate. Consider this:

1 == 2 orelse throw(no_bool_return).

Richard O'Keefe

unread,
Nov 18, 2008, 4:52:24 PM11/18/08
to Serge Aleynikov, erlang-q...@erlang.org

On 18 Nov 2008, at 4:38 pm, Serge Aleynikov wrote:

> Your 'must' statement is not entirely accurate.

Was there any doubt about what I meant?
Let's try again:
both 'andalso' and 'orelse' in Erlang have the
following properties:
the first operand must either return 'true' or 'false'
or raise an exception;
the second operand, if executed, must either return
'true' or 'false' or raise an exception;
the Dialyzer will complain if either operand looks
as if it will return something other than 'false' or 'true';
and above all:
you CANNOT use them to get the same effect as
Lisp's (OR - -) and (AND - -) or Python's similar
operators, which allow a normal result from the
second operand to be anything at all.

I have in fact complained about this, not because I particularly
want non-Boolean results from these operators, but because the
code that's inserted to check makes them non-tail-recursive.

Mikael Pettersson

unread,
Nov 19, 2008, 4:08:32 AM11/19/08
to Richard O'Keefe, erlang-q...@erlang.org
Richard O'Keefe writes:
>
> On 18 Nov 2008, at 4:38 pm, Serge Aleynikov wrote:
>
> > Your 'must' statement is not entirely accurate.
>
> Was there any doubt about what I meant?
> Let's try again:
> both 'andalso' and 'orelse' in Erlang have the
> following properties:
> the first operand must either return 'true' or 'false'
> or raise an exception;
> the second operand, if executed, must either return
> 'true' or 'false' or raise an exception;
> the Dialyzer will complain if either operand looks
> as if it will return something other than 'false' or 'true';
> and above all:
> you CANNOT use them to get the same effect as
> Lisp's (OR - -) and (AND - -) or Python's similar
> operators, which allow a normal result from the
> second operand to be anything at all.
>
> I have in fact complained about this, not because I particularly
> want non-Boolean results from these operators, but because the
> code that's inserted to check makes them non-tail-recursive.

Indeed, the failure of these operators to be properly
tail-recursive makes them utterly useless, and dangerous.

The HiPE compiler once had a performance bug in its
register allocator because some code used an 'andalso'
to control a tail-recursive computation. Needless to say
I've since placed a ban on andalso/orelse in the HiPE code
I maintain.

David Mercer

unread,
Nov 19, 2008, 10:10:09 AM11/19/08
to Mikael Pettersson, Richard O'Keefe, erlang-q...@erlang.org
Can someone please explain the tail recursion problem with andalso? Thanks.

David

> -----Original Message-----
> From: erlang-quest...@erlang.org [mailto:erlang-questions-
> bou...@erlang.org] On Behalf Of Mikael Pettersson
> Sent: Wednesday, November 19, 2008 03:09
> To: Richard O'Keefe
> Cc: erlang-q...@erlang.org
> Subject: Re: [erlang-questions] conditional expressions
>

Richard Carlsson

unread,
Nov 19, 2008, 10:28:24 AM11/19/08
to dme...@alum.mit.edu, erlang-q...@erlang.org
David Mercer wrote:
> Can someone please explain the tail recursion problem with andalso? Thanks.

It checks that the return value of the right hand side is a boolean,
so it must wait for the evaluation to return; it can't just do a tail
call if the left hand side evaluates to true. (The OTP folks thought
that error checking was more important than tail recursion for these
operators.)

/Richard

Bjorn Gustavsson

unread,
Nov 19, 2008, 12:24:46 PM11/19/08
to Richard Carlsson, erlang-q...@erlang.org
On Wed, Nov 19, 2008 at 4:28 PM, Richard Carlsson <rich...@it.uu.se> wrote:
> David Mercer wrote:
>> Can someone please explain the tail recursion problem with andalso? Thanks.
>
> It checks that the return value of the right hand side is a boolean,
> so it must wait for the evaluation to return; it can't just do a tail
> call if the left hand side evaluates to true. (The OTP folks thought
> that error checking was more important than tail recursion for these
> operators.)

Maybe we should reconsider that decision for R13.

I agree with Mikael that andalso/orelse are both useless and dangerous.

/Bjorn
--
Björn Gustavsson, Erlang/OTP, Ericsson AB

David Mercer

unread,
Nov 19, 2008, 12:26:45 PM11/19/08
to Richard Carlsson, dme...@alum.mit.edu, erlang-q...@erlang.org
Richard Carlsson wrote:
> David Mercer wrote:
> > Can someone please explain the tail recursion problem with andalso?
> Thanks.
>
> It checks that the return value of the right hand side is a boolean,
> so it must wait for the evaluation to return; it can't just do a tail
> call if the left hand side evaluates to true. (The OTP folks thought
> that error checking was more important than tail recursion for these
> operators.)

Thanks for the reply, but I still do not understand. If the first operand
evaluates to true the second operand must necessarily be checked, since they
both have to be true in order for the expression to evaluate to true. What
am I missing (or mißing :-) )?

Cheers,

David

Richard Carlsson

unread,
Nov 19, 2008, 1:26:33 PM11/19/08
to dme...@alum.mit.edu, erlang-questions
David Mercer wrote:
> Richard Carlsson wrote:
>> David Mercer wrote:
>>> Can someone please explain the tail recursion problem with andalso?
>> Thanks.
>>
>> It checks that the return value of the right hand side is a boolean,
>> so it must wait for the evaluation to return; it can't just do a tail
>> call if the left hand side evaluates to true. (The OTP folks thought
>> that error checking was more important than tail recursion for these
>> operators.)
>
> Thanks for the reply, but I still do not understand. If the first operand
> evaluates to true the second operand must necessarily be checked, since they
> both have to be true in order for the expression to evaluate to true. What
> am I missing (or mißing :-) )?

If the LHS is true, then it all hangs on the RHS, so you can A) call it
with a tail call (if the result of the andalso will be returned as the
result of the current function) and hope that it evaluates to a boolean
as it ought to, or B) call it, get the return value, check that it is
indeed a boolean, and either return it as it is or throw an exception
if it is not a boolean.

The current implementation does B, which makes it less suitable for
this kind of thing:

all_true([H|T]) -> H andalso all_true(T);
all_true([]) -> true.

which would be a simple traversal if the implementation was A,
but as it is, we have to return down the stack and check that we
are getting neat little booleans all the way. (Yes, type analysis
could sometimes save the day, but far from always.)

On the other hand, B prevents uses such as this:

maybe_goodbye(X) -> X andalso io:format("adieu").

(which returns false if X is false, and otherwise prints
a message and returns ok).

/Richard

Richard O'Keefe

unread,
Nov 19, 2008, 10:01:02 PM11/19/08
to dme...@alum.mit.edu, erlang-q...@erlang.org

On 20 Nov 2008, at 4:10 am, David Mercer wrote:

> Can someone please explain the tail recursion problem with andalso?

SML:

fun all(f, []) = true
| all(f, x::xs) = f x andalso all(f, xs);

The recursive call here is a tail call:
when that recursive call is finished there is nothing
to be done with the result except to return it.

Erlang:

all(_, []) -> true;
all(F, [X|Xs]) -> F(X) andalso all(F, Xs).

The recursive call here is NOT a tail call,
because it is semantically the equivalent of

all(_, []) -> true;
all(F, [X|Xs]) ->
case F(X)
of false -> false
; true -> R = all(F, Xs),
if R == true ; R == false -> R end
end.

Of course SML and Haskell don't need to check because the
type system means that the code that gets past the compiler
couldn't possibly fail such a check. But Lisp and Smalltalk
and Python don't bother making any such check, even though
it _might_ fail if it _were_ done.

Skipping the check on R doesn't seem to be much of a problem
in practice. Loss of tail recursion here IS a problem.

Reply all
Reply to author
Forward
0 new messages