Version française
Home     About     Download     Resources     Contact us    
Browse thread
[Caml-list] a design problem requiring downcasting? (long)
[ Home ] [ Index: by date | by threads ]
[ Search: ]

[ Message by date: previous | next ] [ Message in thread: previous | next ] [ Thread: previous | next ]
Date: -- (:)
From: Michael Vanier <mvanier@c...>
Subject: [Caml-list] a design problem requiring downcasting? (long)

I've run into a design problem that I think requires downcasting, and I
wanted to see if anyone on the list has any ideas about an alternative
approach, since AFAIK downcasting is impossible in the current ocaml object
system (at least without nasty Obj.magic hacks).  I'll try to simplify the
problem as much as possible, but this is still pretty long.

Here's the problem.  I'm working on the redesign of a large simulation system
originally written in C.  It has all the problems that one always finds in
software written in C: lots of bugs, memory leaks, crashes, etc. etc.  I
would like to re-write this in a safe language, preferably ocaml (since it's
my favorite language).  An alternative might be java, although I think ocaml
would be significantly faster (not to mention more reliable and much more fun
to program in).  C++ would also be a valid choice except that I find that I
can't bear to work in languages without garbage collection anymore, and
anyway, the type discipline of ocaml is a big attraction for me.

It turns out that the design of the core simulator doesn't pose any real
problems.  However, part of the simulation infrastructure is very difficult
to achieve without having a way of downcasting (casting from a supertype to a
subtype).  It can be done, but the design becomes extremely non-modular.  The
issue is that these simulations can contain tens of thousands of simulation
objects which can invoke method calls on each other.  These are actually
binary method calls e.g.

  obj1#my_method obj2

where "obj2" has to be of a specific class type (in actuality, it will be
an interface type).  These method calls are highly dependent on the
specific types of the objects.  If you create the objects like this:

  let obj1 = new class1 in
  let obj2 = new class2 in
  obj1#my_method obj2

then everything works.  However, it is not feasible, when writing a
simulation with this many objects, to keep unique identifiers to each object
that you create.  Therefore, you need some kind of data structure to store
objects after you create them.  For our purposes we can assume that there is
a big master array of these objects.  I see two alternatives for the types of
the array elements.

1) All elements will be instances of subclasses of a base type "base".  The
   array will have the type "base array".

2) The array will be of type "anObject array", where anObject is a variant type
   that can hold the type of any simulation object.

With alternative (1), to do the method call above you would need downcasting
e.g. in pseudo-ocaml:

  let obj1 = array.(0) as class1 in
  let obj2 = array.(1) as class2 in
  obj1#my_method obj2

with the cast raising an exception if it fails.  No problem.  With the
variant type you would have:

  let obj1 = array.(0) in
  let obj2 = array.(1) in
  match (obj1, obj2) with
    (Class1 o1, Class2 o2) -> o1#my_method o2
  | _ -> raise (Failure "bad")

The problem is, every time that a new class is added you have to modify the
variant type.  As if this wasn't bad enough, I envision that "my_method"'s
argument is any class type which implements a particular interface.  So
you'd really have to do this:

  let obj1 = array.(0) in
  let obj2 = array.(1) in
  match obj1 with
    Class1 o1 ->
      match obj2 with
        (* any class that implements the given interface *)
        Class2 o 
      | Class3 o
      | ...
      | ClassN o -> obj1#my_method o
      | _ -> raise (Failure "bad")
  | _ -> raise (Failure "bad")
    
This is getting pretty ugly.  Every time you define a new class which
implements the interface you'd have to modify the above code.  A better
alternative is to call a virtual method on obj1 e.g.

  class class1 =  (* obj1's class *)
    object
      method my_method obj2 =  (* obj2 has the variant type *)
        match obj2 with
          Class2 o 
        | Class3 o
        | ...
        | ClassN o -> obj1#my_method o
        | _ -> raise (Failure "bad")
    end

This is still pretty horrible; for one thing, obj1 is a class type while obj2
is a variant type.  If the array elements are of the variant type, how do you
turn them into the class types?  You could have the array elements be a tuple
of (variant type, superclass type) so you could use whichever form is more
convenient, but that's pretty nasty too.

Compared to this, downcasting looks very sweet.  Actually, I think what I
really want is multimethods ;-)  Is there any way around this that I'm
missing?

Thanks,

Mike




  
-------------------
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