To: Pierre Weis <Pierre.Weis@inria.fr>
Subject: Re: convincing management to switch to Ocaml
From: John Prevost <prevost@maya.com>
Date: 30 Aug 1999 20:13:57 -0400
In-Reply-To: Pierre Weis's message of "Mon, 30 Aug 1999 10:02:52 +0200 (MET DST)"
Pierre Weis <Pierre.Weis@inria.fr> writes:
> I don't want to start a flamewar on syntax, but as a computer science
> teacher and Caml implementor and designer, I'm interested at those
> facts you mentioned about the syntax of the language, since I just
> think the opposite way: I find the Caml precedence rules pretty
> convenient, easy to teach, and fairly easy to remember since
> absolutely intuitive and natural (provided they have been explained to
> you and you have understood the design ideas).
>
> So, there is something interesting to understand here, could you
> elaborate a bit on your difficulties on precedences and especially
> about ``all the brackets'' that make the code hard to read and hard to
> write ?
I had a similar problem with SML and Caml until I realized the one
important rule that makes things clear: The juxtaposition
(application) operator has the highest precedence. There are two
places this can hurt you, if you don't understand it yet:
let f_of_sum = f x + y
vs
let f_of_sum = f (x + y)
This one's actually pretty simple to figure out--it doesn't take long,
though people tend to feel unsure and write:
let sum_of_f = (f x) + (f y)
constructions when they don't have to.
(And of course, C programmers get bitten by wanting function
application to look like f(arg), rather than f arg. It doesn't help
that oftentimes intro material takes this tack, then moves away from
it, leaving lingering uncertainties about f(x) in the student's mind.)
The other problem, which is more significant (although it causes more
problems in SML) is constructors. When I first learned ML, it wasn't
made obvious to me that the application of a value constructor is
exactly the same as any other function. In fact, it seemed that it
shouldn't be, since you can do pattern matching on one and not on the
other.
The painful part of this shows up mainly in patterns like the following:
let f = fun (Foo x) (Bar y) (Baz z) -> ...
It's not so much of a problem in Caml, since the above sort of pattern
is rarely useful, but in SML, where using multiple cases and multiple
patterns at the same time is possible, it makes for confusing
behavior:
fun f (Int a) (Int b) = Int (a + b)
| f (Int a) (Flt b) = Flt (float a + b)
| f (Flt a) (Int b) = Flt (a + float b)
| f (Flt a) (Flt b) = Flt (a + b)
When I first tried to do something like the above, my first mistake
was to try something like this:
fun f Int(a) Int(b) = Int (a + b)
But this doesn't work, of course. I then tried various things, until
I got:
fun f (Int a) (Int b) = Int (a + b)
At this point, I decided "Oh, I'll just write everything I can like
Lisp!" and tried to do the following for consistency:
fun f (Int a) (Int b) = (Int a + b)
Which of course, doesn't work.
Summary: It's important for introductory material to point out that
" " is an operator, similar to others, and how it works precedence-wise.
It's also important to point out that constructor application in both
expressions and patterns acts exactly the same as function
application. This makes things much clearer. You can then also point
out useful things like the fact that , binds less tightly than
anything else, so you can say (long expression, long expression) to
form a tuple., and that similar rules apply to other things.
Juxtaposition as application is in some ways the most foreign bit of
syntax for a C-style language programmer.
John.
This archive was generated by hypermail 2b29 : Sun Jan 02 2000 - 11:58:25 MET