[erlang-questions] case expression scope

Szoboszlay Dániel dszoboszlay@REDACTED
Wed Mar 5 21:48:05 CET 2014


On Wed, 05 Mar 2014 03:52:22 +0100, Richard A. O'Keefe <ok@REDACTED>  
wrote:

> Wrong.  Yes, list comprehensions *happen* to be implemented
> as calls to out-of-line functions, but the *necessity* for
> list comprehensions to be separate scopes falls directly
> out of their semantics.
>
> Suppose it were otherwise.
>
> 	R = [(Y = f(X), Y) || X <- L].
>
> What value would X have after the comprehension
>  - if L = []?
>  - if L = [1]?
>  - if L = [1,2]?
> What value would Y have?
>
> The key thing is that X and Y get _different_ values
> on each iteration, and Erlang variables being immutable,
> the only way we can make sense of that is if each
> iteration gets its _own_ X and Y.

The key thing is list comprehensions introduce a new scope, thus behave  
different from the rest of the expressions. This is not intuitive, even if  
it is necessary.

> In Classic C and C89, *none* of 'if', 'switch', 'while',
> 'do', and 'for' introduce new scopes.
> In C99, it's clear from 6.8.4 that 'if' and 'switch'
> do *NOT* introduce new scopes and it is clear from
> 6.8.5 that 'while' and 'do' do *NOT* introduce new
> scopes.
>
> In Classic C and C89 the *ONLY* construction that
> introduces a new scope is '{...}'.

I don't want to argue with specs, you are technically correct of course.  
But the mental model is that whenever I introduce a new variable in an  
if-branch, it will go into a new scope. From the compiler's perspective  
it's due to the curly braces. From my perspective it's because I want to  
use that variable in that single block only.

> The thing is that Erlang is what it is
> and is not something else.
> Criticising Erlang for not being like something else
> (especially when it turns out that the something else
> is not in fact like that either)
> is silly.

I'm not criticizing Erlang for being what it is. I love this language the  
way it is already. But I think it could be improved in some areas, and the  
lack of scoping expressions is one such area. Changing the way case works  
would be no doubt a terrible decision. Allowing to write code in the style  
of my examples without breaking backward compatibility would be a useful  
addition for those who prefer this style.

If starting a new scope would be as easy in Erlang as in C, I'd be very  
happy. And why shouldn't it be?

For example consider a new expression: "<- Expression" that has the value  
of Expression but introduces new variables in Expression to a new scope.  
(OK, probably using the left arrow might have some problems, but at least  
it looks very Erlangish). Then I could write:

foo(Dict) ->
     Value = <- case orddict:find(foo, Dict) of
                    {ok, Value} -> Value;
                    error       -> calculate_very_expensive_default_value()
                end,
     ...

(Btw. my problem with "begin ... within ... end" mentioned in a later  
email is that it is way too verbose. Which is of course also very  
Erlangish...)

And once we are there, there are some other nice tricks we could do with  
scopes. Consider this real life example:  
https://github.com/rebar/rebar/blob/master/src/rebar_core.erl#L182-L263 -  
this function performs a lot of sequential tasks and generates a series of  
Config variables in the meantime (Config1, Config2, ...). A typical error  
is that after creating Config123 somewhere later you accidentally use  
Config122 instead. It would be nice if I could "drop" a variable from the  
scope and get a compile error if I'd happen to reuse it later. Like this:

DirSet3 = sets:add_element(Dir, DirSet2),
~DirSet2, % Never ever use DirSet2 again!
...

or even:

DirSet3 = sets:add_element(Dir, ~DirSet2),
...

But I can accept if you think these ideas are worthless as well, only I'd  
love to hear and discuss your arguments.

Cheers,
Daniel



More information about the erlang-questions mailing list