RE: Syntax for label

From: Don Syme (dsyme@microsoft.com)
Date: Tue Mar 14 2000 - 19:28:34 MET

  • Next message: Max Skaller: "Re: additions to standard library?"

    But it doesn't break any _existing_ code, does it? I am assuming that
    existing uses of "as" would indeed coincidentally create labelled functions,
    but because existing code always uses the default ordering for arguments,
    they would never be used as such, and nothing would break. The full
    original email is attached.

    Cheers,
    Don

    P.S. I guess I'm assuming that the existence of labels can effectively be
    thrown away during signature restriction, i.e. existing code declares

      let foo ((x,y) as z) = ...

    and restricts it to

      val foo : (int * int) -> ...

    without a label. I would imagine this is something you have anyway so you
    can add labels to function without breaking existing code.

    -----Original Message-----
    From: Pierre Weis [mailto:Pierre.Weis@inria.fr]
    Sent: 14 March 2000 18:05
    To: Don Syme
    Cc: caml-list@inria.fr
    Subject: Re: Syntax for label

    > And why not "as", i.e. "t as x", as I explained once in a previous post.
    > I never did get an explanation as to why this wasn't an better solution.
    It
    > reuses a rarely-used keyword in a perfectly backward-compatible way.

    This is not backward-compatible since ident1 as ident2 is a special
    case of pattern as ident. Hence ident1 as ident2 already has a
    meaning in Caml: it means that you want to write ident2 as an alias
    for ident1.

    Best regards,

    -- 
    Pierre Weis
    

    INRIA, Projet Cristal, http://pauillac.inria.fr/~weis

    ----- Message-ID: <740296195051D111BEC900805FFE65F3F52F6A@RED-PFS-02> From: Don Syme <dsyme@microsoft.com> To: 'John Prevost' <prevost@maya.com>, Jacques.Garrigue@inria.fr Cc: caml-list@inria.fr Subject: RE: What will the new syntax be like? (O'Caml + O'Labl) Date: Tue, 7 Dec 1999 07:36:49 -0800 X-Mailer: Internet Mail Service (5.5.2651.58)

    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 : Wed Mar 15 2000 - 08:48:58 MET