Version française
Home     About     Download     Resources     Contact us    
Browse thread
Polymorphic method question
[ 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] Polymorphic method question
From: brogoff <brogoff@speakeasy.net>

> > The only way to solve this problem would be to remove parameter
> > constraint inference from classes. It would certainly break some
> > programs, so this seems difficult.
> 
> That's too bad, as it seems the class based OO version of the extensible
> visitor is fairly straightforward in OCaml except for this counterintuitive
> gotcha. What would have to be done to fix the broken programs? It may be
> worth considering.

You just would have to write constraints explicitly when they are
needed.
This could be a pain for classes. See the tutorial on the
subject-observer pattern in the reference manual for a non-trivial use
of inference.
But maybe we could rather have a way to tell the system that, for some
specific class, type parameters are not constrained. Hacky though.

> > By the way, if you're ready to define your types first, then you
> > can do this properly.
> 
> In the more realistic example I sent along after, you'll see that this
> solution is not really pleasing. You'd need to create a parallel type
> hierarchy to match your class hierarchy, which is too verbose. All this
> because we must have inference, which is supposed to make things less
> verbose. Ironic, isn't it? ;-)

I'm not so sure about verbosity. Type syntax is pretty compact :-)

> I have seen the new private rows stuff, which is very nice, but all of these
> solutions IMO are way too complex to be considered satisfactory. I'll see if
> the Java 1.5 approach actually works (I used some Pizza code to do the OCaml
> implementation) because that was quite straightforward. I'd prefer approaches
> that I can explain with a little hand waving. I think type theorists often
> forget that there are a few programmers still who don't have PhDs in type
> theory and will look at such solutions and conclude that OCaml is not for
> them ;-).

I'm sure you need some tricks to make it work in Java 1.5 too.
I don't have the code in mind.
I agree that the solution I posted is not too simple, but I don't
think this is PhD level. Actually it's not much worse than the visitor
pattern. What is hard is understanding why you have to do all this
extra work, not using the pattern itself.

Note that, if you are ready to use a slightly less functional
approach, where the result is extracted from the visitor afterwards,
then typing is no problem.
This code is based on http://cristal.inria.fr/~remy/work/expr/
It is strictly equivalent to Odersky&Zenger's Scala version.
(Actually this kind of code is one reason we reverted to sharing of
instance variables in 3.10)

Jacques Garrigue

module Shape =
  struct
    class virtual shape_visitor =
      object
        method virtual visit_square : square -> unit
        method virtual visit_rectangle : rectangle -> unit
        method virtual visit_polygon :  polygon -> unit
        method virtual visit_translated : translated  -> unit
        (* and potentially many more! *)
      end
    and virtual shape =
      object
        method virtual visit : shape_visitor -> unit
      end
    and square pt width =
      object (self)
        inherit shape
        method visit visitor = visitor#visit_square (self :> square)

        method as_tuple : ((int * int) * (int * int)) =
          let (ll_x, ll_y) = pt in
          (pt, (ll_x + width, ll_y + width))

        method as_point_list : (int * int) list =
          failwith "Shape.square#as_point_list: unimplemented"
      end
    and rectangle ll ur =
      object (self)
        inherit shape
        method visit visitor = visitor#visit_rectangle (self :> rectangle)

        method as_tuple : ((int * int) * (int * int)) = (ll, ur)

        method as_point_list : (int * int) list =
          failwith "Shape.rectangle#as_point_list: unimplemented"
      end

    and polygon points =
      object (self)
        val points : (int * int) list = points
        inherit shape

        method visit visitor = visitor#visit_polygon (self :> polygon)

        method as_point_list : (int * int) list =
          points
      end

    and translated (pt : int * int) (s : shape) =
      object (self)
        inherit shape
        method visit visitor = visitor#visit_translated (self :> translated)

        method get_shape = s
        method get_translation = pt

        method as_point_list : (int * int) list =
          failwith "Shape.translated#as_point_list: unimplemented"
      end
  end (* module Shape *)

module Contains_point =
  struct
    (* Writing code that works over shapes *)
    class contains_point point =
      object
        inherit Shape.shape_visitor

        val mutable result = false
        method result = result

        method visit_square : Shape.square -> unit =
          fun s ->
            let ((ll_x, ll_y), (ur_x, ur_y)) = s#as_tuple in
            let (x, y) = point in
            result <- ll_x <= x && x <= ur_x && ll_y <= y && y <= ur_y

        method visit_rectangle : Shape.rectangle -> unit =
          fun r ->
            let ((ll_x, ll_y), (ur_x, ur_y)) = r#as_tuple in
            let (x, y) = point in
            result <- ll_x <= x && x <= ur_x && ll_y <= y && y <= ur_y

        method visit_polygon : Shape.polygon -> unit =
          fun p ->
            failwith "Contains_point.contains_point#visit_polygon: unimplemented"

        method visit_translated : Shape.translated -> unit =
          fun t ->
            let (x, y) = point in
            let (dx, dy) = t#get_translation in
            let visitor = new contains_point (x-dx, y-dy) in
            t#get_shape#visit (visitor :> Shape.shape_visitor);
            result <- visitor#result
      end

    let contains_point p (s : #Shape.shape) =
      let visitor = new contains_point p in
      s#visit (visitor :> Shape.shape_visitor);
      visitor#result
  end;;