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

zxq9 <>
Thu Oct 1 14:38:22 CEST 2015


On 2015年10月1日 木曜日 22:35:09 you wrote:
> > On Thursday 01 October 2015 19:28:23 zxq9 <> wrote:
> 
> Personally, I blame Wirth whose broken Pascal precedence
> rules corrupted generations of programmers, including ones
> who've never heard of Pascal.

I've got ambivalent feelings about this statement. Partly because Turbo Pascal was the only real thing available to me when I was a kid, but mostly because the categorical breakdown of precedence operators makes its own kind of sense:

1. Negation
2. Multiplicative operations
3. Additive operations
4. Relational operations

Instead of just trying to make it easier to write a compiler I think he tried to logically group precedence by category (or rather, include logical operations within their proper categories). But that does leave the relative precedence of `and` and `or` surprising and perhaps inconvenient unless you think about things this way. I don't know many people who think about operator precedence this way though, so in reality its a moot point and practically speaking Pascal's precedence rules are "just weird" because they don't mimic everything else.

But there is an underlying logic all the same, and I don't grudge Pascal that (again, childhood bias may just be speaking here -- I thought C was super-awesome-wizardry once I finally got a compiler for it).

> Why do people *expect* something that's expicitly contradicted
> in the manual and textbooks?

Speaking only for myself here...

Probably because I read through that part of the manual once quite a while back but have seen this warning several times in the intervening months and years. So the warning seems to speak louder to me, saying, "this is discouraged and breaks some hitherto unspoken conceptual model to which it is important enough to adhere that the compiler will now scold you".

But I never see this in my own code. I would never have written the examples shown so far. I tend to use `case` and `if` the same way I use funs -- when I need to acccess some local context *and* the result is more readable that way than passing a context variable along. Other than this I usually just write separate functions, sometimes with lots of clauses. I don't think this is particular unusual as far as Erlang style goes. (?)

So... why? Laziness, lack of time, ignorance, unwillingness to adopt a new conceptual model, mental model built on the back of a string of incidentally correct (but unchecked) assumptions, etc. Some subset of these seems likely. It really was explicit in the manual. 

I think the percentage of cool kids who both:

1- want to dabble with Erlang for long enough to subscribe to the ML and toss out a few FAQs after seeing some buzz about it on Y-combinator
2- actually read the manuals instead of fumbling through a few tutorial blogs

is very low.

("Cuz everything is, like, sorta Java or Ruby Javascript or something when you get down to it, right?")

> > It makes me feel sad, all the same. I like limited scope and knowing for
> > sure that it is limited.
> 
> I like that too, which is why I often wish Erlang syntax
> were more Haskell-like.  The answer, of course, is to keep
> Erlang clauses short.  Erlang syntax was strongly influenced by
> Strand-88 syntax, and that was based on Prolog syntax, and
> that's where the single-scope model comes from.

Only being semi-serious here... what would be wrong with just ditching all those other constructs and using only funs and functions for conditionals?

To re-use the contrived example:

blah(File, Mode) ->
    case Mode of
        r -> {ok, FP} = file:open(File, [read]);
        w -> {ok, FP} = file:open(File, [write])
    end,
    file:close(FP).

becomes

blah(File, Mode) ->
    FileOp = fun
        (r) -> file:open(File, [read]);
        (w) -> file:open(File, [write])
    end,
    {ok, FP} = FileOp(Mode),
    file:close(FP).

Of course, once we're that far there is almost nothing stopping us from just writing a separate function which is usually what I wind up doing anyway (though this isn't a very powerful example of why).

> > Not declarative ones, imperative languages that include this or that
> > generator/comprehension feature and are familiar with the cool kids.
> >
> > Python, for example:
> >
> >>>> [x for x in [1,2,3]]
> > [1, 2, 3]
> >>>> x
> > 3
> >
> There is much to like about Python, but it has its share of
> stupidities.  This is one of them.
> 
> >>> [x for x in []]
> []
> >>> x
> Traceback (most recent call last):
>   File "<stdin>", line 1, in <module>
> NameError: name 'x' is not defined
> 
> Talk about landmines!

That made me actually laugh out loud. This particular edge case is just hilarious. Totally understandable from a concept where comprehensions are basically just loops, but still pretty funny, especially since it would be easy to avoid this problem and just make a rule that its safe to always reference it later.

> > especially considering that using unassigned list comprehensions
> > as a shorthand for lists:foreach/2 specifically to get side-effects is now
> > actually supported as a an optimization.
> 
> But those effects do NOT include mutating variables.  Each iteration
> gets its own set of variables because that's the only way that the
> variables *can* have different values in different iterations.

True. Though we do have plenty of opportunities to effectively violate this whenever we want -- fortunately full respect for single-assignment is a part of the culture around here (I don't even see much abuse of the process dictionary, though sometimes I see thoughtless assumptions about what the "next" query to a database or any other similar shared resource might return...)

> There are plenty of things that are technically legal in Erlang but
> probably not a good idea.  For example,
> 
>     f(X) -> X,-1.
> 
> is legal.  But is it sensible?  When I made my Smalltalk compiler
> report about 'statements with no effect' (which is perfectly legal),
> it promptly told me about actual errors in my code I hadn't previously
> noticed.



> PS: the Erlang system I tried the example above in did not compile
> at all.  I haven't installed R18 yet.  Does R18 catch this?

Nope:

4> F = fun(X) -> X, 1 end.
#Fun<erl_eval.6.54118792>

Compiles with no complaints as well in 18.1.

I did think it was good that the shell provides the same scrutiny as the compiler in this case:

1> F =
1>   fun(X) ->
1>     case X of
1>       {foo, A} -> A;       
1>       {bar, B} -> B        
1>     end,                 
1>     A 
1>   end.
* 5: variable 'A' unsafe in 'case' (line 1)

Not all languages REPLs are so thorough about their rules.


More information about the erlang-questions mailing list