cond revisited

Thomas Lindgren <>
Mon Oct 10 13:25:16 CEST 2005

The current proposal for cond has a variable binding
rule I have found unsatisfying:

in the following pseudo-code

cond Expr1 -> Body1; Rest end

this is translated basically into a nested case:

case Exprs1 of
   true -> Body1;
   false -> <rest of code>

But this means the bindings made in Exprs1 are seen
not only in Body but also in Rest (ie, all subsequent
clauses). I find this unintuitive, so I would like to
propose a translation of cond into case which avoids
this problem*. Here we go.

Assume that we have

cond Exprs -> Body; Rest_clauses end

1. We first compute the variables exported from Exprs
into Body (and afterwards) as X1,...,Xn.

2. We then take advantage of a property of fun:s, that
they do not export variable bindings at all. So we
can write

(fun() -> Exprs end)()

which evaluates Exprs without exporting any variable
bindings. Note that the extra (fun ..)() call easily
can be removed at compile time, so there is no extra

If we were content with not exporting variables from
Exprs to Body at all, we could stop with that. But
Richard Carlsson has convinced me that this was not
enough. If we DID stop, we would simply get

   case (fun() -> Exprs end)() of 
      true -> Body; 

but to get the variable bindings out, we need to do a
bit more.

3. Since we want X1,...,Xn to be visible in Body, we
then ensure that they are passed correctly.

Let CondGuard be the code:

(fun() ->
   case Exprs of
      true -> {true, X1,...,Xn};
      false -> false

The above expression is then wrapped in the following:

case CondGuard of
   {true, X1,....,Xn} -> Body;
   false -> <code for Rest_clauses>

This ensures that the variables X1,...,Xn are seen
only in Body. The final clause of the cond can look

case ...
  false -> exit(cond_clause)

That's all. The translation is a bit more involved
than the one currently used, and the compiler will
have to work a bit harder. But note that this can
compile to *quite efficient* code after some
simplifications have been done.

A barebones example:

   X = f(A), Y = g(B), X =< Y -> h(X,Y);
   X = f(A,B), Y = g(A,B), X > Y -> i(X,Y)

becomes (omitting begin ... end pairs)

case (fun() -> case X = f(A), Y = g(B), X =< Y of
                   true -> {true,X,Y};
                   false -> false
     )() of
   {true, X, Y} -> h(X,Y);
   false ->
      case (fun() -> 
             case X = f(A,B), Y = g(A,B), X > Y of
                true ->
                   {true, X, Y};
                false ->
             end)() of
         {true, X, Y} -> i(X,Y);
         false -> exit(cond_clause)

which can quickly be simplified by beta reduction and
case-of-case optimization into

case X1 = f(A), Y1 = g(B), X1 =< Y1 of
   true ->
     {true, X, Y} = {true, X1, Y1}, h(X,Y);
   false ->
      case X2 = f(A,B), Y2 = g(A,B), X2 > Y2 of
         true ->
            {true, X, Y} = {true, X2, Y2}, i(X,Y);
         false ->

The compiler then simplifies the matching of
{true,...} and eliminates the copies. You end up with
basically the case-statement you would have written by
hand. And there you are.


Yahoo! Mail - PC Magazine Editors' Choice 2005

More information about the erlang-questions mailing list