Version française
Home     About     Download     Resources     Contact us    
Browse thread
[Caml-list] Re: OCAML Downcasting?
[ 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@k...>
Subject: [Caml-list] Re: OCAML Downcasting?
Reposted from comp.lang.ml:

Matthias Kretschmer <mccratch@gmx.net> writes:

> Roy Patrick Tan schrieb:
>
> > I'm trying to learn about ocaml's object-oriented features. It seems
> > like you can coerce an object of one type to a supertype, but not from
> > a supertype to a subtype. But what if I need to recover the original
> > type?
> 
> I am a bit curious why it is that unsafe that it is completely 
> forbidden. Of course type-checks can't be done at elaboration time and 
> have to be postponed to the evaluation time, but most other OO-languages 
> just implement this and if it is impossible, they raise an exception or 
> abort the program. At least something like Oberon's WITH-statement would 
> sometimes be very handy.

There are two important differences between ocaml and other
OO-languages that have downcasting.

- inheritance is not subtyping. The subtyping relation being
  structural, a full fledge typecast would not only require to keep
  type information at runtime, but to embark a large part of the
  compiler to check subtyping. This might also be incredibly slow.

  As a result, downcast is only reasonable when we have both
  inheritance and subtyping. You're suddenly losing a large part of
  the language.

  Worse, even if you have a module interface
     class b :
       object
         inherit a
         ....
       end
  in ocaml this does _not_ mean that b inherits from a, just that it
  has at least a's methods and fields.
  So the semantics of downcast would not allow abstraction.

- classes may have type parameters. Assume a situation like
    class ['a] a = object ... end
  Then we cannot downcast any runtime object to "a", because we don't
  know what is its type parameter. Again, downcast would not mix well
  with other features of the language.

So, while this is somehow unfortunate, there is no downcast in ocaml.

There are various ways to simulate it, but at first this is a bit
confusing for people used to languages with reflection.

> On the other hand, one can overcome this by using sum-types: e.g. You 
> know about button widgets and label widgets. Of course there can be 
> other widgets, so we have 3 different types to distinguish:
> - buttons
> - labels
> - others
> =>
> type mywidget = Button of button_widget
>                | Label of label_widget
>                | Other of widget

This is indeed the standard way to do it, as it mixes best with a
functional programming language.

> In some circumstances this is nice, because you can simply use pattern 
> matching to extract the type you want. On the other hand, when defining 
> this type you have to know excactly what types you want to handle. So it 
> might get very ugly and clumsy.

Phrased this way, this sounds like you have no way to modify the above
type when you need to add a new subtype.
The straightforward thing to do is to use your editor and add a new
case to the above list. Then the compiler will tell you all the places
you need to modify to take this new case into consideration --
something most OO compilers wouldn't bother to tell you: they would
force you to have a default case, which may be relevant or not, and
you would only discover this at runtime.

For the more special cases where you can't modify the original code,
there are still various ways to simulate downcast, from keeping
objects in hashtables to advanced uses of parametric polymorphism.

Just for fun, here is an example of the second approach:

class virtual ['a] widget = object
  method virtual raw : 'a
end

class ['a] button = object (self)
  inherit ['a] widget
  method raw = `Button self
  method press = prerr_endline "pressed"
end

class ['a] label = object (self)
  inherit ['a] widget
  method raw = `Label self
  val mutable text = ""
  method set_text s = text <- s
end

let l = [(new button :> _ widget); (new label :> _ widget)]

let f = function
    `Button b -> b#press
  | `Label l -> l#set_text "Hello" 

let test () =
  List.iter (fun o -> f o#raw) l

Such a programming pattern lets you use downcasting in a refined way,
without any language support. It even avoids the first limitation I
described above (label need not really inherit from widget). But the
fact you must keep 'a polymorphic makes it advanced.

Jacques Garrigue

-------------------
To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr
Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/
Beginner's list: http://groups.yahoo.com/group/ocaml_beginners