the Hopwood design process

Richard A. O'Keefe ok@REDACTED
Tue Mar 14 23:45:12 CET 2006


	Second, and more generally, I think new features would
	fare better if we had some language principles to tell
	us what new features strengthen the language, and
	which diffuse it.

In this case it's an easy decision:  the preprocessor is violently
at odds with everything else in the language.  One of the papers I
wrote for SERC had the title "Delenda Est Preprocessor".  There
really isn't anything that can be done with the processor that
could not be done better without it.  In particular, one of the
major things about Erlang is the module system combined with hot
loading, but the preprocessor subverts the module system and
causes dependencies between source units that are not and cannot
be tracked by the run time system.

This DOESN'T mean that abstract patterns are right, but it DOES
mean that records done via the preprocessor are wrong.  In
particular, it doesn't mean that record *syntax* is wrong (although
I believe it muddles up three different things that would be better
separated, so that some of those things could be more widely
available), it means that *accessing* record definitions via the
preprocessor is the wrong way to do it.

I was rather against funs when they first appeared; I was in the middle
of writing a paper about how one would infer certain behavioural
properties of functions in Erlang, and with the introduction of funs
you just *couldn't* any more.  But by this criterion they are a good
thing:  they make higher order functions easier to define and use and
thereby make code easier to get right first time and to maintain
thereafter, and they are certainly in keeping with Erlang's functional
spirit.

List comprehensions are not quite as clear a case as funs.
To this day SML manages without them.  On the other hand, whenever I
have to write SML I am greatly annoyed by their absence.  List
comprehensions are local, concise, and intention-revealing.

In another thread we've been arguing about a notation which,
in the form
    ( Result_Expression
      where P1 = Init1 then Step1, ..., Pn = Initn then Stepn
    || generators and filters
    ),
I have come to think _might_ fit Erlang quite nicely.  Again, it is
local, concise, and intention-revealing, and as the overall
structure <open> <result stuff> ... || <generators and filters> <close>
is similar to the structure of list comprehensions, it at the very
least doesn't *conflict* with the current language.  And it has the
very nice (E0 where P1 = E1, ..., Pn = En) local binding form as a
special case, and even (E0) as a special case!  I now regard it as
settled that this would strengthen Erlang and not diffuse it, but
the *practical* question "would it strengthen Erlang ENOUGH to be
worth doing" remains.  For example, I looked in the sys_core_dsetel
module.  The first thing I saw that looked like a candidate for this
new form was bind_vars/2, but it had loop dependencies that made the
new form inapplicable.  The second thing that looked like a candidate
for this new form was visit_pats/2, and that one worked.  Until I
realised that visit_pat/3 called back to the visit_pats/3 function I
had just eliminated.  The first thing where the new form *could* be
used was

    restore_vars([V|Vs], Env0, Env1) ->
	case dict:find(V, Env0) of
	    {ok, N} ->
		restore_vars(Vs, Env0, dict:store(V, N, Env1));
	    error ->
		restore_vars(Vs, Env0, dict:erase(V, Env1))
	end;
    restore_vars([], _, Env1) ->
	Env1.

which becomes

    restore_vars(Vs, Env0, Env1) ->
	(Env where Env = Env1 then case dict:find(V, Env0)
				     of {ok,N} -> dict:store(V, N, Env)
				      ; error  -> dict:erase(V,    Env) end
	|| V <- Vs).

Given that the original code could have been written as

    restore_vars([], _, Env1) -> Env1;
    restore_vars([V|Vs], Env0, Env1) ->
	restore_vars(Vs, Env0, case dict:find(V, Env0)
	                         of {ok,N} -> dict:store(V, N, Env1)
	                          ; error  -> dict:erase(V,    Env1) end).

or as

    restore_vars(Vs, Env0, Env1) ->
	lists:foldl(fun(V, Env) -> case dict:find(V, Env0)
	                             of {ok,N} -> dict:store(V, N, Env)
	                              ; error  -> dict:erase(V,    Env) end
		    end, Env1, Vs).


and that this was the only one of three apparent candidates where the
new notation could in fact be used, there doesn't seem to be much of a
payoff in that particular module.

This is a case where I suggest that an empirical study of how often
the new form could be used and what difference it would make to readability
is needed.  We can't always make a decision just on how well something fits.




More information about the erlang-questions mailing list