Good practice

Tue Jul 22 03:44:48 CEST 2003

> A bit late (hey, it's summer), but I thought I should try to give an
> explanation of this behaviour.

And greatly appreciated it is!
> On Fri, 18 Jul 2003, Chris Pressey wrote:
> > On Sun, 8 Jun 2003 17:28:01 -0400 (EDT)
> > erlang@REDACTED wrote:
> > > bash$ erl
> > > Erlang (JAM) emulator version 47.4.1
> > >
> > > Eshell V47.4.1  (abort with ^G)
> > > 1> c(test1file).
> > > ./test1file.erl:172: Warning: variable 'Response' exported from
> > > ['case']{ok,test1file}
> > > 2>
> Wow! JAM version 47.4.1! This is an ancient system. (On the other hand,
> that particular compiler warning was not changed until BEAM R7 or R8, so
> a lot of other people may also see this warning still.)

*ahem* well, yes, you see the thing is I have this ancient hacked up slackware 
box with broken libs and header files which is in too delicate a state 
pre migration to a more sensible system on which more recent (BEAM-based)
versions will actually compile.

Don't ask me why; I'd love to use gs, and I love erlang, but when I think
too hard about the rationale behind C's header files and libraries and so
on, my head starts to hurt and my cats all hide and my wife tells me to
stop screaming before she hits me with her cast iron frying pan ...

Anyway, it's what I have to work on for now, until the budget is there for
an upgrade, and I can move critical running systems elsewhere so that I 
can do a clean reinstall.
> > > From the file in question:
> > >       case checkvalid(Funcref, Correctlist) of
> > >         false ->
> > >           Response = false;
> > >         Other ->
> > >           Response = invoker(Other, Otherargs)
> > >       end,
> > >       Requester ! Response,
> > I've never actually understood why this isn't a full-fledged error.
> > Is there a good reason that bindings are allowed to 'leak' out of a
> > case..end?

My position was that a case statement is just that; a single statement.
A rather convoluted one, but from which I would expect bindings to leak
just the same as any other statement.  Much to my surprise, judging
by the remarks below, it seems to have been the way some other people
felt about it ...
> It is the straightforward extension from the current idea that bindings
> 'leak' out of a single expression, so you can use the binding later on,
> for example:
> 		X = f(Y),
> 		...
> 		g(X)
> Now you might say "but that's an *assignment* - it's different!"
> But it's not. "X = f(Y)" is just an expression which happens to
> contain a variable binding, which is "exported" from the expression
> and becomes avaliable in the expressions following the ','.

And more particularly, to my eye, it's occurring in the same function, which
suggests to me that it should, intuitively, be the same scope (more or less).

> So, logically, if an expression (like a 'case') contains alternative
> branches, the exported bindings from that expression should be those
> that are exported from each of the branches. (Those that could be
> undefined in one of the branches are "unsafe", and it *is* a compile
> time error to use an unsafe variable.)

I'm glad you wrote the checker to be this careful.  Then on the other
hand I'm a big, big proponent of very clear code based on the smallest
subset of the syntax diagram which will reasonably do what I need.  It
makes my code more verbose, of course, but that was why the error above
puzzled me; I felt that what I was doing was sensible and sane ...

> The code above should be a perfect illustration of a "good" use of this
> form of binding. Simple, straightforward, readable.

... with which you appear to agree, and thanks for the vote of confidence.

> However, it turned out that in certain programs, this kind of binding
> tended to introduce errors. Sometimes, you introduce a variable such as
> 'X' as a temporary variable in a branch of a 'case' (for instance in the
> clause pattern), and you only intend to use it "locally", but if you use
> the same name 'X' in *all* the branches, the variable 'X' is suddenly
> legally exported to the following expressions. What happens next is of
> course that further below, you use the "local" name 'X' *again*, without
> realizing that it is already bound to some value. The result is that the
> later occurrence of 'X' does not become a binding, but a matching, and
> most likely, you get a runtime 'badmatch' error.

Yes, the badmatch would make sense then.  I'm inclined to think of this,
myself, as a nasty case of programmer error, usually a result of poorly
chosen variable names.

> The quick fix for this was that the "variable exported" warning was
> added to the compiler (in the old JAM days). But this made *all* uses of
> such exported variables trigger a warning. It fixed the problem, but was
> annoying, since you could no longer use this actual *feature* of the
> language without getting compiler warnings all over the place.

Well, on the other hand, to be entirely fair, when I originally asked the
question the responses pointed out some educationally improved ways of
actually performing the trick in question (such as pulling it from the
case explicitly).

[snip description of patch fixing complaints]

> In short: exporting variables from 'case' expressions (and similar) is
> OK if you just *use* the variables afterwards, but if you put them in
> patterns you will get warnings.

Here's a deeper question I would like to ask:

How do you go about deciding which things are bad practice for erlang
programmers which need complaints?  The reason I ask is that there are
neverending discussions of lint for C and why one should do what it 
says (or at least understand it) and why that constitutes good practice,
however I have yet to see such a discussion in erlang.

> > My language designer's intuition is telling me that the scoping rules
> > for case..end should be more like those for fun..end.
> Erlang's variable scoping rules make it sufficiently different to
> confuse any language designer. The hope is still, I think, that the
> actual Erlang programmers are helped by them rather than confused. It is
> after all pretty straightforward to bind variables in Erlang - you don't
> have to think too hard about writing 'let' statements. There is of
> course a tradeoff between easy introduction of variables and simple
> scoping rules. I don't know any other language that has walked this path
> as far as Erlang. You have to ask yourself as a user whether it's good
> or bad.

I think that erlang's current state is on the right side here.

If god meant case statements to have independent variable binding scopes,
then dammit, he'd have made them separate functions!  Instead they're just
selection statements which can go one of several ways.

I like to think of erlang (and functional languages in general) as being
rather similar to macro assemblers in spirit, where each macro is a function,
and what you're passing is register state.  Of course, I very hastily 
don my asbestos suit when saying that, but I also say this because that is 
how I program assembler, and why I tend to have a low error rate.  My asm
isn't the fastest on the block, but when I guarantee the entry and exit 
state of each macro (as far as register state is concerned, anyway), it gets 
a lot easier to produce working code rather than spaghetti ... but I digress.

More information about the erlang-questions mailing list