[erlang-questions] guard expression restriction

Richard O'Keefe ok@REDACTED
Thu Dec 2 04:02:29 CET 2010


On 2/12/2010, at 4:11 AM, Andy Kriger wrote:

> As a newcomer to Erlang, I'm curious about the reason for the
> restriction on guard expressions to a subset of valid Erlang
> expressions

Strictly speaking, they aren't, and in earlier versions this
was much clearer.  Guards used "," for "and" and ";" for "or"
and tests like atom(X) and P > Q, and *originally* these could
not be expressed in bodies at all.

What's more, for those operations which were allowed in both
contexts, the *semantics* were different.  In a body, 1+a
raises an exception; in a guard, 1+a simply fails.

With hindsight, it was a conceptual mistake when the language
was changed to allow guard tests to be called in bodies as if
they were functions, because now it _looked_ as though guards
were a kind of expression, which thanks to the differing
semantics, they are not.

It was another conceptual mistake when the language was
extended to give us (at least) four different ways of saying
"and", and another blurring of categories when they were all
allowed in guards.

But it's _still_ the case that operations in guards and
operations in bodies do not mean the same thing, so despite
the blurring and confusion in recent versions of Erlang,
it still is not true that guards are expressions.


> http://www.erlang.org/doc/reference_manual/expressions.html#id75235
> " The reason for restricting the set of valid expressions is that
> evaluation of a guard expression must be guaranteed to be free of side
> effects."
> 
Actually, that's wrong.  Erlang guards were not obtained by restricting
Erlang expressions, but by generalising Strand88 guards, which are
basically the same as Parlog or FCP guards.

It's also wrong because it is also important that guard tests *terminate*.
The thing is that a "reduction" in Erlang is selecting a rule; it's the
primitive computational step, and because it's the _primitive_ computational
step, it should not result in any communication, the selection of one rule
should be independent of the selection of any other that might be happening
at the same time.

> That's nice, but basically says 'this is so important that the
> programmer cannot be trusted to get it right.'

While that's not what it says, it appears to be true.

> Many other parts of the
> language don't make this statement - fail early and often is
> encouraged, from what I understand.

This is a non-sequitur.  Not allowing communication, side effects, or
non-termination is not about not *failing* but about not going *mad*.

>  So, I'm wondering about the
> history behind that decision and if there's been any talk of removing
> that restriction now that Erlang has expanded far beyond the telecom
> world.

It never had anything to do with "the telecom world".
It has to do with Erlang's origins in the committed concurrent logic
programming world (which is also why Erlang doesn't really have a
proper Boolean type), and the conceptual integrity of the language.

There has been a never-ending stream of people wanting or demanding
Erlang guards to be replaced by expressions.  Please do search the
archives.

> There's a definite benefit to be had in clarity in allowing
> user-defined boolean functions that are specific to the application.

So far nobody has demonstrated this.
On the contrary, I have demonstrated that "user-defined boolean
functions" are almost always a design disaster.  (This is very far
from an original observation.  It was a commonplace in procedural
programming back in the 80s that most Boolean variables should be
of some other enumeration type, and it's a commonplace in functional
languages that most Bools should be Maybes or Eithers or something
else.)
> 
> Not looking to start a fight if this is well hashed ground.

This ground has been fought over so often that it's as much a
mix of mud and blood as a WWI trench.
> 

There is of course a fairly elementary transformation by means of
which anyone who *really* needs a function call in a guard finds
out that they don't.

	f(Arguments) when Expression -> Body;
	<rest of f>

=>
	f(Arguments) ->
	    case Expression
	      of true -> Body
	       ; false -> f'(Arguments)
	    end;
	f(Vars) -> f'(Vars).

where f' is the rest of f with the name changed.

I'm not providing an example, because I've tried four and each of
them was immediately better without trying to do this in guards...



More information about the erlang-questions mailing list