Erlang is getting too big

Richard Carlsson richardc@REDACTED
Tue Oct 14 12:31:30 CEST 2003


On Tue, 14 Oct 2003, Bengt Kleberg wrote:

> Thomas Lindgren wrote:
> ...deleted
> > Here's another part that could stand cleaning up:
> >
> > - guard syntax 1: "," and ";"
> > - guard syntax 2: "and", "or"
> >   (are they there anymore?)
> > - expression syntax 1: "and", "or"
> > - expression syntax 2: "andalso", "orelse"
> >
> > All of them working somewhat differently.
>
> oops. what is the difference between ',' and 'and'? and ';' and 'or'? i
> have been using them to mean the same thing.
>
> eiffel has 'andalso' and 'orelse'. the only :-) people that finds 'and'
> and 'or' strange are the ones that has a c background.

Perhaps the most complicated part of the Erlang language are the clause
guards. They are a mess, although we've tried to clear up a few things.

	1. Guards are comma-separated sequences (conjunctions) of "tests",
	   i.e., the commas mean logical "and". (So far so good.)

	2. Later, it was added that you could have alternative lists
           of tests, if you separated them with semicolon as in
	      "t01, t02, t03; t11, t12, t13; t21, t22, t23 ->"
	   (a disjunction of conjunctions.) So, the semicolons mean
	   logical "or" in this context. (This addition was probably
	   one of the less motivated ones. I've never used it myself.)

	3. A "test" is either a comparison "X == Y", "X > Y" etc, or
	   a call to a built-in test function like "integer(X)" or
	   "float(Y)". (I.e., it is not just any boolean expression.)

	4. Guard tests have a separate name space! "integer(X)" above
	   is not defined outside guards, so you can't write:

	       Bool = (f(X) and integer(Y))

	   in normal Erlang code - unless you define "integer/1"
	   yourself. (But you can't override the tests used in guards.)
	   The test "float(X)" overrides the built-in typecast function
	   of the same name. You _can_ write the following:

	       Bool = (f(X) and float(Y))

	   but you might be surprised when "float(Y)" does not return a
	   boolean.

	5  The subexpressions of guard tests are "guard expressions",
	   which are a limited subset of normal expressions. Basically:
	   variables, constants, operators and calls to a subset of the
	   "automatically recognized" BIFs are allowed, but nothing else.

	6. Guard tests are implicitly "wrapped in a catch", so that if
	   a subexpression crashes (the tests themselves can't crash),
	   they quietly evaluate to "true" instead. (Thankfully, the
	   number of programmers crazy enough to actually make use of
	   this "feature" is very small. I have not seen it used in OTP
	   and I would like to have it removed from the language.)

	7. The boolean operators "and" and "or" (and "not" and "xor")
	   have been in Erlang quite a while, but were defined to be
	   strict, i.e., always evaluate both arguments. Furthermore,
	   they could originally not be used anywhere in a guard(!).

	8. When "and" and "or" were eventually allowed in guards, they
	   actually got the same semantics (within guards) as "," and
           ";" respectively, since guards cannot have side effects
	   (and thus it does not matter if you use strict evaluation or
	   not, space/time aside). But there is no point in using and/or
	   instead of comma and semicolon unless you like longer lines.
	   Stick to the normal separators. It's just in subexpressions
	   that you might use and/or/not/xor once in a blue moon.

	9. 'andalso' and 'orelse' (the non-strict versions of 'and' and
	   'or' can be useful for avoiding deep nesting of case
	   switches`, and can make the code much more concise. For
	   example:

		case f(X) of
		    true -> true;
		    false ->
		        case g(X) of
		            false ->
		                case h(X) of
		                    true -> true;
		                    fasle -> false
		                end;
		            true -> false
		        end
		end

	   is equivalent to

		f(X) orelse (not g(X) andalso h(X))

	   Now, how long did it take for you to verify that the
	   first version really implements the second one? What
	   happens if you misspell 'true' or 'false' somewhere?
	   (Personally, I'd have preferred 'and' and 'or' to be
	   defined as non-strict to begin with.)

And now, for 10,000 dollars*:

    What does the following do, if X is a floating-point number?

	if float(X), (float(X) == true) -> true;
	   true -> false
	end

What we've tried to do in the last years is to make the differences
between normal expressions and guards smaller. It's not easy, as I hope
you can see from the above. It was in order to clear up the confusion
with the guard test namespace that we added the "is_..." versions of the
type tests. These have the same definition everywhere, and you can even
call "erlang:is_integer(X)" if you need to. I think they are a good
thing.  It is much easier to say to a beginner "Oh, 'integer(X)' is
old-style - avoid that and use 'is_integer(X)' instead." instead of
trying to explain why he can't write 'integer(X)' in other places than
guard tests.

A new book would certainly be in order, if anyone could find a
publisher.

	/Richard


* Altairian dollars.



Richard Carlsson (richardc@REDACTED)   (This space intentionally left blank.)
E-mail: Richard.Carlsson@REDACTED	WWW: http://user.it.uu.se/~richardc/
 "Having users is like optimization: the wise course is to delay it."
   -- Paul Graham



More information about the erlang-questions mailing list