[erlang-questions] comma vs andalso
Richard O'Keefe
ok@REDACTED
Thu Jul 9 07:32:27 CEST 2009
Let's see if we can make this clear:
`and` `or` `andalso` and `orelse` are allowed ANYWHERE
in a guard expression are are treated IDENTICALLY to
the way they are treated elsewhere.
Of those things that are accepted at all in guards,
ONLY ',' and ';' are treated differently.
X , Y acts like true=X, Y
X ; Y acts a bit like case (catch X) of true -> true
; _ -> Y end.
> That was one details, yes, but the main reason was to make it possible
> to refactor a piece of code without being forced to change the names
> of the function tests if you moved an expression from within a guard
> to an ordinary expression and vice versa.
It has never been clear to me why this was considered
important. It's not something I often do in Haskell or
Clean, where guards _are_ perfectly ordinary expressions,
yet I refactor Haskell code all the time.
> Recall that before the is_...
> versions, there was no way of saying e.g., "Bool = (is_integer(X) and
> is_atom(Y))" without writing a case/if or a separate predicate
> function.
It's _still_ the case that almost no non-trivial expressions
can be moved into a guard.
> and with any existing
> code that defined a function such as list(X) or integer(X)),
And the new names created clashes with any previously existing
code that defined a function such as is_list(X). In fact,
given the obvious rule
if a module defines or explicitly imports a function
F/N that matches the name of a built-in function,
emit a warning, and for that module pretend that the
built-in function does not exist
no code would have been affected at all, other than to get
new *warning* messages. A language may need to add new
built-ins at any time. Erlang has often done so. Such a
rule is valuable.
So the argument for the "is_" prefix is a non-sequitur: it
was neither necessary nor sufficient to avoid code breakage.
> As a simple example, this macro nowadays works in both
> guard expressions and in general expressions - it didn't back then:
>
> -define(is_a_foo(X), (is_integer(X) or is_binary(X))).
>
> (we can even use orelse here, and it will still work in a guard).
But we _can't_ write
-define(is_a_bar(X), (Y = X*X, (Y < -2 or Y > 2)).
and expect _that_ to work in both guards and expressions.
[Abstract patterns would solve this problem another way.]
>
>
>> And now, for our convenience, the shorter form of these tests is
>> being
>> deprecated.
>
> Hold your horses - nobody is deprecating the use of ',' and ';'.
He didn't say that. He meant that the convenient short tests
like atom(X) are being deprecated in favour of the long names.
> This fail-to-false
> behaviour was in my opinion a mistake, because it occasionally hides
> a bug in the guard and turns what should have been an observable
> runtime
> crash into a silent "well, we take the next clause then".
There are arguments on both sides here.
>
> I'm not sure I have any real arguments against nesting of ','/';',
> but I fear the grammar could get rather messy, and that such nested
> guards could be quite difficult to read in practice.
I've been asked to help with a C compiler for a transport-
triggered architecture, so my micro-Erlang compiler is on
indefinite hold. But I did actually tackle this problem
in the grammar. The grammar rules for guards need to be
separate from the grammar rules for expressions, but neither
gets particularly "messy". If I recall correctly, it took
me about half an hour to make the changes.
Such guards _could_ be hard to read in practice, but there's
no reason they _have_ to be. It is certain that they would
make _some_ guards clearer. Consider this example:
-define(is_vowel(X),
( X == $a ; X == $e ; X == $i ; X == $o ; X == $u )).
count_adjacent_vowels(L) ->
count_adjacent_vowels(L, 0).
count_adjacent_vowels([V|S=[W|_]], N)
when ?is_vowel(V), ?is_vowel(W) ->
count_adjacent_vowels(S, N+1);
count_adjacent_vowels([_|S], N) ->
count_adjacent_vowels(S, N);
count_adjacent_vowels([], N) ->
N.
This could also be done using `orelse`, of course.
But what if you want to define guard tests that *can't*
be (ab)used as expressions?
Right now, we can't even do
-define(is_probability(X),
( is_float(X), X >= 0.0, 1.0 >= X )).
f(X, Y)
when ?is_probability(X), ?is_probability(Y) ->
X*Y.
\
More information about the erlang-questions
mailing list