Version française
Home     About     Download     Resources     Contact us    
Browse thread
Re: [Caml-list] Future of labels
[ Home ] [ Index: by date | by threads ]
[ Search: ]

[ Message by date: previous | next ] [ Message in thread: previous | next ] [ Thread: previous | next ]
Date: -- (:)
From: Patrick M Doane <patrick@w...>
Subject: Re: [Caml-list] Future of labels, and ideas for library labelling
Hi Jacques,

Thanks for your quick response.  I appreciate what you are trying to do,
and evolving the language to include labels is a good thing.  I am really
undecided on the issue and have been trying to take a more conservative
approach since most discussion so far has been, as you say, even more
extreme.

On Tue, 3 Apr 2001, Jacques Garrigue wrote:

> > I see that labels are useful when working with functions with a large
> > number of functions or default arguments. This problem can also be solved
> > nicely when the language supports shared field names among record types.
> > I think it would be much more useful to fix this problem than to focus on 
> > labelled arguments. Even better, support light-weight extensible records
> > with labels as first class values. Then we could build a really good
> > implementation of a relational algebra.
> 
> I'm not really convinced of that. SML for instance has structural
> records, and uses them in its basis library. You can look at the result
> for yourself. For me it's only half the way.
> * You cannot mix labeled and unlabeled arguments, whereas you often
>   want to keep one unlabeled argument in your function.
>   So you only get a weak form of commutation.
> * I believe labels + currying give a nicer syntax. With tupled/record
>   arguments it is much less natural to write a function inside a
>   functional call.
> * You would of course have to extends records with optional fields,
>   with creates new problems of overloading.

I think that we're in agreement here.  Structural records like SML is a
start, but light-weight extensible records would address most of these
issues.

  - Labeled arguments are included in a record, and unlabelled arguments
are outside of the record.  And since they are light-weight, there is no
need to declare a type definition for the record. 

  - Labels and currying do give a nicer syntax.  On the other hand, not
having good support for records is a detriment to the syntax. Most
programmers coming to Caml will have an expectation that fields can belong
to more than one record.  Luckily, labelled arguments removes at least one
reason for light-weight records. 

  - Optional arguments can be addressed by having a value which
contains the default arguments which is modified by a record update
syntax.

> Another way to see it is to compare Perl/Tk or Python/Tk with Tcl/Tk.
> I think Tcl/Tk is nicer, even with all the ugliness of Tcl.

I agree.  Working with labels in the various GUI bindings has been
pleasant.  In fact, I remember reading a paper by Ousterhout where he
showed why strongly-typed languages were a bad idea.  All of examples came
down to showing how the programmer could type less in TCL but express the
same idea when compared to Java or C++.  But it took up the same amount
(or less) in O'caml.

> Note that I've switched to List.fold_left, which is tail recursive,
> but you cannot do it with your approach.

As an aside, it's unfortunate that Caml implements fold_left with the
"wrong typing".  It would be much more useful for fold_left to have the
same type as fold_right but still be tail recursive.  This would allow for
more natural folding compositions with other fold operators (e.g. Set). 

> I have also eta-expanded more than necessary, to make the code more
> readable.
> We can write something shorter if we use labels as they were in ocaml
> 2.99, before their trimming:
>    List.fold_left lists ~acc:IntSet.empty
>      ~f:(List.fold_left ~f:(fun ~acc item -> IntSet.add acc ~item))
> Now, which is the more readable program, this one or yours.

Hmm... hard to say. Using this API, I would wonder why isn't there a label
for the list? It would be much simpler if every argument to a function had
to have a label.  I know this presents its own problems, but it adds to
the things one must remember for coding.

If the desire is to move towards labels, it may be worthwhile to consider

  let f x y z = ...

to be the same as

  let f ~x ~y ~z = ...

> Honestly, using folding functions without labels and eta-expansion is
> just a nightmare to decrypt. For me it was really revealing to hear
> from program transformation researchers in Haskell (who use fold for
> their transformations) that they didn't use it directly in actual
> programs.

I think it is a matter of background and taste. I quite frequently use
folding functions in actual programs, but this good information to be
aware of.

Regarding Haskell, what you say is surprising since I would classify
a common use of monadic binding as a folding function. For the operator
(>>=) which has type

       (>>=) :: m a -> (a -> m b) -> m b

if a and b are the same type than it is a folding over computation state.
This is certainly a common use in real programs.

> Another problem about fold without labels is that I often use it
> with two lists as parameters, and if you pass them in the wrong order
> you have a well-typed utterly wrong program.
> Can you tell at first sight if this code is correct or not?
>
> 
> List.fold_left (fun l x -> if List.mem x l then l else x::l) [1;2;1] []

This is a definite advantage of labels.  

Forgetting to use the label in commuting mode can lead to incredible
cryptic error messages. As discussed above, I like to use a variant of
list folding that gets the arugments in the "right" order and is
tail-recursive. I tried adding labels to the arguments to see what would
happen when they were omitted.  The result was pretty surprising: 

val listFold : f:('a -> 'b -> 'b) -> l:'a list -> acc:'b -> 'b

let cons x l = x :: l

listFold cons (listFold cons [5;4;3] []) [];; 

While this is a contrived example to reverse a list twice, not including
the labels gave this impressive type back from the compiler:

- : f:('a ->
       (('b -> 'b list -> 'b list) ->
        (f:('c ->
            (('d -> 'd list -> 'd list) -> int list -> 'e list -> 'f) ->
            ('d -> 'd list -> 'd list) -> int list -> 'e list -> 'f) ->
         l:'c list ->
         acc:(('d -> 'd list -> 'd list) -> int list -> 'e list -> 'f) ->
'f) ->
        'g list -> 'h) ->
       ('b -> 'b list -> 'b list) ->
       (f:('c ->
           (('d -> 'd list -> 'd list) -> int list -> 'e list -> 'f) ->
           ('d -> 'd list -> 'd list) -> int list -> 'e list -> 'f) ->
        l:'c list ->
        acc:(('d -> 'd list -> 'd list) -> int list -> 'e list -> 'f) ->
'f) ->
       'g list -> 'h) ->
    l:'a list ->
    acc:(('b -> 'b list -> 'b list) ->
         (f:('c ->
             (('d -> 'd list -> 'd list) -> int list -> 'e list -> 'f) ->
             ('d -> 'd list -> 'd list) -> int list -> 'e list -> 'f) ->
          l:'c list ->
          acc:(('d -> 'd list -> 'd list) -> int list -> 'e list -> 'f) ->
'f) ->
         'g list -> 'h) ->
    'h
= <fun>

I think this is more than enough to frighten me from using commuting label
mode.  The classic mode seems to give more sensible error messages.

Another language that I'm very familiar with that has labelled arguments
(in some sense) is Verilog.  Instantiation by named connection is
really crucial to using the language safely. Some observations though:

  1) There are no structures in the language, so some need of labelling is
needed.

  2) There is no partial application which makes it easier to give
intelligent error messages.

Hope there's some good thoughts in there for you.
Patrick

-------------------
To unsubscribe, mail caml-list-request@inria.fr.  Archives: http://caml.inria.fr