[erlang-questions] I think I wish I could write case Any of whatever -> _ end.

Richard O'Keefe ok@REDACTED
Wed May 19 04:41:34 CEST 2010


On May 18, 2010, at 3:05 PM, Henning Diedrich wrote:

> Richard, righteous,
>
> I was just curious about the defense you'd mount to keep ~ for the  
> frame syntax.
>
> Where the first thing that hit my uneducated I was how the use of ~  
> in it was so completely special --- as some other symbol uses in  
> Erlang are --- that it, well, may be an excellent fit. Why starting  
> to get conventional. A language proudly flying =< could also have ~  
> to mean 'something like' while really, yes, significantly different  
> from =.

I am baffled by this "so completely special" claim.
The frames report goes to some trouble to point out that this use of
~ for maplets is *NOT* innovative.  It was a straight steal from
Xerox PARC.  The Mesa programming language had an equivalent of
'make' called C/Mesa.  For Mesa's successor Cedar, this was replaced
by Eric Schimdt's "System Modeller", which took care of
  - versions
  - building
  - distribution
using a "system model" (think "Makefile on steroids") which was nothing
other than a pure lazy functional program for building systems.  (This
is the same Eric Schmidt who became CEO of Google.)  A lovely idea,
and anyone thinking about configuring, building, distributing, and
installing Erlang systems really ought to spend some time reading
Schmidt's thesis.  (Or at least the short paper about it by him and
Butler Lampson, which is a little easier to find.)

Which is where I got "~" from.  Pebble and SML (System Modelling
Language) come from the same stable.  Pebble was mainly used as a
formal notation for specifying Cedar, but it _was_ implemented.
So "~" has been used in at least two functional languages.

And it *does* standardly mean "sort of like =".  I grant you that in
Haskell it means "match this pattern lazily", and in ML it means
"negative sign" as part of a numeric literal, and in C, bizarrely,
it means "bitwise complement", but all of those are *unary* uses.
See for example http://mathworld.wolfram.com/Tilde.html.

The other thing that might make sense would be ":=" (is defined as),
although that would tend to confuse people used to sane programming
languages that don't use = for assignment.  (Fortran has much to
answer for.  FORmula TRANslator, yet they used .EQ. for equality?)

As for =<, that wasn't really a matter of choice.  Prolog was used
to manipulate logical expressions, and desperately needed <= and =>
for free use as arrows, forcing equals-or-less than to be written
=<.  Parlog, FCP, GHC, and Strand 88 all followed it.  The first
implementation of Erlang was in Prolog.  Hence much of Erlang
syntax.  But not all of it by any means.  Erlang *has* innovated.
Just not *there*.


>
> I think I already got used to the proposed frame syntax, followed  
> your arguments and support it.
>
> But I think ~ would be great to also roughly mean 'what I just  
> said'. Also ./. might do, or -"-, or *.

It is extremely difficult to believe that this is not an elaborate
practical joke.

As it happens, I _have_ used a programming language in which * could be
used in much that way:  Burroughs Extended Algol.  From the 1977 manual,
page 5-6:
	Pragmatics
	An "update assignment" can be specified with an asterisk (*)
	after the colon equal (:=) by an assignment to an
	<arithmetic variable> whose <partial word part> is empty.
	For example, "A := *+1" produces the same results as
	"A := A+1".  Updating a <subscripted variable> via this
	method is more efficient."

It _should_ have been in the syntax, not the pragmatics.
It was more efficient because it only evaluated the subscripts once.

I was always rather annoyed because this wasn't consistently carried
out:
	BA[I] := NOT *		not allowed
	RA[I] := * - 1		allowed
	RA[I] := 1 - *		not allowed
Note that
	RA[I] := * * Y + Z
is _not_ like C's ra[i] *= y+z, but like ra[i] = (ra[i]*y)+z.

That taught me to beware of "what I just said".

I mentioned Common Lisp's REPL.  SML has something similar:
	% sml
	- 6*7;
	val it = 42 : int
	- it div 3;
	val it = 14 : int
Now we have two definitions of "it", the later one hiding the
earlier.  Want to refer to the earlier?  You're stuffed.

A SINGLE "WHAT-I-JUST-SAID" IS A BAD IDEA NO MATTER HOW YOU SPELL IT.

If you are sharp-witted and realise that you need to save a value,
you can quickly name it:

	- val planets = it;
	val planets = 14 : int
	- 72;
	val it = 72 : int
	- planets;
	val it = 14 : int

In the same way, the S programming language allows assignment to
be written right-to-left
	var <- expr
or left-to-right
	expr -> var
so that if you've just typed a hairy long expression and realise
that you'll need to refer to the value again, you can tack an
assignment on the end without retyping the whole thing.

The problem is that we need to *think* about our programs, to take
bits of them, manipulate them, put them back, turn them inside out
and run them backwards, in a very fluid way.

But a "what I just said" is tightly bound to its context.
It's not just in a particular place, it's coated with thick
layers of superglue, nailed fast, and anchored with a thick
chain.  Sure, if an expression in an arm of a case refers to
a variable bound in the pattern of that arm, it still needs
that variable, but it *SAYS* what variable it needs.

> The argument about being hard to pronounce sounds a bit convenient  
> in this context. Pronouncing code is a rarified art and runs into  
> this problem time again.

"Rarefied" (note spelling) means "thin", as in the opposite of
"condensed".  As for pronouncing code, it SHOULDN'T be an esoteric
art.  We do, after all, TALK about our programs with each other.
What do we do?  Point and grunt?  I have a tongue and I'm not scared
to use it in the service of my profession.  "The telephone test" is
not of my invention.

Here's an example of an error due to the pronunciation of
characters:
	~1
is the ML way to write what you get by subtracting 1 from 0.
If you *pronounce* it "negative one", well and good.  Any ML
programmer who has got past the tyro stage will get it right.
But if you *pronounce* it "minus one", you will far too often
write
	-1
which is a syntax error, if you are lucky.  For
	f x ~1
means (f (x))(~1), while
	f x -1
means (op -)(f x, 1).  The only way I ever found to cure myself
of this was to religiously say "negative one".  (The same applies
in APL, where the "high minus" character is used for "negative".)

> But just as '=' can mean many things to different people, maybe '~'  
> can be used for both things.

= has three principal uses in programming languages:
    query: is this equal to that?
    definition: this is equal to that, now and always!
    command: change this to be equal to that (at the moment).
"Equal" is in all of them.

Maybe ~ *could* be used to mean "what I just said" or "the price
of fish in Port Stanley last Thursday" or "this CPU's current clock
frequency".  Heck, we could even use it to replace self(), which
would benefit from not being a function call.  That doesn't make
any of these a good idea.

> I thought it was a tad illogical, however, to argue that because  
> there is no obviousness to the meaning of <<< in Lisp might indicate  
> that the 'some as above/before' connotation of ~ should likewise and  
> somehow guilty by association be irrelevant or even  
> counterproductive when deciding about. It's use.

What I was arguing was perfectly logical: a hack can be turned into a
principle if you repeat it *consistently*, but the Lisp * ** ***
convention is not repeated *consistently*.  (The key one was not the
missing <<< but the missing -- and --- .)

To put it another way: you *can* demand that people learn some weird
new convention, BUT you have to make it pay off for them.  Isolated
hacks don't pay off for your readers.

> Not that I believe that anyone cares, but eye have a hobby horse  
> that had me thinking about hacking the pre-compiler already: the  
> doubling of function signatures where the variation that is called  
> in the former is, plus one default argument as literal, exactly the  
> head of the next. I might be doing something wrong there but I keep  
> copying heads and it makes the code hard to read. I regularily go,  
> copy a head, paste it three times (one over the old ) and alter two  
> of them marginally. A nice remedy could be:
>
> Fun(A) -> Fun(A, b).
>
> Fun(A,B) ...

Believe it or not, but this is covered by an old proposal of mine.
My adaptation of Paul Lyons' "split procedure heads" covers both
keywords and optional arguments.

Lisp has	(defun f (a (&optional b 'B)) ...)
S (R) has	f <- function (a, b = "B") { ... }
Ada has		function F(A : T1; B: T2 => "B") is ...
C++ has		T0 f(T1 a, T2 b = 'b') { ... }

Notice a pattern about where the default value goes?
\
>
> This could become
>
> Fun(A) -> ~~ b.

That puts the default value where the body belongs,
and means that you only get ONE default argument.
Two strikes against it to start with.

If I wanted this often, I might use M4, and define

optional(HEAD, E1, ..., En)

to expand to

	HEAD') -> HEAD'(E1).
	HEAD' _V_1) -> HEAD' _V_1, E2).
	...
	HEAD' _V_1, ..., V_n_minus_1) -> HEAD' (_V_1, ..., _V_n_minus_1, En).

where HEAD' is HEAD without its trailing ),
which would be a pretty trivial piece of M4 code, but would
let you write

optional(foobar(A,B), A+2, 42).
foobar(A, B, C, D) -> ...

and have it expand to

	foobar(A,B) -> foobar(A,B, A+2).
	foobar(A,B, _V_1) -> foobar(A,B, _V_1, 42).
	foobar(A, B, C, D) -> ...

Hmm.  I don't quite like that interface.  Maybe

optional(F(A1, ..., An))

where Ai is either Var or Var=>Expr

optional(foobar(A, B=>A+2, C, D=>A*B+C))

That looks like it might work.  I did use a special symbol here,
but (note again the absence of innovation) it's the same one Ada
uses.  Come to think of it, since Ada uses => for records as well
as optional parameters, it would make sense to use the same
symbol in Erlang, and use Var ~ Expr for defaults.

Last draft today:

optional(foobar(A, B ~ A+2, C, D ~ A*B+C))

In any case, no actual change to Erlang is involved.
It would also be possible to hack on the Erlang parser
to recognise
-optional foobar(A, B ~ A+2, C, D ~ A*B+C).
but I'd want to prototype it with M4.

>
> Sorry there is no relation to the negation connotation that ~  
> somehow brings along. But that seemed to be alright for its use in  
> io:format in the first place, didn't it? Also, somehow, it negates  
> writing code doubly.

If you're a C programmer, sure, ~ says bitwise negation.
In Lisp, it doesn't.  Not even close.  And that's where 'format'
comes from.  (Possibly via Prolog.)  In _Erlang_, the only use
of "~" is in formats, and there isn't the slightest whiff of
negation about it anywhere.




More information about the erlang-questions mailing list