ok ok@REDACTED
Mon Sep 10 03:22:44 CEST 2007

```On 8 Sep 2007, at 2:31 am, Richard Carlsson wrote:
>   - you couldn't just cut out a guard and paste it in somewhere,
>     because the scope wasn't the same - atom(X) and the other tests
>     were not defined outside guards

You *still* can't do that because the *semantics* is different.
Consider

atom(X), integer(Y)

or, if you prefer to be long-winded,

is_atom(X), is_integer(Y).

As a guard, this asserts the conjunction "X is an atom (and) Y is an
integer".  As an expression, it says "determine whether X is an atom
or not but forget the answer; now tell me whether Y is an integer".

With comma having different semantics in the two environments, it is
actually a very useful protection when a guard test uses a name that
is not available in expression.

atom(X), integer(Y)

has the decency to raise an exception if you try to use it in an

>
>   - you couldn't access the type tests directly as a boolean value,
>     as in "Bool = atom(A)" - you'd have to write "Bool = (if atom
> (A) ->
>     true; true -> false end)", which is not exactly elegant

This was trivially fixable with a macro:

?G(X) ===> if X => true ; true => false end

Note that the semantics of 'length', amongst others, is subtly
different between guard and expression contexts, so
?G(length(X) < 10)
is *not* the same as
length(X) < 10

>
>   - some names could have different meaning depending on whether they
>     were used as guard tests or as normal expressions - float(X) is
>     a boolean test for float-ness if it's a guard, but if it's a
>     normal expression then it refers to the int-to-float conversion
>     function

That is the only persuasive argument I've ever heard, and in
light of the other semantic differences, it would have been better
to rename the convert-to-float function to as_float/1.

```