[erlang-questions] Erlang elseif

Richard O'Keefe ok@REDACTED
Tue Nov 25 23:23:40 CET 2008


On 25 Nov 2008, at 9:56 pm, Daniel Rönnqvist wrote:
> Richard, I understand and to a great extent agree with your will to  
> stay in the functional language domain and don't adapt to much other  
> paradigms.

That's not exactly what my attitude/will is.
I've forgotten who said "A language that doesn't change the way you
think isn't worth learning", but there's a lot of wisdom in it.

Can you hear the sound of panting?  That's me trying (and failing)
to keep up with how the typeful FP crowd think.  Clean and Haskell 98
I can get my head around, but dependent types are starting to enter
what passes for the mainstream in FP, and while I know perfectly well
what they are, I don't yet know how to use them effectively.  The only
way will be to find some time and *try*.

Constraint logic programming:  I know *what* it is and to a moderate
extent how it works, what I don't know yet is how to use it effectively.
The only way to really get my head around it will be to spend some time
trying on realistic problems *without* all the time trying to pretend
CLP is something else.

I'm an enthusiastic Smalltalker.  My own Smalltalk compiler has
finally reached the point where I can run tests (finding bug of course)
and benchmarks (which look pretty good actually, despite doing no
optimisation at all).  The way to master Smalltalk was to try very
hard to do things in a Smalltalk style, and not try to pretend it's C.

When I was in the happy position of being able to get a free copy
of GNAT for my el cheapo and long off the market but still working
just fine thank you SunBlade 1000, I used Ada, and the way to do
that effectively is to stop trying to think like a C or Pascal or
Fortran programmer and try to think like an Ada programmer and make
really effective use of its type system.

I would love to spend some time playing with APL again, but Haskell
gives me a surprisingly similar programming style if I want it.  But
it was APL that taught me about *calculating* programs.

In short, it's *always* a good idea to try to work a language for
all it's worth *on its own terms* before trying to make it like some
other language.  (As witness the serious mess that exceptions have
made of Haskell's semantics.  Many obvious natural and useful
equalities aren't, thanks to exceptions.)

> All I'm saying is that theoretically it can be inefficient to have  
> to run _all_ tests (expressions), tag the results and then pattern  
> match on this tag, especially when the tests take a long time.

So what?  I for one have never proposed anything even remotely like
that and am not proposing it now.

The point at issue is this:
    The *precise* equivalent (linear source code expansion ratio,
    linear object code expansion ratio, linear run time expansion
    ratio, same semantics up to when types are checked) of
	if E1 then E2 else E3
    in say SML is
	case E1 of true -> E2 ; false -> E3 end
    in Erlang.

That is, Erlang *HAS* if-expressions, they are just *spelled* 'case'.
The only significant difference is thussyntactic style.
Three magic tokens are replaced by eight.
This is admittedly clumsy.
However, in well written Erlang code it should also be rare,
so it should not matter that much.

> This seem to me to be one of the two general thoughts on how to do  
> it the Erlang-way. The other one with pattern matching but that's  
> sometimes not an option, or at least it takes a lot more code (I.E  
> your example in a previous post).

But "if-is-case" is done with pattern matching, so anything that
can be done with "if _ then _ else _" in Algol 60 or SML or "_ ? _ : _"
in C can be done with pattern matching in Erlang and so pattern
matching is *always* an option.

I have provided several examples.  I cannot tell which you are
referring to.
>
>
> One last thing; how am I refusing to discuss "a key reason why the  
> present (or rather, the pre-Great Blunder) state of affairs is a  
> Good Thing." by not wanting to use full blown application as an  
> example?

Because the question at issue is whether the (more apparent than real)
lack of an analogue of if _ then _ else _ in Erlang is a good thing or
a bad thing.  If it stops people writing good code, it is a bad thing.
If it encourages them to write better code, it is a good thing.  The
only way to tell is to try real examples where someone *thinks* that
if _ then _ else would be appropriate.  If we can readily find better
code without it, then Erlang does not have a problem.

By the way, nobody has asked for a "full blown application",
only for *some* real code.  In the absence of real code, all the
debate amounts to is
    X. I think Erlang needs to be like C.
    Y. I don't.
Stalemate.  With a real example, we can make progress.


> Maybe you misunderstood me, what I wanted to know with my question  
> was if there's a way to do this (elseif) in Erlang in a  
> syntactically short and easy way without getting nested expressions.

You have two issues here: brevity and nesting.
Brevity matters for frequent things, and in Erlang, this should
not be a frequent problem.

There's an important reason why it shouldn't be a frequent problem.
Since not very long after Pascal came out, books and articles about
how to be a good programmer started saying "DO NOT OVERUSE Boolean".
Classic examples go something like this:

	function switch(S: SwitchNumber): Boolean;

If switch(17) is true, does that mean switch 17 is on, or that it is  
off?
Better to do

	type SwitchStatus = (Off, On);
	function switch(S: SwitchNumber): SwitchStatus;

and then instead of

	if switch(17) then begin
	   ...
	end else begin
	   ...
	end

do
	case switch(17) of
	   On:  ...;
	   Off: ...;
	end

The character classification example was a textbook instance of this
design antipattern.  Instead of a bunch of Boolean tests
	is_restricted_upper(C) -> true | false
	is_restricted_lower(C) -> true | false
	is_restricted_digit(C) -> true | false
there should be a single classification function
	restricted_character_class(C) -> upper | lower | digit | other

I repeat, this is a classic antipattern going back over 30 years.

So much for the brevity of what should be rare.
Now to nesting.

Algol 60 (but not Algol 68), C, C++, Java, Haskell, Clean, and SML
all require nested expressions if you want more than one 'if' in
an expression.  Let's just take determining the sign of an integer.

Algol 60	if x < 0 then -1 else (if x > 0 then 1 else 0)
Algol 68	if x < 0 then -1 elif x > 0 then 1 else 0 fi
       or	(x < 0 | -1 |: x > 0 | 1 | 0)
BCPL		x < 0 -> -1, (x > 0 -> 1, 0)
C		x < 0 ? -1 : (x > 0 ? 1 : 0)
Clean		if (x < 0) -1 (if (x > 0) 1 0)
Haskell		if x < 0 then -1 else (if x > 0 then 1 else 0)
Erlang		case x < 0 of true -> -1 ; false ->
		case x > 0 of true -> 1 ; false -> 0 end end
Lisp		(if (< x 0) -1 (if (> x 0) 1 0))
   or		(cond ((< x 0) -1) ((> x 0) 1) (T 0))

The parentheses in the Algol 60, BCPL, C, and Haskell examples
are there to point out that the expressions are in fact nested,
you don't really need them.  All other parentheses are required.
Does it really matter that much that only Algol 68 and Lisp in
this list off non-nested multi-armed if (cond)?  Why?

Again, how-to-be-a-good-programmer textbooks have been warning
for decades that complex ifs are probably wrong.
>
> I ran some benchmarks on the examples I provided with lists of 30  
> elements and there was no consistent efficiency winner in any of my  
> examples but it seemed that elseif1 (andalso & orelse) and the  
> elseif5 (using exceptions) was the slowest. I know micro-benchmarks  
> is close to completely useless but I just couldn't help myself.

No, micro-benchmarks can tell you useful things.
I'm not surprised by the exception handling approach coming last.

I will say that on today's machines lists of 30 elements are probably
far too short to get useful timings from, unless you are using hardware
performance counters.





More information about the erlang-questions mailing list