[erlang-questions] Reading, Learning, Confused

Richard A. O'Keefe ok@REDACTED
Tue Jul 22 00:59:41 CEST 2008


On 21 Jul 2008, at 11:46 pm, Hynek Vychodil wrote:
> 1. You can't nest ';', but you can nest orelse. This can be useful  
> for macros and suchlike.

(A) There is no reason whatever why ',' and ';' in guards COULDN'T
     be allowed to nest.  That would have been the sensible thing
     to do, rather than introducing new and confusing operators.

(B) I have yet to see any evidence that writing complicated guards
     is a good idea.  If there are "macros and suchlike" that need
     nested control operators in guards, PLEASE let us see some so
     that we can tell whether there is a better way.
>
> 2. I seem to recall that ';' is implemented by duplicating the  
> clauses, while andalso/orelse is not. If so, andalso/orelse _may_  
> yield somewhat more compact code.

Ah, the old "I seem to recall" trick.
This is something that is easy to discover for sure
by writing a little test case, like adding a clause

	bar(N) when N == a ; N == b ; N == c -> [N];

to a function, then

	compile:file('foo.erl', ['E'])

Nope, "the code after all source code transformations have been
performed" shows no signs of the clause being duplicated,

	compile:file('foo.erl', ['S'])

Nope, this clause turns into

  {label,6}.
     {test,is_ne,{f,7},[{x,0},{atom,a}]}.
     {test,is_ne,{f,7},[{x,0},{atom,b}]}.
     {test,is_eq,{f,8},[{x,0},{atom,c}]}.
   {label,7}.
     {test_heap,2,1}.
     {put_list,{x,0},nil,{x,0}}.
     {'%live',1}.
     return.

with just ONE copy of the clause.

So NO, orelse will *NOT* yield more compact code.  In fact,
why not try the same thing with 'orelse' instead of ';'?
Changing that clause to

	bar(N) when N == a orelse N == b orelse N == c -> [N];

turns into the following code:

   {label,6}.
     {allocate,1,1}.
     {move,{x,0},{y,0}}.
     {test,is_eq,{f,7},[{x,0},{atom,a}]}.
     {move,{atom,true},{x,0}}.
     {jump,{f,9}}.
   {label,7}.
     {test,is_eq,{f,8},[{x,0},{atom,b}]}.
     {move,{atom,true},{x,0}}.
     {jump,{f,9}}.
   {label,8}.
     {bif,'==',{f,10},[{x,0},{atom,c}],{x,0}}.
   {label,9}.
     {test,is_eq_exact,{f,10},[{x,0},{atom,true}]}.
     {test_heap,2,0}.
     {put_list,{y,0},nil,{x,0}}.
     {'%live',1}.
     {deallocate,1}.
     return.

As you see, using 'orelse' results in code that is CONSIDERABLY
LESS COMPACT than code using ';'.  And it is not just less
compact, it executes more instructions.  If compact speedy code
is something you want, then you would have to be off your rocker
to use 'orelse' in a guard.

Note:  this might not be true for all time.
*In a guard*, and when governing tests rather than some random
expressions, 'orelse' should be compiled exactly like ';' and
'andalso' should be compiled exactly like ','.  At the moment
they are not, partly because 'orelse' and 'andalso' were given
seriously flawed semantics that makes them unusable in the
place where I would most want to use them:  a definition like

	member(X, []) -> false;
	member(X, [H|T]) ->
	    X == H orelse member(X, T).

fails to be tail-recursive because the second operand of
'andalso' and 'orelse' is (unlike Lisp, Scheme, Smalltalk, &c)
checked to see if it is 'false' or 'true'.

>
> 3. Code using 'andalso' and 'orelse' can be cut paste copied
> as expression anywhere but ',' and ';' can be used only in
> guards (function, case, if).

This is actually a serious problem with 'andalso' and 'orelse'.
Guards are NOT a kind of expression and cutting a guard then
pasting it as an expression, or cutting an expression and
pasting it as a guard, very often doesn't work.  We are not
so short of ways to shoot ourselves in the feet that we need
more of them.
>
Horses for courses:  when writing Haskell or Clean naturally
I use '||' and '&&' without concern.  In those languages
guards *are* expressions and their logical control flow
operators *are* compatible with tail recursion.




More information about the erlang-questions mailing list