caml-list - the Caml user's mailing list
 help / color / mirror / Atom feed
* generalization in tuples
@ 2000-10-16 12:42 David Monniaux
  2000-10-17  8:11 ` Didier Remy
  0 siblings, 1 reply; 3+ messages in thread
From: David Monniaux @ 2000-10-16 12:42 UTC (permalink / raw)
  To: Liste CAML

(En français: pourquoi ne pas généraliser le type d'un tuple pour cause de
non-généralisabilité d'une des parties du tuple si de toute façon les
variables de type à généraliser n'y interviennent pas?)

Some typing problem has been bothering me for the week-end.

Let us consider the following code:
# let extractor f = f ~foo:"aaa" ~bar:"bbb";;
val extractor : (foo:string -> bar:string -> 'a) -> 'a = <fun>
# let grostruc = List.map string_of_int [1; 2; 3];;
val grostruc : string list = ["1"; "2"; "3"]
# let zoinx = grostruc, extractor;;
val zoinx : string list * ((foo:string -> bar:string -> 'a) -> 'a) =
  ["1"; "2"; "3"], <fun>

Very logical, and what I wanted: a tuple with some big computed stuff in
the first member and a polymorphic function in the second (this is of
course a simplified example of the actual production code).

Now I do not want to pollute my namespace defining extractor and grostruc,
since all I'm interested in is zoinx.

# let bidule = let extractor f = f ~foo:"aaa" ~bar:"bbb" and grostruc =
List.map string_of_int [1; 2; 3] in grostruc,extractor;;
val bidule : string list * ((foo:string -> bar:string -> '_a) -> '_a) =

The function application in the definition of grostruc prevents 'a from
being generalized. This code is nevertheless equivalent to the precedent
one except from the namespace pollution.

So I actually have two questions:

1/ Is it possible to do what I want to do, even if it means using a
kludge? The above code, using multiple let's, is not good: it's not
useable in the middle of an expression (this is for CamlP4-generated
code).

(acceptable kludges include the use of Obj.magic)

2/ Is there a finer notion of a "generalizable" expression that
encompasses the above code, and could the "let generalization" procedure
in the compiler be improved so that the above code gets a polymorphic
type?

David Monniaux            http://www.di.ens.fr/~monniaux
Laboratoire d'informatique de l'École Normale Supérieure,
Paris, France




^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: generalization in tuples
  2000-10-16 12:42 generalization in tuples David Monniaux
@ 2000-10-17  8:11 ` Didier Remy
  2000-10-17 19:17   ` Didier Remy
  0 siblings, 1 reply; 3+ messages in thread
From: Didier Remy @ 2000-10-17  8:11 UTC (permalink / raw)
  To: David Monniaux; +Cc: Liste CAML

On Mon, Oct 16, 2000 at 02:42:11PM +0200, David Monniaux wrote:
> 1/ Is it possible to do what I want to do, even if it means using a
> kludge? The above code, using multiple let's, is not good: it's not
> useable in the middle of an expression (this is for CamlP4-generated
> code).
> 
> (acceptable kludges include the use of Obj.magic)

In principle, Obj.magic should do the job, but it does not:

        Obj.magic (fun x -> x)

is treated as an application and returns a weak type ;-(
The problem is that  "Obj.magic" is defined as a primitive and the above is 
typed as any other application, but I don't see any reason except technical 
to treat Obj.magic as a constructor. Anyway, I don't think Obj.magic is 
a good fix...

> 2/ Is there a finer notion of a "generalizable" expression that
> encompasses the above code, and could the "let generalization" procedure
> in the compiler be improved so that the above code gets a polymorphic
> type?

Yes, there is a very simple generalization of the value-only polymorphism
restriction. 

Expressions need to be partitioned into two sets: expansive and
non-expansive expressions, such that the evaluation of non-expansive is
guaranteed not to create any storage. 

For instance, non-expansive expressions may include variables, values (hence
functions), as well as constructors applied to non-expansive expressions.

Note that subexpressions of non-expansive expression are often expansive
(e.g. typically when the expression is under lambda-abstraction).

Given an expression e, we are only interested in outer expansive
sub-expressions of e, i.e. those that are not sub-expressions of a
non-expansive sub-expression of e (in which case, they are protected from
evaluation). 

When typing an expression e, all type variables appearing in at least one
outer expansive sub-expression of e may also be the type of a store cell
allocation and should not be generalized. All other type variables can be
generalized.

For instance, in (a simpler version of) your example:

        let x = (ref [], fun x -> x);;

The expression (ref [], fun x -> x) has type 'a ref * ('b -> 'b); 
here, "ref []" is an application, hence an (outer) expansive expression and
'a appearing in its type cannot be generalized.  Conversely, "fun x -> x" is
non-expansive and since variable "'b" only appear in the type of this
non-expansive subexpression, it can be generalized.

A few more examples: 
---------------------

        let x = (let y = fun x -> x in ref y, y)
                 : ('a -> 'a) ref * ('a -> 'a)

Here 'a appears both in an outer expansive expansive expression and in a
non-expansive expressions. Hence it is dangerous can cannot be generalized. 

        let x = fun x -> ref x
                 : ('a -> 'a ref)

The expression is protected, i.e. non-expansive and "'a" can be
generalized. 

(Note that this is a strict generalization of the actual solution.()

The implementation 
-------------------

This is actually quite simple: while typeckecking an expression, just keep
track of whether the expression is the outermost non-expansive part of a
let-bound expression, and if not, make its variable non-generalizable.

In fact, I experimented this in MLART a while ago:

    #morgon:~/caml/camlart/src$ ./camlrun ./camltop -I lib
    >       Caml Light version 0.5 (modified with extensible records)

    #(ref 1, fun x -> x);;
    - : int ref * ('a -> 'a) = ref 1, <fun>

or, using extensible records :-)

    #{!a = fun x -> x};;
    > Toplevel input:
    >{!a = fun x -> x};;
    >^^^^^^^^^^^^^^^^^^^
    > Cannot generalize 'a in {a : mut. 'a -> 'a; abs. 'b}
    #{!a =1; b = fun x -> x};;
    - : {a : mut. int; b : pre. 'a -> 'a; abs. 'b} = {!a = 1; b = <fun>}
    #

-Didier

PS: 
This has never been implemented in Ocaml, probably because, besides me,
you are one of first persons to complain about the drastic implementation of
value-only polymorphism restriction.



^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: generalization in tuples
  2000-10-17  8:11 ` Didier Remy
@ 2000-10-17 19:17   ` Didier Remy
  0 siblings, 0 replies; 3+ messages in thread
From: Didier Remy @ 2000-10-17 19:17 UTC (permalink / raw)
  To: David Monniaux; +Cc: Liste CAML

>         let x = (let y = fun x -> x in ref y, y)
>                  : ('a -> 'a) ref * ('a -> 'a)

Indeed, as Michel Mauny pointed out to me, this expression would have type:

          let x = (let y = fun x -> x in ref y, y)
                   : ('a -> 'a) ref * ('b -> 'b)

thus 'b appearing only in a non-expansive expression could be also
generalized here. (BTW, the context "let x = v in [ ]" is non-expansive
whenever v is)

I meant to create the situation where a type variable appears on both
side of a product, when one side is expansive, this other is not, and the
context of the product is non-expansive. But I can't think of a ``natural''
example of this. The usual trick: 

          let x = (fun (y : 'a) -> ref y, y) (fun x -> x)
                   : ('a -> 'a) ref * ('a -> 'a)

does not work, because the whole expression becomes an application and is
expansive. I can only think of using an artifial typing constraint:

          let x = (let y = fun x -> x in ref (y : 'a), (y : 'a))
                   : ('a -> 'a) ref * ('a -> 'a)

> Here 'a appears both in an outer expansive expansive expression and in a
> non-expansive expressions. Hence it is dangerous can cannot be generalized. 

Funny! it is so difficult to stop type generalization...

        Didier




^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2000-10-18  8:36 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2000-10-16 12:42 generalization in tuples David Monniaux
2000-10-17  8:11 ` Didier Remy
2000-10-17 19:17   ` Didier Remy

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).