[erlang-questions] Erlang and syntax.

Richard A. O'Keefe ok@REDACTED
Wed Feb 26 06:06:11 CET 2014


On 26/02/2014, at 4:35 PM, Siraaj Khandkar wrote:

Let's put it this way: the issues I discussed were
(a) verified using the latest available O'CAML manual and
(b) amongst but far from the only features of O'CAML that
    had me unable to bear to use it.

O'CAML has always been famed for the excellent performance
of its compiler, and I used to cherish a daydream of using
its preprocessing facilities to give it a syntax I liked..

F# has a number of merits -- which is why I have it on my
machine -- including native threads and some very interesting
innovations.  It actually has two syntaxes.  The syntax is,
however, very complex.

>> There are two kinds of lambda expression:
>> 
>> 	fun Pat+ [when Guard] -> Expr
>> 
>> may have multiple *arguments* but not multiple *clauses*,
>> while
>> 
>> 	function [|] Pat [when Guard] -> Expr
>>               {  |  Pat [when Guard] -> Expr }...
>> 
>> may have multiple *clauses* but not multiple *arguments*.
>> 
>> The calls to loop have parentheses around the arguments
>> (to make a tuple, because a function with multiple clauses
>> can't have multiple arguments), but the pattern matches
>> _don't_ have the parentheses.
> 
> This describes the form, but does not make any judgements. Why is that
> so terrible? I don't see the problem.

Surely nobody expects a syntax to be _capable_ of making judgements.
I don't understand you here.

The problem is having two different ways to do the same kind of
thing, both of them crippled, and neither having a name that
reminds you of the direction of its crippling.

Back when I was trying to write O'CAML, I banged my head
on this over and over again:  I wanted to write one simple
function with
 - no tuples,
 - multiple arguments, and
 - multiple clauses.

Here's a simple little Erlang function:

    fun (a, b) -> 1
      ; (a, _) -> 2
      ; (_, b) -> 3
      ; (_, _) -> 4
    end

Writing that is not simple in O'CAML, and
there doesn't seem to be any *REASON* for the restrictions.


> Sure, I can entertain the idea that having an additional form can be
> seen as less than perfectly symmetric,

We're not talking about "less than perfect", we are talking
about *unmotivated crippling*.  We're talking about having
to remember than 'fun' does multi-arg but not multi-case
and 'function' does multi-case but not multi-arg.
(If 'function' were spelled 'matchfun' it would then
*obvious* which one was like 'match' and which wasn't.)

>  There's never a
> situation where one _has_ to use the form "function" over "fun", it is
> provided merely as an alternative that you _may_ find convenient at
> times.

No, 'function' is not "merely an alternative".
'fun' and 'function' do _different_ things, and
neither of them does a full job.

Let's look back at that little Erlang function.
The closest I can come in O'CAML is

   fun x y ->
       match x with
          | A -> match y with
                   | B -> 1
                   | _ -> 2
          | _ -> match y with
                   | B -> 3
                   | _ -> 4

If I'm going to have to twist my head around something
complicated in a language, I'd rather it was something
like type families than pointlessly restrictive syntax.


> 
> Though, frankly, I'm not fully satisfied with function declaration in
> any language that I'm familiar with so far. I mostly lean toward the way
> it is done in OCaml, because it reinforces the idea that a function is
> no different from any other value, so you bind it to a name in exactly
> the same way (let name = expr), where as in SML there's a distinction
> between (val name = expr) and (fun name arg1 .. argn = expr).

But in SML nothing stops you writing

    val f = fun ... -> ...

if you want to.  Turning your own words against you,
'fun' in SML is "merely an alternative syntax that you
may find convenient".

> And, before you bring it up as a gold standard, I find Haskell's way
> quite unsatisfactory as well. Primarily in two ways: 1) all bindings are
> _implicitly_ recursive and 2) no shadowing (which I know, from other
> discussions, that you hate, but that is a preference...).

The implicit recursive bit -- which is what we also have in
Erlang -- is in practice what I always want, and it is strongly
connected with the free order of function definitions -- which
we also have in Erlang.

_Not_ having to have a weird special form for declaring
a group of mutually recursive functions is one of the
things that had me running from SML to Haskell (and Erlang!).

> There has been a lot of interesting progress in both, the OCaml
> language and ecosystem, since the last time you seriously looked at it
> (which I can only imagine must have been a while):

There are some incredibly interesting tools I would like to use
that are written in O'CAML.  I even went to the trouble of
setting up a special VirtualBox VM running a specific version
of Linux to run one of them.  That was last year.
> 
> - recursive modules
> - private types (which differ from abstract types by allowing reading
> but still not constructing the representation)
> - locally abstract types (which allow "shared secrets", that Harper
> said were only possible in SML [2])
> - first-class modules
> - GADTs!
> - a really awesome package manager [3]

Those are all nice things, although I note that the Goedel
programming language had GADTs back in the 1980s -- mainly
because John Lloyd couldn't think of a reason _not_ to
offer them.  He wasn't aware of the type inference issues
they cause, but since Goedel did type checking, not type
inference, that wouldn't have been a problem anyway.

I have kept going back to O'CAML because of the fantastic
things that have been done with it, but I have really really
bad memories of GODI.  If OPAM lets me forget about GODI,
I may try O'CAML again.

>>>>    (S where S = 0 then S+X*Y || X <- Xs & Y <- Ys).
>>> 
>>> Where is S coming from here?
>> 
>> From the 'where' part.  It's
>>   ( <result> where <binding>,... || <generators and tests> )
>> and a <binding> is
>>   <pattern> = <initial value> [then <subsequent value>].
>> 
>> 'let P = E0 in E1' and 'E1 where P = E0' are traditional
>> alternatives in functional programming.
>> 
>> It's a cross between Lisp's DO (the <binding> part and the
>> <result> part) and comprehensions (the <generators and tests> part).
> 
> I really like the idea of being able to express "X <- Xs & Y <- Ys",
> but, even with the explanation, I still find the left hand side quite
> unintuitive.

Familiarity again.

First, multiple generators in parallel is a straight
steal from Clean.
Second,
<expression> where <bindings>
was back in Landin's "The next 700 programming languages" paper
and has appeared in several programming languages since ISWIM.

The proposed syntax has (Expression where Matches)
as a special case.  Having used both 'let' and 'where' in
Haskell, I find that

	roots a b c = (-b + sqrt discrim)/(2*a)
          where discrim = b^2 - 4*a*c

reads better than

	roots a bc =
	    let discrim = b^2 - 4*a*c
	     in (-b + sqrt discrim)/(2*a)

In Haskell, 'let' and 'where' do slightly different things
('let' is local to an expression, 'where' goes with a
group of guarded alternatives in the same clause).

I tried (let <bindings> [while <guard>] [|| <generators and tests>]
          in <result>)
but found that it was more awkward.

What really matters is that we _could_ have a form that does
for fold+map+filter what list comprehensions do for map+filter.









More information about the erlang-questions mailing list