Re: Syntax for label, NEW PROPOSAL

From: Jacques Garrigue (garrigue@kurims.kyoto-u.ac.jp)
Date: Thu Mar 16 2000 - 03:55:40 MET

  • Next message: Jacques Garrigue: "Re: Syntax for label, NEW PROPOSAL"

    From: Pierre Weis <Pierre.Weis@inria.fr>

    > > *** Proposal
    > >
    > > Objective Caml 3.00 is not yet released, and I believe we can still
    > > have modifications on this point.
    >
    > Yes, you're perfectly right, we can still modify several points.
    > However, I think there are many other points that are more important
    > than the choice of ``%'' instead of ``:'', which is only cosmetic
    > after all.

    Well, I think I've got enough reactions to rule out %.
    These are of the small details of syntax you want to check for
    sure. And that are very hard to change afterwards.

    > Thus, I would prefer to discuss deeper and more semantic problems:
    >
    > -- Problem1: labels can be reserved keywords. This is questionable
    > and it has been strongly criticised by some Caml users, especially when
    > reading in the code the awful sequence fun:begin fun ...

    Well, maybe just because I've grown used to it, I do not find it awful
    at all.

    The rationale behind allowing the use of keywords as labels is that
    there are lots of keywords in Caml, and many of them are good
    candidates for labels. Not allowing them makes often choice more
    difficult, or forces to use longer names.

    On the other hand, their particular use in the standard library may be
    another subject of discussion.
    If I remember correctly, there are only 2 keywords used as label in
    the standard library: `fun' (in functionals) and `to' (in output
    functions).

    If we want to remove them, I think a good policy would be to change
    the "default" length of labels from 3 to 4. Since many Caml keywords
    are of length 3, we avoid lots of conflicts!

    This would mean:

    fun -> func
    to -> chan or dest
    acc -> accu :-)
    src -> orig (src meant a lot to a now disappeared company)
    dst -> dest (maybe because I'm French, dst reminds me of something)
    buf -> buff ???
    ...
    Still it seems reasonable to keep pos: and len:.

    Beware however that label changes are inherently dangerous: you often
    realize afterwards that a choice was not so good, and I really hoped
    that we could definitively fix the standard library labels in 3.00.

    > -- Problem2: labels that spread all over the standard libraries, even
    > when they do not add any good.

    Well, the current policy (as defined in the manual) is to put labels
    on at least all arguments but one (with some very special exceptions
    like printf). This is necessary to allow commuting between arguments,
    which is also an important theme of labels. As such I would not say
    that they do not do any good.

    > I would cite:
    >
    > * the labels completely redundant with the types
    > (E.g. char:char in the type of String.contains or String.index)

    Following the above policy, I would say that these are not ideal
    choices, but then what other possibility is there?

    Moreover, even if this label is redundant in types, it is not
    necessarilly so in code.

    > * undesired labels: in many cases I don't want to have labels just
    > because I don't want to remember their names. (E.g., I very often
    > mispell the label acc since I've always used accu to name an
    > accumulator; furthermore, when I do not mispell this label, I feel
    > acc:accu extremely verbose). Also because labels are verbose at
    > application.

    Isn't there a classic mode for that?
    If you think that labels are verbose, you can use it, and it even
    provides some partial support for label checking when you want it.

    I suppose we should have a precise idea of what labels are there for.
    For me they are a systematic mechanism, and the fact we allow to have
    no label on one argument in function types is just a little comfort,
    particularly useful when working with functionals.

    > * labels that prevent you to use comfortably your traditional functions.
    > This is particularly evident for the List.map or List.fold_right
    > higher-order functionals.
    >
    > This last point is a real problem. Compare the usual way of using
    > functionals to define the sum of the elements of a list:
    >
    > # let sum l = List.fold_right ( + ) l 0;;
    > val sum : int list -> int = <fun>

    Whether you consider this readable or not is a question of taste. I
    personally believe that you need quite a background in HO formalisms
    to understand the above. But of course most current Caml users have
    this background.

    > # let sum l = List.fold_right ( + ) l 0;;
    > ^^^^^
    > This expression has type int -> int -> int but is here used with type 'a list

    Aargh, that's right, error messages are a pain. I believe I worked a
    lot making them informative, but there are still difficult situations.
    I'll try to improve this.

    Remark however that, in modern mode, it is already strange to have an
    application with no labels at all...

    > The real problem is that fixing the code makes no good at all to its
    > readability (at least that's what I would say):
    >
    > # let sum = List.fold_right fun:begin fun x acc:y -> x + y end acc:0;;
    > val sum : 'a -> int list -> int = <fun>

    You do not have to use begin .. end everywhere.
    I do personally write:
    # let sum = List.fold_right acc:0 fun:(fun x :acc -> x + acc);;

    Remark that here you didn't care about whether acc is the first or
    second argument of the passed function, just because (+) is
    commutative. However, in general functions are not commutative, and I
    always have to think a lot when using List.fold_left or
    List.fold_right without labels, just because I may write wrong code
    without the type checker telling me anything.

    > For all these reasons, I would suggest to carefully use labels into
    > the standard libraries:
    >
    > -- remove labels from higher-order functional
    > -- remove redundant labels: when no ambiguity can occur you need not
    > to add a label.
    > -- use labels when typechecking ambiguity is evident (for instance
    > when there are two or more parameters with the same type).
    >
    > Labels must enforce readability of code or help documenting the
    > libraries, it should not be an extra burden to the programmer and a
    > way of offuscating code.

    This seems to boil down to using labels for documentation purposes
    only. If you reduce them to such an extent, I'm afraid they will not
    do very much for code readability anymore. And I stated above, you get
    typechecking holes in functionals.

    By the way, I do not see how labels can help obfuscating code.
    What you are probably intending to say is that they make more
    difficult to write some combinator-based HO-style code. Does it mean
    we should have a version of the List module without labels for such
    uses?

    > Evidently, as any other extension, labels must not offuscate the
    > overall picture, that is they must not clobber the semantics, nor add
    > extra exceptional cases to the few general rules we have for the
    > syntax and semantics of Caml.
    >
    > In this respect, optional labelled arguments might also be discussed,
    > particularly for the following facts:
    >
    > -- syntactically identical patterns and expressions now may have
    > incompatible types:
    > # let f ?style:x _ = x;;
    > val f : ?style:'a -> 'b -> 'a option = <fun>
    >
    > As a pattern on the left-hand side x has type 'a, while as an
    > expression on the right hand side it has type 'a option

    Well, internally the left-hand side is also 'a option, but option is
    abbreviated because it is redundant. I do not think people want to see
    the left-hand side option, but maybe this technical part should be
    made more explicit in the manual.

    Conversion from 'a to 'a option is done at application. I do not
    think there is any semantical problem here.

    > -- some expressions can be only written as arguments in an application
    > context:
    > # let f ?style:x g = ?style:x;;

    ?style:x is not an expression: labels are part of the application
    node, not of the arguments.

    > -- the simple addition of a default value to an optional argument may
    > trigger a typechecking error:
    >
    > # let f ?(style:x) g = g ?style:x;;
    > val f : ?style:'a -> (?style:'a -> 'b) -> 'b = <fun>
    >
    > # let f ?(style:x = 1) g = g ?style:x;;
    > This expression has type int but is here used with type 'a option

    That one is more serious. This is not a "simple addition". Default
    values unroll some syntactic sugar, changing the type of the
    argument. Whether this is a good idea to have such syntactic sugar in
    the language is an interesting question. However I believe it provides
    some real comfort.

    > Do not forget the design decision that has always been used before in
    > the development of Caml: interesting but not universal extensions to
    > the language must carefully be kept orthogonal to the core language
    > and its libraries. This has been successfully achieved for the
    > important addition of modules (that do not prevent the users from
    > using the old interface-implementation view of modules) as well as for
    > the objects system addition that has been also maintained orthogonal
    > to the rest of the language (in particular the standard library has
    > never been ``objectified''). I don't know of any reason why labels
    > cannot follow the same safe guidelines.

    I just believed that it was agreed that labelizing the standard
    library was a progress. Of course this also means that you do not stay
    100% compatible in modern mode. Still, there are no optional arguments
    in the standard library, meaning that you stay 100% compatible in classic
    mode.

    > There are also people around that would like to keep Caml a true
    > functional language, where usage of higer order functions is easy and
    > natural. We have to be careful not to lose what is the actual
    > strength of the language.

    Of course, I agree with that.

    A first approach would be to say that one can always use classic mode.
    Modern is not pedantic, it is just another typing discipline.
    But this would endanger the unicity of the language, so this cannot be
    an universal answer.

    Now, the problem we are talking about seems to boil down to higher
    order functions in modern mode. And more particularly List.fold_left
    and List.fold_right, since these are about the only two functions
    where the argument itself has labels.

    It would cost nothing to add 4 more unlabelled functions to the List
    module.

    val foldl : fun:('a -> 'b -> 'a) -> 'a -> 'b list -> 'a
    val foldr : fun:('a -> 'b -> 'b) -> 'a list -> 'b -> 'b
    val foldl2 : fun:('a -> 'b -> 'c -> 'a) -> 'a -> 'b list -> 'c list -> 'a
    val foldr2 : fun:('a -> 'b -> 'c -> 'c) -> 'a list -> 'b list -> 'c -> 'c

    This way you can write

    # let sum l = List.foldr fun:(+) l 0

    The same additions would also apply to the Array module.
    There is also the problem of iteri and mapi, where i: may be not
    that important in practice (even when working on an array of integers,
    I wouldn't expect anybody to make such a mistake).

    val iteri : fun:(i:int -> 'a -> unit) -> 'a array -> unit
    val mapi : fun:(i:int -> 'a -> 'b) -> 'a array -> 'b array

    One may even insist that fun: is superfluous.
    Then it would probably be better to have another List module without
    labels at all. Again, this is pretty easy to do, and does not incur
    any code blow in general.

    Or do you think that the problem is deeper, and that labels are
    breaking the foundations of the language ?

    Amicalement,

    Jacques
    ---------------------------------------------------------------------------
    Jacques Garrigue Kyoto University garrigue at kurims.kyoto-u.ac.jp
                    <A HREF=http://wwwfun.kurims.kyoto-u.ac.jp/~garrigue/>JG</A>



    This archive was generated by hypermail 2b29 : Tue Mar 21 2000 - 15:11:55 MET