Version française
Home     About     Download     Resources     Contact us    
Browse thread
Class/prototype-based OO
[ 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] Class/prototype-based OO
From: Ted Kremenek <kremenek@cs.stanford.edu>

> While the structural typing in Ocaml is wonderful (I really love it),  
> there are missing features of the OO system in OCaml that can be  
> particularly bothersome, and are worth considering when comparing the  
> OO features of Ocaml with other languages (including those that use  
> nominal typing).
> 
> For example, from what I can tell it appears that Ocaml lacks real  
> support for "downcasts" in the language, which require run-time type  
> information (RTTI) and run-time checks.  In the context of structural  
> typing, I am talking about "casting" an object with a type with a  
> given set of method signatures to another type with an extended set  
> of method signatures.  Whether or not you agree downcasts are a good  
> thing, it is one thing to take into account when comparing the OO  
> features of different languages (and this is a feature that both C++  
> and Java have that OCaml does not).  In general, I don't believe the  
> OO system in Ocaml supports any kind of RTTI, which enables features  
> such as downcasts and reflection.  All of the type checking of  
> objects in OCaml is static, which is very nice but often insufficient.

The question of downcasts in ocaml is a recurring one.
There is indeed no way to downcast an object to an arbitrary
superclass of its dynamic class.
But a major question here is whether we want to be able to do that or
not, particularly in a structural type system.
"Universal" downcasting would mean that you would be able to revert
any ocaml object to its original object type, as long as you know
this type (structurally). This clearly breaks almost any form of
abstraction. So this means that programs that currently guarantee,
through the use of subtyping or private row types, that some methods
of an object are not visible, could be easily broken.
This is a rather philosophical problem. Do we really need abstraction
to be unbreakable? From a theoretical point of view yes, while
practicians do not always agree.
Another problem with "universal" downcast is that it would be
extremely costly. Checking subtyping with structural types is a rather
heavy weight algorithm, which can takes several seconds in some
cases. It looks a bit like trying to break encryption while running a
program...

For the above reasons I think that "universal" downcast in ocaml is
not a good idea. 

Now, one might want some weaker, explicit form of allowing downcasts.
Actually some proposal was done already in the last century:
http://groups.google.com/group/fa.caml/browse_thread/thread/48fc2b87effc1097/e3ef9dab723b7c52
Basically, it allowed to declare a posteriori a downcasting
hierarchy, giving subtyping relations between classes, that was
checked by the type-checker. One could then downcast objects of a
certain class (each object already contains a pointer to its class, so
this can be checked at untime) to any type declared as supertype.
The idea was not bad, and it does not completely break abstraction: if
you do not have a class declaration in your scope, you cannot add it
to the downcasting hierarchy. But there are hard to overcome
limitations:
* only monormophic classes can be added to the hierarchy
  (not polymorphic ones or immediate objects)
* it is easy to forget to add a subclass to the hierarchy, and then
  there is no way to downcast it, even to one of its superclasses
  appearing in the hierarchy

One could think of other approaches, but I'm not sure there is a
canonical one to add to the language, that would make everybody
happy.

On the other hand, there are various ways to simulate downcasting.
My favorite one is already described in the above thread: keep objects
you may want to downcast in an hash table.

  let memorize o table = Hashtbl.add table (o :> < >) o
  val memorize : (< .. > as 'a) -> (<  >, 'a) Hashtbl.t -> unit = <fun>

Now, you just need to keep one hash table for each target supertype,
and can then downcast any object in this hash table to this supertype.
Using object identity is a bit less efficient than using the class
pointer, but it partially solves the problem of polymorphic classes
and immediate objects: any subtype of a given type can be added to the
table, even if it is not from a monomorphic class.
Note that using a normal approach causes a memory leak, but using a
weak hash table solves that problem. See Remy Vanicat's Weak_memo for
that:
http://remi.vanicat.free.fr/ocaml/hweak/

Since this approach does not modify the compiler, or use any unsafe
feature, it does not break abstraction in any way.

Independently of downcasting, RTTI would also be needed for providing
more information in the debugger. But there seems not to be much
demand for that.

Jacques Garrigue