[erlang-questions] How about a new warning? Was: Re: trouble with erlang or erlang is a ghetto

Richard Carlsson carlsson.richard@REDACTED
Fri Aug 5 10:47:29 CEST 2011

On 08/05/2011 04:22 AM, Jeff Schultz wrote:
> The difference between the two cases seems a bit weird to me.  I would
> have expected that the meaning of a call f(Expr1, Expr2) (I'll write
> it as |f(Expr1, Expr2)|) where the Expr are other Erlang expressions
> should be exactly the same as
>      |T1 = Expr1|,
>      |T2 = Expr2|,
>      f(T1, T2)
> where the T are new variables and |.| is applied recursively.
> In this case, |(X = 8) + X| means X = 8, T1 = X, T2 = X, T1 + T2,
> which is okay, and |X + (X = 8)| means T1 = X, X = 8, T2 = X, T1 + T2,
> which is a compile-time error.
> While I appreciate the intent of Barklund's rule above, I don't think
> it plays well with Erlang's explicit left-to-right order of
> evaluation.

I think you have misunderstood something - or maybe I'm missing 
something in your reasoning. Barklund's stated rule means that |(X = 8) 
+ X| is just as invalid in Erlang as |X + (X = 8)|, because even though 
it will work in a left-to-right evaluation order (which is ultimately 
the order in which the arguments _will_ be executed), it will not work 
in _any_ evaluation order. The compiler therefore rejects it.

The nice consequence is that you can always naively reorder the 
arguments of a function or operator call (in a valid program), because 
there's no possibility that one affects the variable bindings expected 
of another.

Hence, the rule actually plays very _well_ with the explicit evaluation 
order: it says that even though it's tempting to think it would be good 
to allow this kind of variable propagation since the evaluation order is 
strictly left-to-right anyway, that's not a good idea, because it 
actually makes programs harder to reason about and harder to refactor 
without introducing bugs.

The general linearization of f(Expr1, Expr2) can be expressed as:

       %% *if* Expr1 and Expr2 don't depend on each other's bindings:
       T1 = Expr1,
       T2 = Expr2,
       %% for all X,Y,... exported from both Expr1 and Expr2:
       X1 = X2, Y1 = Y2, ... % ensure they are the same
       f(T1, T2)

(of course, the check of bindings only happens in extremely rare cases 
like my example f(X=8, X=8), so there's normally no overhead for this).

Of course, side effects in argument expressions are a different matter. 
An expression like f(P ! foo, P ! bar) will _always_ cause foo to arrive 
before bar at P, because the evaluation order is fixed. And naively 
reordering arguments with side effects in them could easily screw up 
your program - which is why having side effects in arguments is bad style.


More information about the erlang-questions mailing list