exported variables (was: RE: How to make GS Widgets)

Richard Carlsson richardc@REDACTED
Thu Apr 15 17:32:57 CEST 2004



(Long explanation below, but it might be worth reading. Bear with me.)


On Thu, 15 Apr 2004, Ulf Wiger (AL/EAB) wrote:

> Let me dodge your questions and instead volunteer a suggestion
> on a minor aspect of programming style:   (:
>
> 	if
> 		is_tuple(SuppliedColor), size(SuppliedColor) == 3 ->
> 			{Red, Green, Blue} = SuppliedColor;
> 		true ->
> 			{Red, Green, Blue} = {0, 0, 0}
> 	end,
>
> If we tell the compiler to be a bit picky, it will warn against
> this type of code:
>
> erlc -W +warn_export_vars mywidgets.erl
> /home/etxuwig/work/erlang/mywidgets.erl:44: Warning: variable 'Red' exported from 'if' (line 35)
> /home/etxuwig/work/erlang/mywidgets.erl:47: Warning: variable 'Green' exported from 'if' (line 35)
> /home/etxuwig/work/erlang/mywidgets.erl:50: Warning: variable 'Blue' exported from 'if' (line 35)

Yes, but if the code is readable enough, as I would argue that it
definitely is in the above case, there is no real reason to rewrite
it to avoid "exporting variables from clauses". It's mostly a
matter of personal taste, but after all, Erlang was designed from
start to allow this way of binding variables, so why not use it
as long as your code is readable?


> Personally, I was a bit surprised that the compiler doesn't actually
> issue a warning when compiling mywidgets.erl, since Red, Green and
> Blue are in fact referred to after the 'if' expression.
>
> >From the man page for 'compile':
>
> "warn_export_vars
> Causes warnings to be emitted for all implicitly exported variables
> referred to after the primitives where they were first defined. No
> warnings for exported variables unless they are referred to in some
> pattern, which is the default, can be selected by the option
> nowarn_export_vars. "
>
> After a short experiment, I understand that the operative phrase is
> "in some pattern", but it's too early in the morning for me to grasp
> why one needs to issue warnings when implicitly exported variables are
> used in patterns, and not when they are used in expressions.

Here's the explanation:

  1. In the dawn of time, Erlang allowed case switches (and receives,
     etc.) to export variable bindings from clauses, as long as
     they were "safe", i.e., guaranteed to be bound no matter which
     clause was executed. All fine so far.

  2. A particular form of errors became common, sometimes due to
     cut-and-paste programming, sometimes due to programmers forgetting
     which variable names they had already used further up in the same
     function, like this:

        case ... of
            true -> ..., X = foo, ...;
            false -> ..., X = bar, ...
        end,
        ...
        ...
        ...
        case Y of
            baz -> ...;
            X -> f(X)
        end,

     It's easy to miss that the "temporary local variable" X in the
     first case switch actually is legally exported (since it is
     bound in both clauses). As a consequence, the intended catch-all
     clause in the second case switch becomes not a binding occurrence,
     but a *test* to see if Y equals the value of X. Often, your
     program just crashes when this sort of thing happens, but sometimes
     it mysteriously keeps working, for instance if you instead had
     the following second case switch:

        case Y of
            {baz, X} -> f(X);
            _ -> g()
        end

     where the {baz, X} pattern would perhaps never match, and the
     catch-all is executed every time instead.

     Anyway, someone thus decided to add a compiler warning for all uses
     of exported variables, even the correct and reasonable ones. Since
     these warnings were enabled in several releases of Erlang, many
     people got used to the idea that exports were completely banned.

  3. However, the following programming patterns are both
     correct, useful, and reasonable:

        X = f(...),
        ...
        case Y of
            {foo, X} -> ...;
            {foo, Other} -> ...;
            _ -> ...
        end

     (which did not cause any warnings), and

        case ... of
            true -> X = 1,
                    Y = 2,
                    Z = 3;
            false -> X = 2,
                     Y = 4,
                     Z = 8
        end,
        R = f(X, Y, Z)

     which made the compiler complain (although it still
     compiled the code, since it is not at all incorrect).

     So it seems that the only place where a warning was really
     warranted was in the case I first described above, which is:

         A) a variable is exported from a case/if/receive
         B) the same variable is used in a pattern further down

     Of course, such a use could still be legal, but this rule
     seems to be a better compromise that allows you to use the
     language *feature* of exporting variables (without complaints),
     while still catching most typical mistakes.

     (I think this was done in R8, if I remember correctly.)

  /Richard



Richard Carlsson (richardc@REDACTED)   (This space intentionally left blank.)
E-mail: Richard.Carlsson@REDACTED	WWW: http://user.it.uu.se/~richardc/
 "Having users is like optimization: the wise course is to delay it."
   -- Paul Graham



More information about the erlang-questions mailing list