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: Fred Smith <fsmith@m...>
Subject: RE: [Caml-list] a design problem requiring downcasting? (long)
Hi Mike,

I have just recently started playing around with classes so am sure that my
solution can be improved.  In fact it may be a duplicate of other solutions
posted on the list although I haven't seen it.  Still it was fun to come up
with so here goes.

The basic idea is to have each class implement a method wrap that returns
self in a variant type that can be dispatched on.  The problem with this
naive approach is that type declarations and class declarations cannot
(AFAIK) be mutually recursive.  Besides it would be best to avoid mutual
recursion if possible.

The solution I propose is one familiar to people with mutually recursive
modules.  Make this wrap method polymorphic and bind the type variable later
on after the definition of the datatype.  I believe this solves the problem
and should be fairly extensible.

The code I used to test my solution is below.  Your array would have
elements of type c_wrapped.

Good luck.

-Fred


class virtual className =
  object
    method virtual className : string
  end
;;

class ['a] c1_raw wrapper =
  object(self)
    inherit className
    method wrap = (wrapper (self :> 'a c1_raw) : 'a)
    method className = "c1"
    method my_method (o : className) = o#className
  end

class ['a] c2_raw wrapper =
  object(self)
    inherit className
    method wrap = (wrapper (self :> 'a c2_raw) :'a)
    method className = "c2"
  end

type classVar = C1 of classVar c1_raw | C2 of classVar c2_raw ;;

class c1 = [classVar]c1_raw (fun o -> C1 o);;
class c2 = [classVar]c2_raw (fun o -> C2 o);;

class type c_wrapped =
    object
      method wrap : classVar
      method className : string
    end
;;

let app o1 o2 =
  match o1#wrap with
    C1 o1' -> o1'#my_method (o2 :> className)
  | _ -> failwith "error"
;;

let o1 = new c1;;
let o2 = new c2;;

app (o1 :> c_wrapped)  (o2 :> c_wrapped)

> -----Original Message-----
> From: owner-caml-list@pauillac.inria.fr
> [mailto:owner-caml-list@pauillac.inria.fr]On Behalf Of Michael Vanier
> Sent: Thursday, September 26, 2002 5:01 AM
> To: caml-list@inria.fr
> 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

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