[erlang-questions] variable exported from case in OTP 18

Richard A. O'Keefe <>
Thu Oct 1 08:28:23 CEST 2015


On 30/09/2015, at 12:42 am, zxq9 <> wrote:
> 
> The reason this has been made into a warning (and many tools compile with warnings-as-errors set) is that it is possible to not assign a variable you access outside the case in every branch of it and still get a clean compile:
> 
> foo() ->
>    case bar() of
>        {bing, Spam} -> eat(Spam);
>        {bong, Eggs} -> eat(Eggs)
>    end,
>    puke(Spam).

No, that one's an outright error because Spam is not defined
in *all* branches.

It's really misleading to even talk about 'exporting' variables.
Before there were funs or list comprehensions, the model was
incredibly simple:

(a) the scope of a variable is the ENTIRE clause it appears in
(b) at any use of a variable, every path from the entry to that
    use must bind the variable once and only once.

(Yuck.  I just realised that (a) is an uncomfortably close
parallel to JavaScript.)

The code above would be just as wrong in Java and for the same reason.

funs and list comprehensions break this simple model (in a slightly
broken way, what's more; the resulting model is so far from simple
that I shan't try to summarise it).  But 'if', 'case', 'receive',
'try' have NEVER introduced new scopes in the past and don't know.

I'm reminded of programming languages like IMP and Ada where
you are not allowed to write
   p() and q() or r()
because as a programmer you are presumed to be too dumb to
work effectively with the concept of operator precedence as
applied to Boolean operators.

> 
> This has been discussed several times (here, for example: http://erlang.org/pipermail/erlang-questions/2014-March/078017.html), and because of the weirdness of case scope being one way (its a scope semantically and conceptually, but its not really)

Case is *NOT* a scope semantically, conceptually, or any other way,
and never has been.  The *only* nested scopes in Erlang are the
late-comers, funs and list comprehensions, and in both cases they
are separate scopes for a very simple semantic reason:

   variables introduced in a list comprehension may be bound
   zero or more times while the comprehension is being executed.

       Note the zero:  If I do
         [{X = foo(),X} || Y <- List]
       the List might be empty and Y and X might *never*
       be bound to anything.

       We *could* have a dialect of Erlang in which list
       comprehensions were NOT an exception to the single-scope
       model, but since such variables might never be initialised,
       they would not be available outside.  And you would not be
       allowed to redefine them because they *might* have been bound.

    variables introduced in a fun may be bound
    zero or more times, depending on whether and how often
    the fun is called.

       Note the zero:  If I do
          F = fun (X} -> Y = X+2, ok end
       the function F might *never* be called and so
       X and Y would never get values.

       We *could* have a dialect of Erlang in which funs
       were NOT an exception to the single-scope model,
       but since variables introduced in funs might never
       be initialised,, they would not be available outside.
       And you would not be allowed to redefine them because
       they *might* have been bound.

> and list comprehensions being another (it really is a separate scope, but that's not how some other languages work) this has been made into a warning.

I'm tolerably familiar with Clean, Haskell, F#, and Erlang.
They all make list comprehensions a scope.
The only declarative-family language I can think of where there
is an analogue of list comprehension that isn't a scope is Prolog
(setof/3 and bagof/3), and that works because Prolog is perfectly
comfortable with variables that might or might not be bound.
So which "other languages" make list comprehension not a scope?
> 
> I remember the warning/error discussion happening, but can't find the notes for it (I had thought the "this is always a warning" thing came up with R16 or 17...?). :-( Anyway, what I remember of it was the danger of not assiging a variable in every branch,

That's a quite different issue.  The issue we're talking about
here is where a variable is unambiguously defined in EVERY branch
of a branching construct yet the compiler whines when you try to use it.



More information about the erlang-questions mailing list