From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Original-To: caml-list@yquem.inria.fr Delivered-To: caml-list@yquem.inria.fr Received: from nez-perce.inria.fr (nez-perce.inria.fr [192.93.2.78]) by yquem.inria.fr (Postfix) with ESMTP id BB308BB84 for ; Tue, 11 Jul 2006 09:32:31 +0200 (CEST) Received: from kurims.kurims.kyoto-u.ac.jp (kurims.kurims.kyoto-u.ac.jp [130.54.16.1]) by nez-perce.inria.fr (8.13.6/8.13.6) with ESMTP id k6B7WLFt002776 for ; Tue, 11 Jul 2006 09:32:30 +0200 Received: from localhost (suiren [130.54.16.25]) by kurims.kurims.kyoto-u.ac.jp (8.13.7/8.13.6) with ESMTP id k6B7WE56020021; Tue, 11 Jul 2006 16:32:15 +0900 (JST) Date: Tue, 11 Jul 2006 16:32:07 +0900 (JST) Message-Id: <20060711.163207.72458365.garrigue@math.nagoya-u.ac.jp> To: brogoff@speakeasy.net Cc: caml-list@yquem.inria.fr Subject: Re: [Caml-list] Polymorphic method question From: Jacques Garrigue In-Reply-To: References: <20060711.110916.22502254.garrigue@math.nagoya-u.ac.jp> X-Mailer: Mew version 4.2 on Emacs 21.3 / Mule 5.0 (SAKAKI) Mime-Version: 1.0 Content-Type: Text/Plain; charset=us-ascii Content-Transfer-Encoding: 7bit X-Miltered: at nez-perce with ID 44B35405.000 by Joe's j-chkmail (http://j-chkmail.ensmp.fr)! X-Spam: no; 0.00; inference:01 ocaml:01 non-trivial:01 inference:01 verbose:01 verbose:01 syntax:01 ocaml:01 expr:01 odersky:01 reverted:01 struct:01 translated:01 translated:01 failwith:01 X-Spam-Checker-Version: SpamAssassin 3.0.3 (2005-04-27) on yquem.inria.fr X-Spam-Level: X-Spam-Status: No, score=0.4 required=5.0 tests=DNS_FROM_RFC_ABUSE autolearn=disabled version=3.0.3 From: brogoff > > 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;;