caml-list - the Caml user's mailing list
 help / color / mirror / Atom feed
* [Caml-list] polymorphic method question
@ 2002-08-21 21:49 nadji
  2002-08-21 22:57 ` Jacques Garrigue
  0 siblings, 1 reply; 12+ messages in thread
From: nadji @ 2002-08-21 21:49 UTC (permalink / raw)
  To: caml-list

Hi,

I am puzzled by this behaviour, can a guru explain me ?
I have two classes
class type ['a] basic = object
  method foo : 'a
end;;

 class ['a] toto (a:'a) = object
  method foo = a
  method bar = ()
end;;

I want to do a class gentoto :
class gentoto = object
  method gen : 'a . 'a -> 'a basic =
    fun x -> (new toto x :> 'a basic )
end;;
But the compiler complains that :
      Characters 64-98:
      fun x -> (new toto x :> 'a basic )
      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This method has type 'a -> 'a basic which is less general than
  'b. 'b -> 'b basic

... but if I do :
let newtotoasbasic x = (new toto x :> 'a basic );;

class gentoto =
object
  method gen : 'a . 'a -> 'a basic =
(*     fun x -> (new toto x :> 'a basic ) *)
    newtotoasbasic
end;;

The compiler agrees with me ...
What is wrong with the first declaration ?

Thanks,
Nadji

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


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [Caml-list] polymorphic method question
  2002-08-21 21:49 [Caml-list] polymorphic method question nadji
@ 2002-08-21 22:57 ` Jacques Garrigue
  0 siblings, 0 replies; 12+ messages in thread
From: Jacques Garrigue @ 2002-08-21 22:57 UTC (permalink / raw)
  To: nadji; +Cc: caml-list

From: nadji@noos.fr
> I am puzzled by this behaviour, can a guru explain me ?
> I have two classes
[...]
> I want to do a class gentoto :
> class gentoto = object
>   method gen : 'a . 'a -> 'a basic =
>     fun x -> (new toto x :> 'a basic )
> end;;
> But the compiler complains that :
>       Characters 64-98:
>       fun x -> (new toto x :> 'a basic )
>       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> This method has type 'a -> 'a basic which is less general than
>   'b. 'b -> 'b basic
> 
> ... but if I do :
> let newtotoasbasic x = (new toto x :> 'a basic );;
[...]
> The compiler agrees with me ...
> What is wrong with the first declaration ?

There is a problem with the scoping of named type variables in the
language, which makes them incompatible with polymorphic methods.
You can solve this by using anonymous variables:
# class gentoto = object
    method gen : 'a . 'a -> 'a basic =
      fun x -> (new toto x :> _ basic)
  end;;
class gentoto : object method gen : 'a -> 'a basic end

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


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [Caml-list] Polymorphic method question
  2006-07-12  0:37         ` Jacques Garrigue
@ 2006-07-12 19:26           ` brogoff
  0 siblings, 0 replies; 12+ messages in thread
From: brogoff @ 2006-07-12 19:26 UTC (permalink / raw)
  To: Jacques Garrigue; +Cc: caml-list

On Wed, 12 Jul 2006, Jacques Garrigue wrote:
> From: brogoff <brogoff@speakeasy.net>
> > > I'm sure you need some tricks to make it work in Java 1.5 too.
> >
> > The nastiest one for Caml is a downcast, when you want to extend the data
> > the new class must call the extended visitor in its visit method. But Java
> > supports this, so no big deal.
>
> Well, if you're ready to have downcasts in your code...
> But then,

I'd hoped that there would be some trick I could figure out to remove the
downcast, but it's essentially to overcome the lack of covariant
method arguments (the visitor argument of the visited class) so yes it's a
flaw in the approach.

> I would not say that your Java version is more "typed". It is less so
> if you have downcasts!

Fair enough. However, the "typecase" that's being performed has only one branch,
and is localized to the extending class, so as such crimes go, it's more
like vandalism than arson.

> [About having to write the type hierarchy by hand]
> > But we have to repeat the information from the type in a later class,
> > which seems redundant. Also, there's no way I know of to build object types
> > from other object types, similar to the way I can combine polymorphic variant
> > types.
> >
> > I haven't tried to encode it yet, maybe I'll try later, but I don't
> > see how it can be made to work.
>
> Indeed. At times I wonder whether it would not be better to have a
> syntax to do this with types. Or maybe not to infer parameter
> constraints for class types, as they are required for extensibility.

I think if we're going to do OO in an ML like language, I think it is probably
more promising to consider a system like Dylan or CLOS. But that's a huge
change, for a brand new language.

I'd rather have a simpler language, with more powerful records, variants and
module system. I usually use classes in OCaml as records anyways.

-- Brian


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [Caml-list] Polymorphic method question
  2006-07-11 18:20       ` brogoff
@ 2006-07-12  0:37         ` Jacques Garrigue
  2006-07-12 19:26           ` brogoff
  0 siblings, 1 reply; 12+ messages in thread
From: Jacques Garrigue @ 2006-07-12  0:37 UTC (permalink / raw)
  To: brogoff; +Cc: caml-list

From: brogoff <brogoff@speakeasy.net>

> > I'm sure you need some tricks to make it work in Java 1.5 too.
> 
> The nastiest one for Caml is a downcast, when you want to extend the data
> the new class must call the extended visitor in its visit method. But Java
> supports this, so no big deal.

Well, if you're ready to have downcasts in your code...
But then,

> > 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.
> 
> That's probably the right way to proceed, namely to eliminate the
> problem by eliminating the polymorphic methods. Too bad, the Java
> solution is both more functional, and more "typed". One of those
> rare cases for me where I feel that the Java solution is more
> elegant than the OCaml one.

I would not say that your Java version is more "typed". It is less so
if you have downcasts!
By the way, the Scala version I mentioned uses no downcast, but cannot
accomodate polymorphic methods (at least in the mentioned paper.)
So there seems to be a tension between guaranteed type-safety and
immutability. The point is that to solve it cleanly, one has to use type
parameters in an abstract type. OCaml is able to do it, but I agree
that this is rather verbose.

[About having to write the type hierarchy by hand]
> But we have to repeat the information from the type in a later class,
> which seems redundant. Also, there's no way I know of to build object types
> from other object types, similar to the way I can combine polymorphic variant
> types.
> 
> I haven't tried to encode it yet, maybe I'll try later, but I don't
> see how it can be made to work.

Indeed. At times I wonder whether it would not be better to have a
syntax to do this with types. Or maybe not to infer parameter
constraints for class types, as they are required for extensibility.

Jacques Garrigue


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [Caml-list] Polymorphic method question
  2006-07-11  7:32     ` Jacques Garrigue
@ 2006-07-11 18:20       ` brogoff
  2006-07-12  0:37         ` Jacques Garrigue
  0 siblings, 1 reply; 12+ messages in thread
From: brogoff @ 2006-07-11 18:20 UTC (permalink / raw)
  To: Jacques Garrigue; +Cc: caml-list

On Tue, 11 Jul 2006, Jacques Garrigue wrote:
> From: brogoff <brogoff@speakeasy.net>
> > > 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 :-)

But we have to repeat the information from the type in a later class,
which seems redundant. Also, there's no way I know of to build object types
from other object types, similar to the way I can combine polymorphic variant
types.

I haven't tried to encode it yet, maybe I'll try later, but I don't see how it
can be made to work.

> I'm sure you need some tricks to make it work in Java 1.5 too.

The nastiest one for Caml is a downcast, when you want to extend the data
the new class must call the extended visitor in its visit method. But Java
supports this, so no big deal. Also, Java generics require that primitive
types be wrapped, a mild annoyance. That's about it. Way easier than my
attempts in OCaml, even though I don't use Java much at all. OK, to be fair,
the original paper uses some Java variant (Pizza?) so maybe that's not too
surprising. I wrote the code and it compiled, but I haven't tested it.

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

Sure, I exaggerated for affect, but I think the comparison with Java for this
problem is telling.

I'm reminded of something David MacQueen wrote in his paper "Should ML be
Object Oriented" where he says in essence that the functional core of ML
is lightweight and understandable but that OO (from the static typing POV)
is surprisingly subtle and complex.

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

That's probably the right way to proceed, namely to eliminate the
problem by eliminating the polymorphic methods. Too bad, the Java
solution is both more functional, and more "typed". One of those
rare cases for me where I feel that the Java solution is more
elegant than the OCaml one.

-- Brian


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [Caml-list] Polymorphic method question
  2006-07-11  5:22   ` brogoff
@ 2006-07-11  7:32     ` Jacques Garrigue
  2006-07-11 18:20       ` brogoff
  0 siblings, 1 reply; 12+ messages in thread
From: Jacques Garrigue @ 2006-07-11  7:32 UTC (permalink / raw)
  To: brogoff; +Cc: caml-list

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


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [Caml-list] Polymorphic method question
  2006-07-11  2:09 ` Jacques Garrigue
@ 2006-07-11  5:22   ` brogoff
  2006-07-11  7:32     ` Jacques Garrigue
  0 siblings, 1 reply; 12+ messages in thread
From: brogoff @ 2006-07-11  5:22 UTC (permalink / raw)
  To: Jacques Garrigue; +Cc: caml-list

On Tue, 11 Jul 2006, Jacques Garrigue wrote:

> From: brogoff <brogoff@speakeasy.net>
>
> Because bar is not yet completely defined when you use it in foobar.
> This is all related with parameter constraint inference: we don't know
> yet wether bar's parameter is really polymorphic or is constraint (e.g.
> 'a = 'b * 'c).
> Note that the problem does not occur with direct type definitions, as
> constraint inference is not done in that case (this is one of the
> reasons you cannot combine type and class definitions.)
>
> # type 'a bar = < get: 'a > and foobar = < f : 'a. 'a bar -> 'a >;;
> type 'a bar = < get : 'a >
> and foobar = < f : 'a. 'a bar -> 'a >
>
> 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.

> 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 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 ;-).

-- Brian


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [Caml-list] Polymorphic method question
  2006-07-11  2:24     ` skaller
@ 2006-07-11  4:56       ` brogoff
  0 siblings, 0 replies; 12+ messages in thread
From: brogoff @ 2006-07-11  4:56 UTC (permalink / raw)
  To: skaller; +Cc: caml-list, Richard Jones

On Tue, 11 Jul 2006, skaller wrote:

> On Mon, 2006-07-10 at 16:25 -0700, brogoff wrote:
>
> > The restriction on polymorphism in mutually recursive class
> > definitions makes the idea unworkable. Any possibility that restriction will be
> > lifted?
>
> I don't believe there is such a restriction:

See the thread that Richard referenced, and go to Jacques Garrigue's final post.
There he writes:

    "Inside mutually recursive definitions of classes, parameters cannot be
    used polymorphically"

That's the problem, isn't it?

> the problem is
> when you introduce other types such as variants which must
> recurse with class types, there's no support for
>
> type class t1 = ..
> and t2 = [`X ..]

That's surely annoying, but it's a different problem I think.
>
> etc. The solution, using extra parameters and open
> recursion, followed by closure of the recursions,
> works but is vastly too messy to encode by hand if there
> are more than a couple of parameters needed. This really
> does need to be automated in the special case where
> you're only opening the types so you can later close them.

If you look at the faux OCaml code I provided, I believe the intent
is clear, forgiving OCaml's, ummm, suboptimal syntax. I could code
it up in Java 1.5 (maybe I should, just to make sure it works!) and
since Java doesn't do parameter inference it may all work fine.

> In general, for incremental type building, you need to retain
> the open types.. and it isn't clear if there's any way
> to make it tractable (syntactically I mean).

The code for this in Java 1.5 is quite clear, syntactically. It's a bit
tricky to get it all right, of course, but someone smarter than me already
figured it out, I just want to implement the trick in my language of
choice, which is not Java.

> Unfortunately .. the easiest kind of solution is to write
> a code generator in Python or Perl .. this kind of polymorphism
> is not exactly desirable .. ;(

No, I think you're missing the point of the "exercise" here. I'm not looking for
a code generator, but an OO approach to building extensible visitors with
precise static type checking.

-- Brian


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [Caml-list] Polymorphic method question
  2006-07-10 23:25   ` brogoff
@ 2006-07-11  2:24     ` skaller
  2006-07-11  4:56       ` brogoff
  0 siblings, 1 reply; 12+ messages in thread
From: skaller @ 2006-07-11  2:24 UTC (permalink / raw)
  To: brogoff; +Cc: Richard Jones, caml-list

On Mon, 2006-07-10 at 16:25 -0700, brogoff wrote:

> The restriction on polymorphism in mutually recursive class
> definitions makes the idea unworkable. Any possibility that restriction will be
> lifted?

I don't believe there is such a restriction: the problem is
when you introduce other types such as variants which must
recurse with class types, there's no support for

type class t1 = ..
and t2 = [`X ..]

etc. The solution, using extra parameters and open
recursion, followed by closure of the recursions,
works but is vastly too messy to encode by hand if there
are more than a couple of parameters needed. This really
does need to be automated in the special case where
you're only opening the types so you can later close them.

In general, for incremental type building, you need to retain
the open types.. and it isn't clear if there's any way
to make it tractable (syntactically I mean).

Unfortunately .. the easiest kind of solution is to write
a code generator in Python or Perl .. this kind of polymorphism
is not exactly desirable .. ;(

Can MetaOcaml can handle this better?

-- 
John Skaller <skaller at users dot sf dot net>
Felix, successor to C++: http://felix.sf.net


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [Caml-list] Polymorphic method question
  2006-07-10 19:21 Polymorphic " brogoff
  2006-07-10 20:05 ` [Caml-list] " Richard Jones
@ 2006-07-11  2:09 ` Jacques Garrigue
  2006-07-11  5:22   ` brogoff
  1 sibling, 1 reply; 12+ messages in thread
From: Jacques Garrigue @ 2006-07-11  2:09 UTC (permalink / raw)
  To: brogoff; +Cc: caml-list

From: brogoff <brogoff@speakeasy.net>
Subject: [Caml-list] Polymorphic method question
Date: Mon, 10 Jul 2006 12:21:26 -0700 (PDT)

> Hi,
>     I'm sure I'll slap my forehead in disgust when someone lifts the
> scales from my eyes, but I'm once again perplexed by a type error
> from OCaml when using objects. Can someone tell me why I get the
> error from the second case (with the classes connected by "and") when
> the first case is OK?
> # class virtual ['a] bar =
>     object
>       method virtual get : 'a
>     end
>   and foobar =
>     object
>       method virtual f : 'a . 'a bar -> 'a
>     end;;
>               Characters 115-132:
>         method virtual f : 'a . 'a bar -> 'a
>                            ^^^^^^^^^^^^^^^^^
> This type scheme cannot quantify 'a :
> it escapes this scope.

Because bar is not yet completely defined when you use it in foobar.
This is all related with parameter constraint inference: we don't know
yet wether bar's parameter is really polymorphic or is constraint (e.g.
'a = 'b * 'c).
Note that the problem does not occur with direct type definitions, as
constraint inference is not done in that case (this is one of the
reasons you cannot combine type and class definitions.)

# type 'a bar = < get: 'a > and foobar = < f : 'a. 'a bar -> 'a >;;
type 'a bar = < get : 'a >
and foobar = < f : 'a. 'a bar -> 'a >

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.

By the way, if you're ready to define your types first, then you
can do this properly.
 
# class virtual ['a] bar = object method virtual get : 'a end
  and virtual foobar = object method virtual f : 'a . 'a bar_t -> 'a end;;
class virtual ['a] bar : object method virtual get : 'a end
and virtual foobar : object method virtual f : 'a bar_t -> 'a end

>    BTW, This example was distilled from one in which I could not cleanly
> remove the class recursion, one involving an "extensible visitor" in
> which there is a recursion between the visited and visitor classes.
> Assuming there's an obvious answer to my first question, is there a
> nice workaround in this case?

Extensible visitor is more complex. You can already find the
polymorphic variant solution at
   http://www.math.nagoya-u.ac.jp/~garrigue/papers/
There is a new solution using private rows and recursive modules
inside "Private rows: abstracting the unnamed".

With Keiko Nakata, we found another solution using objects in place of
polymorhic variants. This is rather verbose, but here is the code.
This is close to the Odersky&Zenger approach in Scala, but here our
visitor is purely functional.
Note how we are not able to use eval's self directly for recursion, as
we need an abstract row.

Jacques Garrigue

(* OO decomposition in Objective Caml *)

class type ['a,'exp] visit_add = object
  method visitNum : int -> 'a
  method visitAdd : 'exp -> 'exp -> 'a
end

module type AddTE = sig
  type ('a,'exp) t
  type exp = < accept : 'a. ('a, exp) t -> 'a >
  val num : int -> exp
  val add : exp -> exp -> exp
end

module type AddT = sig
  include AddTE
  val eval : (int, exp) t Lazy.t
end

module AddF(X : AddT with type ('a,'exp) t = private ('a,'exp) #visit_add) =
struct
  type ('a, 'exp) t = ('a, 'exp) X.t
  class type exp = object ('e) method accept : 'a. ('a, 'e) t -> 'a end
  class num x = object (_ : #exp) method accept v = v#visitNum x end
  let num x = new num x
  class add a b = object (_ : #exp) method accept v = v#visitAdd a b end
  let add x = new add x
  class eval = object
    method visitNum (i : int) = i
    method visitAdd (r : exp) (l : exp) =
      r#accept (Lazy.force X.eval) + l#accept (Lazy.force X.eval)
  end
  let eval = lazy (new eval)
end

module rec Add : AddT with type('a,'exp) t = ('a,'exp) visit_add = AddF(Add)

class type ['a,'exp] visit_add_neg = object
  inherit ['a,'exp] visit_add
  method visitNeg : 'exp -> 'a
end

module type AddNegT = sig
  include AddT
  val neg : exp -> exp
end

module AddNegF(X : AddNegT with
               type ('a,'exp) t = private ('a,'exp) #visit_add_neg) =
struct
  module Add = AddF(X)
  include (Add : AddTE with type ('a,'e) t = ('a,'e) X.t)
  class neg x = object (_ : #Add.exp) method accept v = v#visitNeg x end
  let neg x = new neg x
  class eval = object
    inherit Add.eval
    method visitNeg (e : exp) = - e#accept (Lazy.force X.eval)
  end
  let eval = lazy (new eval)
end

module rec AddNeg : AddNegT with type ('a,'exp) t = ('a,'exp) visit_add_neg =
  AddNegF(AddNeg)


# open Add;;
# let e1 = (add (num 2) (num 3));;
val e1 : Add.exp = <obj>
# e1#accept (Lazy.force eval);;
- : int = 5
# open AddNeg;;
# let e2 = neg (e1 : Add.exp :> exp);;
val e2 : AddNeg.exp = <obj>
# e2#accept (Lazy.force eval);;
- : int = -5


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [Caml-list] Polymorphic method question
  2006-07-10 20:05 ` [Caml-list] " Richard Jones
@ 2006-07-10 23:25   ` brogoff
  2006-07-11  2:24     ` skaller
  0 siblings, 1 reply; 12+ messages in thread
From: brogoff @ 2006-07-10 23:25 UTC (permalink / raw)
  To: Richard Jones; +Cc: caml-list

On Mon, 10 Jul 2006, Richard Jones wrote:

> On Mon, Jul 10, 2006 at 12:21:26PM -0700, brogoff wrote:
> > This type scheme cannot quantify 'a :
> > it escapes this scope.
>
> Check out this thread:
>
> http://caml.inria.fr/pub/ml-archives/caml-list/2005/02/a67b1a509263be01705305f36d64c39c.en.html
>
> Rich.

Hi Rich,

Thanks for the info, I guess I missed that one when it came out. Unfortunately,
the solution proposed, breaking the recursion by introducing some polymorphic
algebraic type to hold any kind of value in some class type to represent the
visitor, breaks the whole thing. There are just too many recursive
dependencies. Here's a more complete, but still small, example. The modules
are only there to show how the classes would go into files, they're not
essential.

PS: Before anyone tells me that algebraic data types are favored for this
over visitor classes, I'm well aware of all that, and in general agree
with those who prefer their ML sans OOP. However, I was trying to encode a
design pattern I read about somewhere (Felleisen?) which made good use of
polymorphic methods, only to find that it just doesn't translate into the OCaml
class system. The restriction on polymorphism in mutually recursive class
definitions makes the idea unworkable. Any possibility that restriction will be
lifted?

(* The following code won't work, but hopefully gives the idea *)

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

        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

        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

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

    class translated pt s =
      object (self)
        inherit shape
        method visit visitor = visitor#visit_translated self

        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 bool Shape.shape_visitor

        method make_contains_point p =
          new contains_point p

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

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

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

        method visit_translated : Shape.translated node  -> bool =
          fun t ->
            failwith "Contains_point.contains_point#visit_translated: unimplemented"
  end;;



-- 
-- Brian



^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [Caml-list] Polymorphic method question
  2006-07-10 19:21 Polymorphic " brogoff
@ 2006-07-10 20:05 ` Richard Jones
  2006-07-10 23:25   ` brogoff
  2006-07-11  2:09 ` Jacques Garrigue
  1 sibling, 1 reply; 12+ messages in thread
From: Richard Jones @ 2006-07-10 20:05 UTC (permalink / raw)
  To: brogoff; +Cc: caml-list

On Mon, Jul 10, 2006 at 12:21:26PM -0700, brogoff wrote:
> This type scheme cannot quantify 'a :
> it escapes this scope.

Check out this thread:

http://caml.inria.fr/pub/ml-archives/caml-list/2005/02/a67b1a509263be01705305f36d64c39c.en.html

Rich.

-- 
Richard Jones, CTO Merjis Ltd.
Merjis - web marketing and technology - http://merjis.com
Team Notepad - intranets and extranets for business - http://team-notepad.com


^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2006-07-12 19:26 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2002-08-21 21:49 [Caml-list] polymorphic method question nadji
2002-08-21 22:57 ` Jacques Garrigue
2006-07-10 19:21 Polymorphic " brogoff
2006-07-10 20:05 ` [Caml-list] " Richard Jones
2006-07-10 23:25   ` brogoff
2006-07-11  2:24     ` skaller
2006-07-11  4:56       ` brogoff
2006-07-11  2:09 ` Jacques Garrigue
2006-07-11  5:22   ` brogoff
2006-07-11  7:32     ` Jacques Garrigue
2006-07-11 18:20       ` brogoff
2006-07-12  0:37         ` Jacques Garrigue
2006-07-12 19:26           ` brogoff

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).