Version française
Home     About     Download     Resources     Contact us    
Browse thread
Why can't immediate objects be extended?
[ Home ] [ Index: by date | by threads ]
[ Search: ]

[ Message by date: previous | next ] [ Message in thread: previous | next ] [ Thread: previous | next ]
Date: -- (:)
From: Jacques Garrigue <garrigue@m...>
Subject: Re: [Caml-list] Why can't immediate objects be extended?
From: Richard Jones <rich@annexia.org>

> I'm wondering if there's a reason why one cannot inherit from
> immediate objects? (See description at:
> http://caml.inria.fr/pub/docs/manual-ocaml/manual005.html#ss:immediate-objects)
> Is it just a syntax problem because the 'inherit' keyword currently
> needs to take a class type, or is there a deeper reason?
> 
> This is the code I'd like to write (non-working, obviously):
[..]
>   let rec annotate = function
>     | (Leaf _ as t) -> t
>     | (Node (left, parent_obj, right) as t) ->
>         let obj = object
>   	  inherit (typeof parent_obj)
>   	  method str = string_of_tree t
>         end in
>         Node (annotate left, obj, annotate right)
[..]
> The problematic function is 'annotate'.  I believe I want annotate to
> have a type along these lines (again, this is not precisely OCaml code
> because I've used alpha to stand for '..'):
> 
>   val annotate : < 'a > tree -> < str : string; 'a > tree

There are several problems here.
What what you here is extensible records, with differential
rows (i.e. you need some way to ensure that str is not in 'a);
otherwise you would end up with a record with two str fiels, with
possibly different type. Extensible records are not available in
ocaml, and while objects allow structural typing of records, they do
not allow extension.

So the next question is why objects do not allow that. And here the
answer is very clear: this form of inheritance is incompatible with
subtyping.
Namely, by subtyping you can hide a field. So you could start
with a  <data: int; str: float> tree, coerce it to
<data: int> tree by subtyping, and then annotate it and obtain
a <data: int; str: string> tree.
If we were talking about records, there would be no problem,
but those are objects, so the definition of data may actually be
  method data = truncate self#str
If str now returns a string, this is broken.
This question is studied in details in Abadi and Cardelli's "A theory
of objects".

So there is no way you can inherit from an immediate object.
(The syntax "inherit object ... end" is not about immediate objects
but immediate classes (inside another class), and is not really
useful)

Going back to your original problem, another approach would be to
allow extension of objects _without overriding of methods_.
The trouble here is that it would require major changes in the type
system, to accomodate the differential rows mentioned above, for
something not directly connected with objects.

Now about workarounds. Since you problem is really a problem of
records, the simplest approach might be to use... records!

  type ('str,'cmt) parent_data = {str: 'str; cmt: 'cmt}

  let none = {str = (); cmt = ()}

  let annotate_data d (str:string) = {d with str = str}
  (* ('a, 'b) parent_data -> string -> (string, 'b) parent_data *)

Note here how the type is updated when you use the with construct.
This should provide you with the behaviour you want.
This may not be as polymorphic as you hoped (you need to know all the
fields w\you will use in advance), but providing a fully
polymorphic solution to the update problem requires a very complicated
type system (look at Didier Remy's papers)

Also, if you want lots of fields, this may be a good idea to use
a constraint to name parameters.

  type +'a parent_data =
    {str: 'str; cmt: 'cmt} constraint 'a = <str:'str; cmt:'cmt>

The explicit variance annotation is required here because it is not
automatically inferred for constrained parameters.
With this definition, the type of none becomes:

  < cmt : unit; str : unit > parent_data

Hope this helps,

     Jacques Garrigue