[erlang-questions] Side-effects in "if" guards

Robert Virding rvirding@REDACTED
Mon May 19 00:58:19 CEST 2008


2008/5/15 David Mercer <dmercer@REDACTED>:

> > The main snag is that the rules for both syntax and semantics of
> > guards is subtly different from those of normal expressions, so
> > it's hard to extend what you can write in an if-guard in a way that
> > does not change the behaviour anywhere in all the existing code.
>
> What are the subtle differences?  I suspect most new users do not
> completely
> grok that, since this subject comes up repeatedly.


There are 2 differences between guards and normal expressions:

1. The most obvious: guards are restricted to using a subset of BIFs.
Basically those that are side-effect free. This is both easy and difficult
to understand. Easy, because you can read in the manual which ones are
allowed, difficult because it may not be obvious why only a restricted
subset is allowed.

2. The more subtle difference is handling of errors. In a "normal"
expression, even ones only using the guard subset, an error generates an
error which crashes the process if not caught in some way. In a guard,
however, no error is generated, the only result is that the guard fails and
the next if/case/receive clause is tried.

This means that the following are *NOT* equivalent:

    if  5 * X > 39 -> ... ;
        true -> ...
    end

    case 5 * X > 39 of
        true -> ... ;
        false -> ...
    end

Assume X is not a number. In the 'if' case then all that will happen is that
the if test, a guard, will fail and the second clause will be chosen as the
test in an if is a guard. In the 'case' case, however, an error will be
generated and neither case chosen.

A normal expression has two outcomes: it can succeed and return a value or
it can generate an error. A guard (expression) has 3 outcomes: it return
true (succeed), it return false (fail) or it can generate an error. BUT for
a guard generating an error results in the guard failing, i.e. it is
equivalent to it returning false.

This was done to make the testing easier and remove obvious explicit type
tests. As guards originally were just flat sequences of simple tests this
caused no real logical problem. When guards were extended to be a sequence
of guards, separated by ';', an error now caused that guard in the sequence
to fail leading to testing the next sequence. As guards were still flat
sequences of simple tests it was still easy to comprehend.

Now guards can be full boolean expressions and it is not quite so clear how
errors in guards should be handled. How far do you go up before returning
false? The actual operation which caused the error? Up to the next boolean
operator and return false to it? All the way? What is done is the same as
before, an error fails that guard in the guard sequence.

This answer became a little longer than I had anticipated but I hope things
are a little clearer now.

Robert
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://erlang.org/pipermail/erlang-questions/attachments/20080519/67816f3b/attachment.htm>


More information about the erlang-questions mailing list