Enhanced type guard syntax]

Joachim Durchholz joachim.durchholz@REDACTED
Thu Sep 18 22:23:45 CEST 2003


Valentin wrote:
> For example, when we say ONE, we can write it as "1" or
> "one" -- it wouldn't change the meaning, right? 

<aside note>
Er... I wouldn't consider that Good Style (an interface shouldn't accept 
inputs that are so ill-defined as natural language), but of course there 
are similar and worse interface in practice. (Heck, I have been forced 
to do some interfaces of this kind myself.)
For example, if a library has lived long enough, it will accept inputs 
of "modern" and of "ancient" style, which tend to be quite different 
data structures.
</aside note>

I think two things are getting conflated here: guards and preconditions.

A guard is a kind of selector: the first implementation that has guards 
that match a given set of parameters is chosen. (In OO, all guards are 
on "typeof (first argument) <= typeof (first parameter)", so, in a 
sense, Erlang is indeed OO, but not on the message level *grin*.)
If a given function has the wrong guards, the system will try another 
one, it will not fail.

A precondition is unrelated to implementation. If the precondition 
fails, the caller has done something evil. This is things like calling 
sqrt with a negative argument, or, say, bank transfers with identical 
source and destination accounts. Any routine added that has a matching 
guard is simply wrong, it violates the contract that's supposed to go 
with the function of that description.

Preconditions are valuable. Both as a kind of built-in unit test, and as 
a documentation aid.
For testing, have the system compile the stuff with precondition 
checking enabled. For extra convenience, have the system compile them in 
anyway, and have them activated and deactivated by the run-time system. 
For generous convenience, have them activated and deactivated on a 
per-module or even per-function basis :-) (oh, and activate and 
deactivate them in the *calling* places, you usually want to debug the 
caller if you're checking precondition violations *g*)
For documentation, it's simply both more precise and concise to have
   sqrt (X >= 0.0)
instead of the verbose and natural-language all-too-often-fuzzy
   sqrt (X)
   %% X must be >= 0.0


Syntax ideas (just thinking loudly):

Ideally, one would use something like
   function (X/guard)
where "guard" is a fun() that accepts a single parameter, and returns 
True or False.
For example, this would allow one to write stuff like
   is_real (X) and X >= 0.0
Unfortunately, taking out the X and getting this into a fun() is going 
to be *very* ugly. Too much syntactic cruft around here.
Haskell solves the issue far more elegantly IMHO: any "incomplete" 
expression (which is an expression missing its last parameter) is 
automatically a fun(). The above as a fun() would then look like this:
   function (X / is_real and ('>=' 0))
where the constituents are:
'>=' is the ordinary >= operator, just as a function of two parameters 
(i.e. you'd use this as '>=' 0 X to mean "0 >= X"); this is just 
syntactic sugar to convert the >= symbol from an operator into a 
function name.
'>=' is a function of two parameters. In the above, the first parameter 
is given, yielding the function ('>=' 0), which is the function that 
returns True if its only parameter is greater than or equal to zero.
is_real is just the name of a function, a one-parameter function that 
takes a single parameter.
"and" is actually and/3, not the ordinary and/2. It take two 
single-parameter functions and a value; when called, it feeds the value 
to the functions and returns the logical AND of the two function result. 
(I'm not sure whether the compiler can be made to recognize that the 
"and" in the "is_real and ('>=' 0)" bit actually is and/3. Maybe some 
syntactic sugar is required. Well, I'm just thinking loudly anyway, 
without any claims to relevance *g*.)

Hmm... no, it won't do. Erlang identifies functions by name and 
parameter count. If I write "foo X", the compiler expects it to be a 
call to foo/1, not a scantily clad foo/2 missing its second parameter. 
We'd need something like an extra "dummy" parameter, something like
"foo X _".
In the above, this would be
   function (X / is_real (_) and ('>=' 0 _))
or even
   function (X / is_real (_) and 0 <= _)
which isn't much of an advantage over
   function (X / is_real (X) and 0 <= X)
but remember that parameter names can be *much* longer than a single 
character :-)


Anyway, on to another aspect: guards that involve multiple parameters.
For a contrived example, assume
   distribute_percentages (X, Y, Z)
   %% X + Y + Z must sum up to 100.
I'd prefer to write this as
   distribute_percentages (
     X / in_range (0, 100, _),
     Y / in_range (0, 100, _),
     Z / in_range (0, 100, _) and X + Y + Z = 100.0)
(See how the formalization forced me to become quite explicit about what 
a "percentage is?)


Just my 2c.

Regards,
Jo




More information about the erlang-questions mailing list