RE: What will the new syntax be like? (O'Caml + O'Labl)

From: Don Syme (dsyme@microsoft.com)
Date: Tue Dec 07 1999 - 16:36:49 MET


From: Don Syme <dsyme@microsoft.com>
To: "'John Prevost'" <prevost@maya.com>, Jacques.Garrigue@inria.fr
Subject: RE: What will the new syntax be like? (O'Caml + O'Labl)
Date: Tue, 7 Dec 1999 07:36:49 -0800

Ignoring the label question, allowing or requiring an explicit keyword for
"optional" and "default" would seem appropriate. This fits with "mutable" -
we don't have a wierd obscure syntax for that, after all, and that feature
is probably used about as often as these will be.

let foo bar:a zub:b (optional qux:c default 10) woz:d () =
  match d with
  | None -> a + b + c
  | Some x -> a + b + c + d

One answer to the label question might be to reuse "as". This is
particularly plausible because labelled parameters now do not have to be
assigned labels at the callsite, so this would not break existing uses of
"as". Another nice thing may be that the label comes second: OCaml seems
to have made the "right" choice by putting the name after the "as", rather
than before like SML. I can't quite express _why_ this is the right choice,
but again and again it feels right to me when I use it in practice. The
same may turn out to be true for labelled arguments.

let foo ((e,f) as bar) (b as zub) (optional c as qux default 10) (d as woz)
() =
  match d with
  | None -> a + b + c
  | Some x -> a + b + c + d

The brackets may not be needed, though I'm not sure...
let foo (e,f) as bar (* comment *)
        b as zub (* comment *)
        optional c as qux default 10 (* comment *)
        d as woz (* comment *)
        () =
  match d with
  | None -> a + b + c
  | Some x -> a + b + c + d

I guess the interpretation of
let f = function
  | None as foo -> 0
  | Some x as foo -> x

would also be a labelled function, though one might require the "as" name to
be the same on each branch of the match in order for it to be considered
labelled.

Don

-----Original Message-----
From: John Prevost [mailto:prevost@maya.com]
Sent: 07 December 1999 07:36
To: Jacques.Garrigue@inria.fr
Cc: caml-list@inria.fr
Subject: Re: What will the new syntax be like? (O'Caml + O'Labl)

Jacques Garrigue <Jacques.Garrigue@inria.fr> writes:

> Changes are rather small:
>
> let foo bar:a zub:b ?qux:c{=10} ?woz:d () =
> match d with
> | None -> a + b + c
> | Some x -> a + b + c + d
>
> Notice that you need a non-labeled argument after optional ones, due
> to a change in semantics. Optional arguments are now only discarded if
> the function is applied to a non-labeled argument appearing after them
> the function type.
> This change was necessary to resolve some ambiguities, and provide an
> untyped semantics for the language.

Hmm. This seems like a pain, but I guess I can see the need. I
always was a little leery of the optional arguments' semantics.

Another thing I've noticed is that non-optional labeled arguments may
have their labels left off. I wholeheartedly approve of this, since
this was one of my gripes with O'Labl. (Why should I need to say:
"List.map fun:f l" instead of "List.map f l"? In many cases, the
label just gets in the way, though it is of help if you're really
going for clarity or you don't know the "standard" order for things
like map.)

The final thing I've noticed in the development version is that I'm
not able to apply labeled arguments out of order. Is this
intentional, or a side-effect of ongoing work to unify O'Labl and
O'Caml?:

let add :a :b = a + b

# let add :a :b = a + b;;
val add : a:int -> b:int -> int = <fun>
# add b:5 a:3;;
Expecting function has type a:int -> b:int -> int
This argument cannot be applied with label b:

Labels are probably still useful without the potential for
re-ordering, but it does cut down on the utility. One of the uses I
have for labels is when I have two arguments to a function (say,
List.map), and I want to re-order to make the larger one second. That
is, it's generally useful for map's first argument to be the function,
but sometimes I want to be able to say:

List.map (short expr of list)
  fun: (long expr
        for the function
        which keeps going)

This follows the natural language model that large complex phrases are
generally not put between a short phrase and the binding phrase--they
reposition to be out of the way. Having the power to add a short
modifier (fun:) and reorder the expression is a great boon.

> You may like or not the above notation for labels. We had discussions
> here, and the conclusion was that putting spaces around type
> annotations was a good thing anyway...

> It's hard to put that many new features in the language, keeping the
> compatibility with all, and obtain a clean syntax. I must admit.
> However I think that ocaml3 syntax is reasonable enough, at least
> better than olabl's. A second attempt has its advantages.
>
> By the way, the new version is not yet released, so if you have a
> coherent syntax to propose for the new features, you can propose.

Well, I did some thinking this evening, and here's a summary of where
I went with it. (I've got what I think is a reasonable and slightly
cleaner proposal near the end, along with a "to my eyes" greatly more
pleasing proposal which would require more parser and lexer changes
(though nothing incompatible).)

The first thought I had was "these optional argument things would be
nicer to read if you could use a keyword to express the optionalness,
rather than a single symbol, and it would also be nice if the equals
sign were more visually bound with the argument". This led to a
syntax that I feel is too heavy--but I'll show it anyway. My examples
are based on the following very simple set of functions (in the
current proposed O'Caml 3 syntax):

let div1 num:x den:y = x / y
let div2 :num :den = num / den
let div3 :num ?:den () =
   let v = match den with Some y -> y | None -> 2 in num / y
let div4 num:x ?den:y {= 2} () = x / y

Here's the heavy syntax:

let div1 {| num = x |} {| den = y |} = x / y
let div2 {| num |} {| den |} = num / den
let div3 {| num |} {| optional den |} () = ...
let div4 {| num = x |} {| optional den = y = 2 |} () = x / y

The first problem is seen in div4, where we see that there's a problem
if we wanted to not include the explicit variable binding--I see no
clean way to solve this without an extra special character, which I
was trying to avoid.

The second problem is the weight of {| ... |}, which I thought might
be resolved with:

let div1 {| num = x; den = y |} = ...

and so on. And a similar syntax for calling. But this is alien from
the point of view of function application. So, I dumped this.

The second thing I looked at was the set of "special characters" used
and not used by O'Caml:

Special characters (non-grouping): ~ ` ! @ # $ % ^ & * ' " , . / ? = + \ | -
_
Used by O'Caml (library or special): ! @ # $ % ^ & * ' " , . / = + | - _

O'Caml does use ??, but in a very specific circumstance. I include $
in the specials list because it's used by camlp4.

This leaves us with: ~ ` ? \

Of these, only ~ is allowed as part of a symbolic ident already.

Which leaves: ` ? \

My proposal is that we should avoid further obfuscating the use of
":". (Not only is it the type operator, but "::" is cons. And
there's already confusion between the Haskell and ML families in that
the Haskell family does the exact opposite. No need to add to the
trouble.)

In order to do that, I first picked "\" as the character of
choice--for two reasons: First, it's a reasonable "separator"
character, like ":" is. Second, it's used for absolutely nothing at
the moment. This leads to the following syntax (keeping default
values the same, and changing only the character used.)

let div1 num\x den\y = x / y
let div2 \num \den = num / den
let div3 \num ?\den () = ...
let div4 num\x ?den\y {= 2} () = x / y

as a further refinement, we can unify the use of \ and ? in the
following way:

let div1 num\x den\y = x / y
let div2 \num \den = num / den
let div3 \num ?den = ...
let div4 num\x den?y {= 2} () = x / y
or
let div4 num\x ?den\y {= 2} () = x / y

this last bit is possible since the O'Caml 3 syntax will not allow
unlabeled optional arguments because of the extra unlabelled argument
constraint you mentioned above.

A final possibility, which I think has great merit but may be hard to
parse, is:

let div1 (\num x) (\den y) = x / y
let div2 \num \den = num / den
let div3 \num ?den () = ...
let div4 (\num x) (?den y = 2) () = x / y

I like this last one, since it allows whitespace between the label and
the expression carrying the value, and allows the = to be used in a
more natural way with the label. Use follows the same pattern:

div4 (\num 5) (\den 5) ()
div2 (\num 5) (\den 2)
div1 (\den 2) (\num 5)

But the parsing, as I said, could be a pain.

Finally, there's the question of type syntaxes for these. I propose:

val div1 : num\int -> den\int -> int
val div3 : num\int -> den?int -> unit -> int

or

val div1 : num / int -> den / int -> int
...

(Since / doesn't have to bind the same in types, and "int list" looks
bad as "foo:int list" or "foo\int list", but okay as "foo \ int list")

or

val div1 : \num int -> \den int -> int
val div3 : \num int -> ?den int -> unit -> int

(For the (\label expr), (\label pattern) style.)

Finally, if "\" is rejected for labels, I suggest it be used in place
of "`" on variants, since it's much more distinctive than "`" is:

let (+) x y = match (x,y) with
  | `int x, `int y -> `int (x + y)
  | `int x, `float y -> `float (float x +. y)
  | `float x, `int y -> `float (x +. float y)
  | `float x, `float y -> `float (x +. y)

vs

let (+) x y = match (x,y) with
  | \int x, \int y -> \int (x + y)
  | \int x, \float y -> \float (float x +. y)
  | \float x, \int y -> \float (x +. float y)
  | \float x, \float y -> \float (x +. y)

This is of course incompatible with using \ as the label character.

In summary, I think the style which is most aesthetic is that with
(\label expr), (\label pattern), "\label type" where "\" alternates
with "?" rather than adding to it.

I'm perfectly willing to rework the parser and lexer to use this
style. I can also work my way through the camltk source, but that
will take longer.

John.



This archive was generated by hypermail 2b29 : Sun Jan 02 2000 - 11:58:29 MET