[erlang-questions] Local namespaces in case/try

Richard O'Keefe ok@REDACTED
Mon Jul 16 01:47:13 CEST 2012


On 15/07/2012, at 10:40 PM, Dmitry Groshev wrote:
> don't you think about case as some sort of imperative construction?

HELL NO!

I don't think of 'case' in SML as imperative.
I don't think of 'case' in Haskell as imperative.

Because they aren't.

So why should I think of 'case' in Erlang as imperative? 

Because it isn't.

Yes, of course you can wrap 'case' around some code that
_is_ imperative, but if I put a tiger in a wooden box,
that doesn't mean the *box* has teeth and claws.

And in any case, what has "imperative" to do with
"variable scope"?

> Because I can't see why "case" can't be that "keyword to introduce new variable"

It _could_ be in a new language.  But it isn't in Erlang,
and there are good reasons why it wasn't.

> — case is isolated in some sense.

Why yes: in the sense that there is nothing like 'case'
except, oh, 'if', and, 'receive'.  They are all exactly
the same in this respect.  Where do you get 'isolated'?

> It was already done with list comprehensions, after all.

There is a HUGE difference.
In the alternative-choice constructs of Erlang,
if execution continues after them, exactly one of the
alternatives will have been executed exactly once, so
any variable which is bound in every alternative will
certainly be available with an unambiguous binding.

List comprehensions and funs share an important characteristic
which is relevant: the bindings in them might never be
executed at all, or if executed might be executed very many
times.  They cannot produce an unambiguous binding.  So they
do not.  

> Moreover, this is the case in Haskell that you've mentioned:
> 
> Prelude> let a = [1, 2, 3]
> Prelude> case a of [b, c, d] -> "ok"
> "ok"
> Prelude> b
> <interactive>:6:1: Not in scope: `b'

Haskell is not Erlang; many aspects of Haskell differ from Erlang,
starting with the syntax of identifiers ("a" is a variable in
Haskell but a constant in Erlang; Haskell allows "'" in identifiers
but forbids "." while Erlang does it the other way).

In particular, Haskell and Erlang have different scope rules.
So what else is new?  That does NOT mean that Haskell is the only
way to do things.  (Although I do love Haskell.)

> So does OCaml:

which is the runner-up for the title of "world's ugliest functional
language".  The prize-winner is of course XSLT.

I keep trying to use O'Caml for the excellence of its compiler,
and keep being driven away by its ugliness.

The important point, of course, is that these are different languages
with different rules about practically everything.

> 
> So it's more like some obscure "feature" of Erlang for newcomers, which is no good.

Erlang is designed for people who are willing to RTFM.

> Speaking about better example:
> 
> test(Input) ->
>     Result = case test1(Input) of
>                  {ok, BetterInput} ->
>                      case test2(BetterInput) of
>                          {ok, BestInput} ->
>                              do_something(BestInput);
>                          {error, _Reason}=Error ->
>                              Error
>                      end;
>                  {error, _Reson}=Error ->
>                      Error
>              end,
>     case Result of
>         {ok, _}=Ok -> Ok;
>         {error, _Reason}=Error -> %% OOPS!
>             do_something_on_error(Error)
>     end.

But this is NOT ***REAL*** CODE.  It is an example made up
for the occasion.  I have no reason whatsoever to imagine
for one instant that this is anything like code that anyone
*really* needs to write.

Amongst other things, I find this tangled code quite
unnecessarily hard to read.  Let's try to figure out what
it is supposed to do

test1(Input)            = {ok,BetterInput} and
test2(BetterInput)      = {ok,BestInput} and
do_something(BestInput) = {ok,Answer}
 => {ok,Answer}

Anything along the way = {error,Reason}
 => do_something_on_error({error,Reason})

Anything else
 => crash.

Ah hah!  Just

test(Input) ->
    case case test1(Input)
           of {ok,Better_Input} ->
              case test2(Better_Input)
                of {ok,Best_Input} ->
                   do_something(Best_Input)
                 ; E -> E
              end
            ; E -> E
         end
      of OK  = {ok,_} ->
         OK
       ; ERR = {error,_} ->
         do_something_on_error(ERR)
    end.

Now it's a _little_ clearer.

This actually looks like a case where throwing
exceptions instead of returning {error,Reason}
would be better:

test(Input) ->
    try do_something(test2(test1(Input)))
    catch {error,E} -> do_something_on_error(E)
    end.

I repeat my earlier request, with added emphasis.

> Give us a SERIOUS example! 
> Show us some code that is easier to read if you do this!





More information about the erlang-questions mailing list