* [Caml-list] does class polymorphism need to be so complicated? @ 2003-08-20 15:42 Benjamin Geer 2003-08-20 16:05 ` Brian Hurt 0 siblings, 1 reply; 30+ messages in thread From: Benjamin Geer @ 2003-08-20 15:42 UTC (permalink / raw) To: caml-list I'm looking for a convenient way to use a derived class anywhere its base class can be used. There seem to be two ways to do this, but neither of them is convenient. For example, suppose I have the following examples (borrowed from the O'Reilly Caml book): class virtual printable () = object (self) method virtual to_string : unit -> string method print () = print_string (self#to_string()) end ;; class point (x_init, y_init) = object inherit printable () val mutable x = x_init val mutable y = y_init method get_x = x method get_y = y method to_string () = "( " ^ (string_of_int x) ^ ", " ^ (string_of_int y) ^")" end ;; I want to make a class 'printer', which prints the string representation of any 'printable'. It seems that I have two options: 1. Write 'printer' like this: class printer = object method print (obj : printable) = obj#print() end ;; And use it like this: let p = new point (1, 2) ;; let pr = new printer ;; pr#print (p :> printable) ;; It is cumbersome to have to write the coercion, and it seems strange to have to do so in an object-oriented language; why can't Caml recognise that a 'point' is a 'printable', and do the coercion automatically? Moreover, it introduces a potential maintenance problem. Suppose that, after writing the application, I decide to to move the printing logic out of 'printable', and into the 'printer' class. I refactor the classes like this: class virtual stringable () = object method virtual to_string : unit -> string end ;; class virtual printable () = object (self) inherit stringable () method print () = print_string (self#to_string()) end ;; class printer = object method print (obj : stringable) = print_string (obj#to_string()) end ;; But now I have to change all the coercions from (p :> printable) to (p :> stringable). If it had been legal to write: let p = new point (1, 2) ;; let pr = new printer ;; pr#print p ;; there would be nothing more to change; all the calls to 'pr#print' would still work. 2. The second option is to write 'printer' like this: class printer = object method print : 'a. (#printable as 'a) -> unit = fun obj -> obj#print() end ;; This syntax is horribly awkward. It would be very unpleasant to have to write (or read) a lot of methods in this style. Moreover, it seems strange to have to do this, because I can write a function like this: let print (obj : #printable) = obj#print() ;; Or even: let print obj = obj#print() ;; So why can't I write: class printer = object method (obj : #printable) = obj#print() end ;; Or even: class printer = object method obj = obj#print() end ;; Why isn't a method just like a function in this respect? Of course, I could write 'printer' as a function instead of a class. But this would lead to an approach in which objects are manipulated only by functions, and never by other objects. If that were really the only convenient way to use classes in Caml, it would be difficult to say that Caml supported object-oriented programming. So my questions are: Does it really need to be this complicated? Could the language be improved so that either (1) explicit coercions to a base class were not needed or (b) methods could use types the way functions do? Is there a more convenient approach that I've missed? What do people generally do in order to get round this problem, when using classes in Caml? Ben ------------------- 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] 30+ messages in thread
* Re: [Caml-list] does class polymorphism need to be so complicated? 2003-08-20 15:42 [Caml-list] does class polymorphism need to be so complicated? Benjamin Geer @ 2003-08-20 16:05 ` Brian Hurt 2003-08-20 16:19 ` Richard Jones ` (2 more replies) 0 siblings, 3 replies; 30+ messages in thread From: Brian Hurt @ 2003-08-20 16:05 UTC (permalink / raw) To: Benjamin Geer; +Cc: caml-list On Wed, 20 Aug 2003, Benjamin Geer wrote: > class printer = > object > method print (obj : printable) = obj#print() > end ;; Instead of declaring obj to be printable, why not just declare that it has a function print? Like: class printer = object method print (obj: <print: unit->unit>) = obj#print (); end;; This removes the need for a coercion, as it gets around the need to upcast. Brian ------------------- 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] 30+ messages in thread
* Re: [Caml-list] does class polymorphism need to be so complicated? 2003-08-20 16:05 ` Brian Hurt @ 2003-08-20 16:19 ` Richard Jones 2003-08-20 16:25 ` Benjamin Geer 2003-08-20 20:43 ` Issac Trotts 2 siblings, 0 replies; 30+ messages in thread From: Richard Jones @ 2003-08-20 16:19 UTC (permalink / raw) To: Brian Hurt; +Cc: Benjamin Geer, caml-list On Wed, Aug 20, 2003 at 11:05:35AM -0500, Brian Hurt wrote: > Instead of declaring obj to be printable, why not just declare that it has > a function print? Like: > > class printer = > object > method print (obj: <print: unit->unit>) = obj#print (); > end;; > > This removes the need for a coercion, as it gets around the need to > upcast. Interesting. Am I right in thinking that the <print : unit -> unit> type syntax can refer to _any_ object which has a print method, regardless of class hierarchy? This could be quite a fun feature, although I'm not quite sure of the best way to use it ... Rich. -- Richard Jones. http://www.annexia.org/ http://freshmeat.net/users/rwmj Merjis Ltd. http://www.merjis.com/ - all your business data are belong to you. 'There is a joke about American engineers and French engineers. The American team brings a prototype to the French team. The French team's response is: "Well, it works fine in practice; but how will it hold up in theory?"' ------------------- 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] 30+ messages in thread
* Re: [Caml-list] does class polymorphism need to be so complicated? 2003-08-20 16:05 ` Brian Hurt 2003-08-20 16:19 ` Richard Jones @ 2003-08-20 16:25 ` Benjamin Geer 2003-08-20 17:09 ` brogoff 2003-08-20 20:43 ` Issac Trotts 2 siblings, 1 reply; 30+ messages in thread From: Benjamin Geer @ 2003-08-20 16:25 UTC (permalink / raw) To: Brian Hurt; +Cc: caml-list Brian Hurt wrote: > Instead of declaring obj to be printable, why not just declare that it has > a function print? Like: > > class printer = > object > method print (obj: <print: unit->unit>) = obj#print (); > end;; > > This removes the need for a coercion, as it gets around the need to > upcast. That's pretty cumbersome, because it will have to be repeated for every method that uses an object of that type. And suppose you need the method's argument to be an object with several methods. You could end up writing methods like this: class thing_processor = object method do_something (t : obj: <foo: int->int; bar: unit->string; baz: unit->bool ; quux: unit->unit>) = (* do something that calls all those methods *) method do_something_else (t : obj: <foo: int->int; bar: unit->string; baz: unit->bool; quux: unit->unit>) = (* do something else that calls all those methods *) end;; It would seem natural to define an interface as a shorthand, and to use it like this: class type thing = object method foo : int -> int method bar : string method baz : bool method quux : unit end;; class thing_processor = object method do_something (t : #thing) = (* do something that calls all those methods *) method do_something_else (t : #thing) = (* do something that calls all those methods *) end;; But alas, this is not valid in Caml. Ben ------------------- 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] 30+ messages in thread
* Re: [Caml-list] does class polymorphism need to be so complicated? 2003-08-20 16:25 ` Benjamin Geer @ 2003-08-20 17:09 ` brogoff 2003-08-20 17:25 ` Jacques Carette 2003-08-20 18:19 ` Benjamin Geer 0 siblings, 2 replies; 30+ messages in thread From: brogoff @ 2003-08-20 17:09 UTC (permalink / raw) To: Benjamin Geer; +Cc: Brian Hurt, caml-list The extension of Brian's code to rows with more than one field is obvious though, isn't it? type fbbq = <foo: int->int; bar: unit->string; baz: unit->bool;quux: unit->unit> class thing_processor = object method do_something (o : fbbq) = (* do something that calls all those methods *) method do_something_else (o : fbbq) = (* do something else that calls all those methods *) end -- Brian ------------------- 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] 30+ messages in thread
* RE: [Caml-list] does class polymorphism need to be so complicated? 2003-08-20 17:09 ` brogoff @ 2003-08-20 17:25 ` Jacques Carette 2003-08-20 23:34 ` Jacques Garrigue 2003-08-20 18:19 ` Benjamin Geer 1 sibling, 1 reply; 30+ messages in thread From: Jacques Carette @ 2003-08-20 17:25 UTC (permalink / raw) To: brogoff, 'Benjamin Geer'; +Cc: 'Brian Hurt', caml-list > The extension of Brian's code to rows with more than one field is obvious > though, isn't it? Indeed - but that rather begs the question of why are classes and rows different, as they (naively perhaps) seem so ripe for 'unification'. The work on dependent records in Coq seems highly related (and looks quite successful, at least in the context of the FOC project). Jacques ------------------- 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] 30+ messages in thread
* RE: [Caml-list] does class polymorphism need to be so complicated? 2003-08-20 17:25 ` Jacques Carette @ 2003-08-20 23:34 ` Jacques Garrigue 2003-08-21 13:27 ` Jacques Carette 0 siblings, 1 reply; 30+ messages in thread From: Jacques Garrigue @ 2003-08-20 23:34 UTC (permalink / raw) To: carette; +Cc: caml-list From: "Jacques Carette" <carette@mcmaster.ca> > > The extension of Brian's code to rows with more than one field is obvious > > though, isn't it? > > Indeed - but that rather begs the question of why are classes and rows > different, as they (naively perhaps) seem so ripe for 'unification'. > > The work on dependent records in Coq seems highly related (and looks quite > successful, at least in the context of the FOC project). I'm not sure of what you mean by rows. At least, in the above examples, rows were used as a name for object types. And a class type is just an object type plus a bit more information. In that respect, I would say they are one and the same thing, and there is no unification needed. class type printable = object method print : unit end also defines the type type printable = < print : unit > Of course there could be a discussion on whether we really need class types, or whether classes should define a class type or not, etc... 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] 30+ messages in thread
* Re: [Caml-list] does class polymorphism need to be so complicated? 2003-08-20 23:34 ` Jacques Garrigue @ 2003-08-21 13:27 ` Jacques Carette 0 siblings, 0 replies; 30+ messages in thread From: Jacques Carette @ 2003-08-21 13:27 UTC (permalink / raw) To: Jacques Garrigue; +Cc: caml-list Jacques Garrigue <garrigue@kurims.kyoto-u.ac.jp> wrote: > From: "Jacques Carette" <carette@mcmaster.ca> > > Indeed - but that rather begs the question of why are classes and rows > > different, as they (naively perhaps) seem so ripe for 'unification'. > > > I'm not sure of what you mean by rows. > At least, in the above examples, rows were used as a name for object > types. By 'rows' I meant the type of that name referred to in say François Pottier, "A Constraint-Based Presentation and Generalization of Rows" (available from http://pauillac.inria.fr/~fpottier/biblio/pottier.html). The sub-typing of classes and of rows seem to me to be highly related. Treating classes as rows would seem to me to allow the kind of polymorphism that is being asked for in this thread (ie with no need for explicit coercions). Jacques C. ------------------- 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] 30+ messages in thread
* Re: [Caml-list] does class polymorphism need to be so complicated? 2003-08-20 17:09 ` brogoff 2003-08-20 17:25 ` Jacques Carette @ 2003-08-20 18:19 ` Benjamin Geer 2003-08-20 20:39 ` brogoff 1 sibling, 1 reply; 30+ messages in thread From: Benjamin Geer @ 2003-08-20 18:19 UTC (permalink / raw) To: brogoff; +Cc: Brian Hurt, caml-list brogoff@speakeasy.net wrote: > The extension of Brian's code to rows with more than one field is obvious > though, isn't it? > > type fbbq = > <foo: int->int; bar: unit->string; baz: unit->bool;quux: unit->unit> > > class thing_processor = > object > method do_something (o : fbbq) = > (* do something that calls all those methods *) > method do_something_else (o : fbbq) = > (* do something else that calls all those methods *) > end Unless I've missed something, this only works if none of the classes of type fbbq have any additional methods. Here's an example that doesn't work: type fbbq = <foo: int->int; bar: string; baz: bool; quux: unit > ;; class thing x_init = object (self) val mutable x = x_init method foo y = x + y method bar = string_of_int (self#foo 2) method baz = (x = 1) method quux = print_string (string_of_int x) end;; class extra_thing x_init = object (self) inherit thing x_init method quuux = self#quux; self#quux end;; class thing_processor = object method process (obj : fbbq) = obj#quux; print_string (string_of_int (obj#foo 1)); print_string obj#bar; print_string (string_of_bool obj#baz) end ;; let et = new extra_thing 1 ;; let tp = new thing_processor ;; tp#process et ;; The call to 'process' produces the following error: This expression has type extra_thing = < bar : string; baz : bool; foo : int -> int; quuux : unit; quux : unit > but is here used with type fbbq = < bar : string; baz : bool; foo : int -> int; quux : unit > Only the first object type has a method quuux So this approach doesn't fit the requirement, which is to be able to use a derived class anywhere its base class can be used. Ben ------------------- 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] 30+ messages in thread
* Re: [Caml-list] does class polymorphism need to be so complicated? 2003-08-20 18:19 ` Benjamin Geer @ 2003-08-20 20:39 ` brogoff 2003-08-20 21:04 ` Benjamin Geer 2003-08-20 23:40 ` Benjamin Geer 0 siblings, 2 replies; 30+ messages in thread From: brogoff @ 2003-08-20 20:39 UTC (permalink / raw) To: Benjamin Geer; +Cc: Brian Hurt, caml-list On Wed, 20 Aug 2003, Benjamin Geer wrote: > Unless I've missed something, this only works if none of the classes of > type fbbq have any additional methods. Here's an example that doesn't work: Yes, OCaml doesn't have implicit subtyping. The language manual is quite clear on that. If you want to solve your problem, you'll need to coerce, and you can solve it fairly simply by replacing the troublesome line below with one of the following tp#process (et :> fbbq) ;; or let proc o = (new thing_processor)#process (o :> fbbq);; proc et;; or stuff like that. You get the picture. The (row) type representing "fbbq or anything which has the same methods" is this type 'a fbbq_c = 'a constraint 'a = < foo: int->int; bar: unit->string; baz: unit->bool; quux: unit->unit; .. > where as you can see there is a type variable to handle that row polymorphism. So, if you want a method which takes that as an argument, it has to be polymorphic. Easier to solve it as I did above with a function wrappers and coercions. A solution using polymorphic methods is sketched next, where I use them to fake implicit subtyping class type thing = object method foo : int -> int method bar : string method baz : bool method quux : unit end;; class thing_processor = object method process : 'a . (#thing as 'a ) -> unit = fun obj -> begin obj#quux; print_string (string_of_int (obj#foo 1)); print_string obj#bar; print_string (string_of_bool obj#baz); print_endline "" end end ;; let tp = new thing_processor ;; tp#process et ;; so I guess you are now left with a Perl like TMTOWTDI situation. Overall, yes, the class system is complicated. -- Brian > type fbbq = > <foo: int->int; bar: string; baz: bool; quux: unit > ;; > > class thing x_init = > object (self) > val mutable x = x_init > method foo y = x + y > method bar = string_of_int (self#foo 2) > method baz = (x = 1) > method quux = print_string (string_of_int x) > end;; > > class extra_thing x_init = > object (self) > inherit thing x_init > method quuux = self#quux; self#quux > end;; > > class thing_processor = > object > method process (obj : fbbq) = > obj#quux; > print_string (string_of_int (obj#foo 1)); > print_string obj#bar; > print_string (string_of_bool obj#baz) > end ;; > > let et = new extra_thing 1 ;; > let tp = new thing_processor ;; > tp#process et ;; > > The call to 'process' produces the following error: > > This expression has type > extra_thing = > < bar : string; baz : bool; foo : int -> int; quuux : unit; quux : > unit > > but is here used with type > fbbq = < bar : string; baz : bool; foo : int -> int; quux : unit > > Only the first object type has a method quuux > > So this approach doesn't fit the requirement, which is to be able to use > a derived class anywhere its base class can be used. > > Ben > > ------------------- > 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 ^ permalink raw reply [flat|nested] 30+ messages in thread
* Re: [Caml-list] does class polymorphism need to be so complicated? 2003-08-20 20:39 ` brogoff @ 2003-08-20 21:04 ` Benjamin Geer 2003-08-21 0:28 ` Jacques Garrigue 2003-08-21 0:58 ` brogoff 2003-08-20 23:40 ` Benjamin Geer 1 sibling, 2 replies; 30+ messages in thread From: Benjamin Geer @ 2003-08-20 21:04 UTC (permalink / raw) To: brogoff; +Cc: caml-list brogoff@speakeasy.net wrote: > If you want to solve your problem, you'll need to coerce, > [...] > A solution using polymorphic methods is sketched next, These are the two options I described in my original post. Both are inconvenient. My original questions remain: Why is upcasting necessary, given that inheritance relationships are known at compile time? Could Caml be modified to correct this problem? Is any work currently being done on this? Why can't methods be polymorphic in the way that functions can be? At the very least, would it be possible to add some syntactic sugar, so we could write: method process (obj : #thing) -> (* ... *) instead of: method process : 'a . (#thing as 'a ) -> unit = fun obj -> (* ... *) It is puzzling that functions provide much better polymorphism than methods; could the Caml experts provide an explanation? Does any of the current research into Caml extensions offer a possibility of improving polymorphism for methods? Ben ------------------- 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] 30+ messages in thread
* Re: [Caml-list] does class polymorphism need to be so complicated? 2003-08-20 21:04 ` Benjamin Geer @ 2003-08-21 0:28 ` Jacques Garrigue 2003-08-21 8:17 ` Benjamin Geer 2003-08-21 0:58 ` brogoff 1 sibling, 1 reply; 30+ messages in thread From: Jacques Garrigue @ 2003-08-21 0:28 UTC (permalink / raw) To: ben; +Cc: caml-list From: Benjamin Geer <ben@socialtools.net> > brogoff@speakeasy.net wrote: > > If you want to solve your problem, you'll need to coerce, > > [...] > > A solution using polymorphic methods is sketched next, > > These are the two options I described in my original post. Both are > inconvenient. My original questions remain: > > Why is upcasting necessary, given that inheritance relationships are > known at compile time? Could Caml be modified to correct this problem? > Is any work currently being done on this? Upcasting is needed because type inference in ocaml does not include subtyping. There is work on type inference with subtyping, but it would create much more complicated types, even for usual functions which do not involve objects. Personally I don't think there is a strong hope of combining full ML type inference with subtyping in a practical programming language. Now, ocaml tries to avoid the problem by providing something similar to subtyping, but through ML polymorphism. This is the #printable types. This is not exactly subtyping (in some cases an instance of #printable is not a subtype of printable) but it is actually sometimes closer to the notion of "usable in place of". When using functions, this approach using "open rows" works well. However there is a problem with methods. > Why can't methods be polymorphic in the way that functions can be? Methods are part of an object. In normal ML this means that the polymorphism can only appear at the level of the object, not at the level of the method (ML only allows outermost polymorphism). As an additional twist, since in ocaml a class definition also defines a type, all polymorphic variables must be explicitely declared as parameters to the class, otherwise you get the dreaded Some type variables are unbound in this type Anyway, what you want in many cases is not the object, but the method to be polymorphic: you want to be able to apply the same method of the same object to values of different types. This is now possible with the introduction of polymorphic methods, but only with a rather heavy syntax. There are several reasons to that: * since the "normal" behaviour would be to restrict the polymorphism to the object level, choosing to apply it automatically at the method level would be an arbitrary decision. * contrary to functions, whose types are instantiated before unification, object method types have to be unified with their polymorphic variables not instantiated (because an object with a polymorphic method can be passed around, while only an instance of a polymorphic function would be passed around). This means that the "most general type" we would infer for a method would give rise to an object type incompatible with less general types. Not a good property for inference. * last, by explicitely declaring a polymorphic type we make it possible to call it polymorphically from other methods in the same object. This seems to be a nice property to have between methods. You cannot do that with functions defined in the same "let rec" statement. > At the very least, would it be possible to add some syntactic sugar, > so we could write: > > method process (obj : #thing) -> (* ... *) > > instead of: > > method process : 'a . (#thing as 'a ) -> unit = > fun obj -> (* ... *) To speak truly, the current syntax is based on the assumption that you won't define often polymorphic methods, and that defining them is a work for library designers, not for the average end user. This also means that you have a number of workarounds hiding this heavy syntax to the end user, even when he has to define such a method. For instance you could be provided a virtual class printer: class virtual printer : object method virtual print : #printable -> unit method ... end Then you would use it as class my_printer () = object inherit printer method print obj = ... end There is no need to write type information on the inheriting side. You will get an error message if your method is not polymorphic enough. Another remark is that, when the only goal of the polymorphism is to allow subtyping of the argument, the workaround of using an auxiliary function as suggested by Brian Rogoff makes perfectly sense, and avoids getting involved in the details of the object system. class printer () = object method print (obj : printable) = ... end let print ~(printer : #printer) obj = printer#print (obj : #printable :> printable) val print : printer:#printer -> #printable -> unit Now the print function has a type polymorphic enough, and using it makes clear you are using the print method of a printer, not of an arbitrary object (which might have a completely unrelated print method). This is more precise, and does the work in the traditional ML way. Cheers, Jacques Garrigue P.S. Having a lighter syntax for polymorphic methods might be a good idea. But since we must keep it explicit enough, the improvement would be quite limited. The best I can think of is something like: method 'a. print (obj : #printable as 'a) = ... Maybe a bit better, but also more complicated to handle. An advantage of such a syntax is that it could also be used in normal "let rec" to provide polymorphic recursion. ------------------- 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] 30+ messages in thread
* Re: [Caml-list] does class polymorphism need to be so complicated? 2003-08-21 0:28 ` Jacques Garrigue @ 2003-08-21 8:17 ` Benjamin Geer 2003-08-21 8:58 ` Jacques Garrigue 0 siblings, 1 reply; 30+ messages in thread From: Benjamin Geer @ 2003-08-21 8:17 UTC (permalink / raw) To: Jacques Garrigue; +Cc: caml-list Jacques Garrigue wrote: > [explanation of issues concerning method polymorphism] Thank you for explaining this. > To speak truly, the current syntax is based on the assumption that you > won't define often polymorphic methods, and that defining them is a > work for library designers, not for the average end user. I think that one of the things that would improve life a great deal, for people wanting to write applications in Caml, would be the existence of many more libraries. Unfortunately, I think languages become popular not mainly because of how expressive they are, but because of the libraries available in them. Therefore, in order to help Caml become more widely used, it would be a good idea to make things as easy as possible for library authors. (That's actually why I came to this list; I want to write libraries in Caml, to make it more generally useful for writing applications.) Moreover, a library user needs to handle the library's own polymorphism. For example, suppose there were a Caml API for accessing databases, and that this API consisted entirely of class types, intended to be implemented by Caml 'drivers' for different databases. The library user would get a #connection; the class implementing #connection would be determined by the driver (and would never be known by the library user). In this way, the user could switch to a different database by switching to a different driver, without having to change any application code. In order to pass around this #connection object within the application, the library user would have to write polymorphic methods. > This also means that you have a number of workarounds hiding this > heavy syntax to the end user, even when he has to define such a > method. > > For instance you could be provided a virtual class printer: > > class virtual printer : object > method virtual print : #printable -> unit > method ... > end > > Then you would use it as > > class my_printer () = object > inherit printer > method print obj = ... > end That's somewhat better, but it means that every class must be derived from a virtual base, even when there's no other reason for it. > P.S. Having a lighter syntax for polymorphic methods might be a good > idea. But since we must keep it explicit enough, the improvement would > be quite limited. The best I can think of is something like: > method 'a. print (obj : #printable as 'a) = ... > Maybe a bit better, but also more complicated to handle. I think that would definitely be an improvement. > An advantage of such a syntax is that it could also be used in normal > "let rec" to provide polymorphic recursion. That would be a big advantage in my view. Ben ------------------- 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] 30+ messages in thread
* Re: [Caml-list] does class polymorphism need to be so complicated? 2003-08-21 8:17 ` Benjamin Geer @ 2003-08-21 8:58 ` Jacques Garrigue 2003-08-21 9:38 ` Benjamin Geer 2003-08-21 13:38 ` Benjamin Geer 0 siblings, 2 replies; 30+ messages in thread From: Jacques Garrigue @ 2003-08-21 8:58 UTC (permalink / raw) To: ben; +Cc: caml-list From: Benjamin Geer <ben@socialtools.net> > > To speak truly, the current syntax is based on the assumption that you > > won't define often polymorphic methods, and that defining them is a > > work for library designers, not for the average end user. > > I think that one of the things that would improve life a great deal, for > people wanting to write applications in Caml, would be the existence of > many more libraries. Unfortunately, I think languages become popular > not mainly because of how expressive they are, but because of the > libraries available in them. Therefore, in order to help Caml become > more widely used, it would be a good idea to make things as easy as > possible for library authors. Sure. There's no intent to make it difficult. The idea is only that being a bit more verbose on a declaration that is hopefully made only once in a hierarchy is not that bad. The real problem actually is not verbosity, but the fact you have to understand that there can be two levels for polymorphism: the class or the method. I think that's not that immediate, and I don't want to bother beginners with that. We'll see the impact on Java programmers when they will get generics. > Moreover, a library user needs to handle the library's own polymorphism. > For example, suppose there were a Caml API for accessing databases, > and that this API consisted entirely of class types, intended to be > implemented by Caml 'drivers' for different databases. The library user > would get a #connection; the class implementing #connection would be > determined by the driver (and would never be known by the library user). > In this way, the user could switch to a different database by > switching to a different driver, without having to change any > application code. In order to pass around this #connection object > within the application, the library user would have to write polymorphic > methods. Here there may be a deeper misunderstanding about the ocaml type system: if a subclass does not add methods to its superclass, its type does not change. That is, I would expect all connections to have the same type, and as a result there is no need for considering the more general #connection. > > This also means that you have a number of workarounds hiding this > > heavy syntax to the end user, even when he has to define such a > > method. > > > > For instance you could be provided a virtual class printer: > > > > class virtual printer : object > > method virtual print : #printable -> unit > > method ... > > end > > > > Then you would use it as > > > > class my_printer () = object > > inherit printer > > method print obj = ... > > end > > That's somewhat better, but it means that every class must be derived > from a virtual base, even when there's no other reason for it. OK, there's also another way to do it, without inheritance. I just tried not to be confusing. class type printer = object method virtual print : #printable -> unit end class my_printer () = object (self : #printer) method print obj = ... end Looks a bit strange at first, but it does the work. > > P.S. Having a lighter syntax for polymorphic methods might be a good > > idea. But since we must keep it explicit enough, the improvement would > > be quite limited. The best I can think of is something like: > > method 'a. print (obj : #printable as 'a) = ... > > Maybe a bit better, but also more complicated to handle. > > I think that would definitely be an improvement. Might consider it. But its a rather big change in the language, so this requires some more study. Jacques ------------------- 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] 30+ messages in thread
* Re: [Caml-list] does class polymorphism need to be so complicated? 2003-08-21 8:58 ` Jacques Garrigue @ 2003-08-21 9:38 ` Benjamin Geer 2003-08-21 11:44 ` Remi Vanicat 2003-08-21 18:04 ` brogoff 2003-08-21 13:38 ` Benjamin Geer 1 sibling, 2 replies; 30+ messages in thread From: Benjamin Geer @ 2003-08-21 9:38 UTC (permalink / raw) To: Jacques Garrigue; +Cc: caml-list Jacques Garrigue wrote: > Here there may be a deeper misunderstanding about the ocaml type > system: if a subclass does not add methods to its superclass, its type > does not change. > That is, I would expect all connections to have the same type, and as > a result there is no need for considering the more general > #connection. I think this would place an undesirable restriction on driver authors. They may want to add additional methods to their implementation of #connection, for use by other classes in the driver, even if the user will never be able to call those methods. In general, this approach allows for a complete separation between interface and implementation: the implementing class can always have more methods than the interface, if this makes the implementation more convenient to write. Alternatively, you could use a virtual base class 'connection', and always downcast the implementing class before passing it to application code. But this places an additional burden on the library author. > OK, there's also another way to do it, without inheritance. I just > tried not to be confusing. > > class type printer = object > method virtual print : #printable -> unit > end > > class my_printer () = object (self : #printer) > method print obj = ... > end > > Looks a bit strange at first, but it does the work. Is there a way to write a class that implements more than one interface? I've tried the following, but it produces a syntax error: class type virtual printer = object method virtual print : #printable -> unit end ;; class type virtual talker = object method virtual talk : #printable -> unit end ;; class my_printer_talker () = object (self : #printer; #talker) method print obj = (* ... *) method talk obj = (* ... *) end ;; Am I right in guessing that you have to use multiple inheritance to achieve this? Ben ------------------- 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] 30+ messages in thread
* Re: [Caml-list] does class polymorphism need to be so complicated? 2003-08-21 9:38 ` Benjamin Geer @ 2003-08-21 11:44 ` Remi Vanicat 2003-08-21 13:11 ` Richard Jones 2003-08-21 18:04 ` brogoff 1 sibling, 1 reply; 30+ messages in thread From: Remi Vanicat @ 2003-08-21 11:44 UTC (permalink / raw) To: caml-list Benjamin Geer <ben@socialtools.net> writes: > Jacques Garrigue wrote: >> OK, there's also another way to do it, without inheritance. I just >> tried not to be confusing. >> class type printer = object >> method virtual print : #printable -> unit >> end >> class my_printer () = object (self : #printer) >> method print obj = ... >> end >> Looks a bit strange at first, but it does the work. > > Is there a way to write a class that implements more than one > interface? I've tried the following, but it produces a syntax error: > > class type virtual printer = object > method virtual print : #printable -> unit > end ;; > > class type virtual talker = object > method virtual talk : #printable -> unit > end ;; > > class my_printer_talker () = object (self : #printer; #talker) > method print obj = (* ... *) > method talk obj = (* ... *) > end ;; well this work : class my_printer_talker () = object (self : 's) constraint 's = #printer constraint 's = #talker method print obj = (* ... *) method talk obj = (* ... *) end ;; -- Rémi Vanicat vanicat@labri.u-bordeaux.fr http://dept-info.labri.u-bordeaux.fr/~vanicat ------------------- 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] 30+ messages in thread
* Re: [Caml-list] does class polymorphism need to be so complicated? 2003-08-21 11:44 ` Remi Vanicat @ 2003-08-21 13:11 ` Richard Jones 2003-08-21 16:41 ` Remi Vanicat 0 siblings, 1 reply; 30+ messages in thread From: Richard Jones @ 2003-08-21 13:11 UTC (permalink / raw) Cc: caml-list On Thu, Aug 21, 2003 at 01:44:23PM +0200, Remi Vanicat wrote: > Benjamin Geer <ben@socialtools.net> writes: > > class type virtual printer = object > > method virtual print : #printable -> unit > > end ;; > > > > class type virtual talker = object > > method virtual talk : #printable -> unit > > end ;; > > > > class my_printer_talker () = object (self : #printer; #talker) > > method print obj = (* ... *) > > method talk obj = (* ... *) > > end ;; > > well this work : > > class my_printer_talker () = object (self : 's) > constraint 's = #printer > constraint 's = #talker > method print obj = (* ... *) > method talk obj = (* ... *) > end ;; Is there a way to add extra methods to my_printer_talker? Rich. -- Richard Jones. http://www.annexia.org/ http://freshmeat.net/users/rwmj Merjis Ltd. http://www.merjis.com/ - all your business data are belong to you. 'There is a joke about American engineers and French engineers. The American team brings a prototype to the French team. The French team's response is: "Well, it works fine in practice; but how will it hold up in theory?"' ------------------- 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] 30+ messages in thread
* Re: [Caml-list] does class polymorphism need to be so complicated? 2003-08-21 13:11 ` Richard Jones @ 2003-08-21 16:41 ` Remi Vanicat 0 siblings, 0 replies; 30+ messages in thread From: Remi Vanicat @ 2003-08-21 16:41 UTC (permalink / raw) To: caml-list Richard Jones <rich@annexia.org> writes: > On Thu, Aug 21, 2003 at 01:44:23PM +0200, Remi Vanicat wrote: >> Benjamin Geer <ben@socialtools.net> writes: >> > class type virtual printer = object >> > method virtual print : #printable -> unit >> > end ;; >> > >> > class type virtual talker = object >> > method virtual talk : #printable -> unit >> > end ;; >> > >> > class my_printer_talker () = object (self : #printer; #talker) >> > method print obj = (* ... *) >> > method talk obj = (* ... *) >> > end ;; >> >> well this work : >> >> class my_printer_talker () = object (self : 's) >> constraint 's = #printer >> constraint 's = #talker >> method print obj = (* ... *) >> method talk obj = (* ... *) >> end ;; > > Is there a way to add extra methods to my_printer_talker? Yes. Add them. the constraint 's = #foo only say that the current class must have at least the method specified in foo, and with the the type specified in foo. but you can still make the type grow. -- Rémi Vanicat vanicat@labri.u-bordeaux.fr http://dept-info.labri.u-bordeaux.fr/~vanicat ------------------- 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] 30+ messages in thread
* Re: [Caml-list] does class polymorphism need to be so complicated? 2003-08-21 9:38 ` Benjamin Geer 2003-08-21 11:44 ` Remi Vanicat @ 2003-08-21 18:04 ` brogoff 2003-08-21 20:20 ` Benjamin Geer 1 sibling, 1 reply; 30+ messages in thread From: brogoff @ 2003-08-21 18:04 UTC (permalink / raw) To: Benjamin Geer; +Cc: Jacques Garrigue, caml-list On Thu, 21 Aug 2003, Benjamin Geer wrote: > Alternatively, you could use a virtual base class 'connection', and > always downcast the implementing class before passing it to application > code. But this places an additional burden on the library author. I think the burden is very slight, but I have no problem at all with using functions outside of objects. Realistic implementations would provide coercion functions for every base class you want to coerce to, perhaps named something like "as_base_class_name". Using the exmaple you give in another message, we get something like this (* A simple API *) class virtual connection = object method virtual close : unit end ;; class virtual driver = object method virtual get_connection : string -> connection end ;; let as_connection o = (o : #connection :> connection);; (* An implementation of the API *) class mysql_connection db_name = object inherit connection val _db_name = db_name method close = print_string "closing connection "; print_string _db_name; print_newline(); (* An extra method, which could be used by the driver *) method get_status = "OK" end ;; class mysql_driver = object inherit driver method get_connection db_name = as_connection (new mysql_connection db_name) end;; which doesn't seem bad compared to your original desired code. -- Brian ------------------- 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] 30+ messages in thread
* Re: [Caml-list] does class polymorphism need to be so complicated? 2003-08-21 18:04 ` brogoff @ 2003-08-21 20:20 ` Benjamin Geer 2003-08-21 23:35 ` Benjamin Geer 0 siblings, 1 reply; 30+ messages in thread From: Benjamin Geer @ 2003-08-21 20:20 UTC (permalink / raw) To: brogoff; +Cc: Jacques Garrigue, caml-list brogoff@speakeasy.net wrote: > I think the burden is very slight, but I have no problem at all with using > functions outside of objects. Realistic implementations would provide > coercion functions for every base class you want to coerce to, perhaps > named something like "as_base_class_name". Using the exmaple you give in > another message, we get something like this [...] Thanks for the example; I think I can live with the approach you propose. :) I agree with you (and Scott Meyers) about functions outside of objects being a good thing, as long as they're doing something useful, and not just working around syntactical problems. Or at least as long as I don't have to write a large number of them. With the approach you suggest, it looks like I only need one coercion function per base class, which I agree isn't too burdensome: > let as_connection o = (o : #connection :> connection);; I'm still curious to know why the example I gave (returning a mysql_connection from a method that was typed to return a #connection) didn't compile, though ("This method has type string -> mysql_connection which is less general than 'a. string -> (#connection as 'a)"). Ben ------------------- 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] 30+ messages in thread
* Re: [Caml-list] does class polymorphism need to be so complicated? 2003-08-21 20:20 ` Benjamin Geer @ 2003-08-21 23:35 ` Benjamin Geer 2003-08-22 3:59 ` Jacques Garrigue 0 siblings, 1 reply; 30+ messages in thread From: Benjamin Geer @ 2003-08-21 23:35 UTC (permalink / raw) To: Benjamin Geer, caml-list Benjamin Geer wrote: > I'm still curious to know why the example I gave (returning a > mysql_connection from a method that was typed to return a #connection) > didn't compile, though ("This method has type string -> mysql_connection > which is less general than 'a. string -> (#connection as 'a)"). Curiously, it works if the class is parameterised instead of the method: class type ['a] driver = object constraint 'a = #connection method get_connection : db_name:string -> 'a end class mysql_driver = object (self : 's) constraint 's = #connection #driver method get_connection ~(db_name:string) = new mysql_connection db_name end Maybe something about these different approaches should go in a FAQ somewhere. Ben ------------------- 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] 30+ messages in thread
* Re: [Caml-list] does class polymorphism need to be so complicated? 2003-08-21 23:35 ` Benjamin Geer @ 2003-08-22 3:59 ` Jacques Garrigue 2003-08-22 7:12 ` Benjamin Geer 0 siblings, 1 reply; 30+ messages in thread From: Jacques Garrigue @ 2003-08-22 3:59 UTC (permalink / raw) To: ben; +Cc: caml-list From: Benjamin Geer <ben@socialtools.net> > Benjamin Geer wrote: > > I'm still curious to know why the example I gave (returning a > > mysql_connection from a method that was typed to return a #connection) > > didn't compile, though ("This method has type string -> mysql_connection > > which is less general than 'a. string -> (#connection as 'a)"). Because #connection is _not_ an interface. It is just a polymorphic type including all types having more methods than connection. So returning a #connection means that the returned object already has all the methods in the world, with all types imaginable. Nonsense. #connection only makes sense on the left-hand side of an arrow. By the way, the typing of #connection really uses rows (as by Remy and later Pottier), but in a simplified way. So the unification Jacques Carrette is talking about is already there from the beginning of ocaml. > Curiously, it works if the class is parameterised instead of the method: > > class type ['a] driver = > object > constraint 'a = #connection > method get_connection : db_name:string -> 'a > end > > class mysql_driver = > object (self : 's) > constraint 's = #connection #driver > method get_connection ~(db_name:string) = > new mysql_connection db_name > end The meaning is completely different. Your class type says that get_connection must at least have all methods of connection. But in mysql_driver, you end up with a get_connection returning a (monomorphic) mysql_connection. No progress whatsoever. 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] 30+ messages in thread
* Re: [Caml-list] does class polymorphism need to be so complicated? 2003-08-22 3:59 ` Jacques Garrigue @ 2003-08-22 7:12 ` Benjamin Geer 0 siblings, 0 replies; 30+ messages in thread From: Benjamin Geer @ 2003-08-22 7:12 UTC (permalink / raw) To: Jacques Garrigue; +Cc: caml-list Jacques Garrigue wrote: > Because #connection is _not_ an interface. It is just a polymorphic > type including all types having more methods than connection. > So returning a #connection means that the returned object already has > all the methods in the world, with all types imaginable. Nonsense. > > #connection only makes sense on the left-hand side of an arrow. Thanks for this explanation. It shows very clearly the advantage of using the approach involving coercions: with coercions, a method can return an instance of the base class, so factory methods become possible. Ben ------------------- 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] 30+ messages in thread
* Re: [Caml-list] does class polymorphism need to be so complicated? 2003-08-21 8:58 ` Jacques Garrigue 2003-08-21 9:38 ` Benjamin Geer @ 2003-08-21 13:38 ` Benjamin Geer 1 sibling, 0 replies; 30+ messages in thread From: Benjamin Geer @ 2003-08-21 13:38 UTC (permalink / raw) To: Jacques Garrigue; +Cc: caml-list Jacques Garrigue wrote: > OK, there's also another way to do it, without inheritance. I just > tried not to be confusing. > > class type printer = object > method virtual print : #printable -> unit > end > > class my_printer () = object (self : #printer) > method print obj = ... > end I've just tried a little experiment with this, and it doesn't seem to work in the case I had in mind: (* A simple API *) class type connection = object method close : unit end ;; class type driver = object method get_connection : string -> #connection end ;; (* An implementation of the API *) class mysql_connection db_name = object (self : 's) constraint 's = #connection val _db_name = db_name method close = print_string "closing connection "; print_string _db_name; print_newline(); (* An extra method, which could be used by the driver *) method get_status = "OK" end ;; class mysql_driver = object (self : 's) constraint 's = #driver method get_connection db_name = new mysql_connection db_name end;; Caml rejects the definition of 'get_connection' in 'mysql_driver': This method has type string -> mysql_connection which is less general than 'a. string -> (#connection as 'a) Ben ------------------- 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] 30+ messages in thread
* Re: [Caml-list] does class polymorphism need to be so complicated? 2003-08-20 21:04 ` Benjamin Geer 2003-08-21 0:28 ` Jacques Garrigue @ 2003-08-21 0:58 ` brogoff 1 sibling, 0 replies; 30+ messages in thread From: brogoff @ 2003-08-21 0:58 UTC (permalink / raw) To: Benjamin Geer; +Cc: caml-list On Wed, 20 Aug 2003, Benjamin Geer wrote: > brogoff@speakeasy.net wrote: > > If you want to solve your problem, you'll need to coerce, > > [...] > > A solution using polymorphic methods is sketched next, > > These are the two options I described in my original post. Both are > inconvenient. My original questions remain: Sorry, I didn't read your original post, only Brian Hurt's reply to it. Jacques Garrigue explained the reasons why it is hard to do everything you want in OCaml, but I still think you are being a bit harsh. First, calling the methods through functions is not the uncontested evil that you make it out to be. http://www.cuj.com/documents/s=8042/cuj0002meyers/ Second, I don't think that there would be a big gain in productivity by changing the polymorphic method syntax. As JG points out, it's not too much work to ensure that the user of the code doesn't need to use that syntax, as long as the library author is careful, so for instance class virtual processor = object method virtual process : 'a . (#thing as 'a ) -> unit end;; class thing_processor = object inherit processor method process obj = begin obj#quux; print_string (string_of_int (obj#foo 1)); print_string obj#bar; print_string (string_of_bool obj#baz); print_endline "" end end ;; and in any realistic program the virtuals will be in some separate module. Having had a foot in the Ada world, I appreciate this little bit of verbosity in which the typing is all laid out. My problem is not usually the issue of changing text, but the issue of remembering what the types of things are. -- Brian ------------------- 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] 30+ messages in thread
* Re: [Caml-list] does class polymorphism need to be so complicated? 2003-08-20 20:39 ` brogoff 2003-08-20 21:04 ` Benjamin Geer @ 2003-08-20 23:40 ` Benjamin Geer 2003-08-21 1:29 ` Jacques Garrigue 1 sibling, 1 reply; 30+ messages in thread From: Benjamin Geer @ 2003-08-20 23:40 UTC (permalink / raw) To: brogoff; +Cc: caml-list brogoff@speakeasy.net wrote: > Yes, OCaml doesn't have implicit subtyping. [...] > If you want to solve your problem, you'll need to coerce, and you > can solve it fairly simply by replacing the troublesome line below with one of > the following > > tp#process (et :> fbbq) ;; > > or > > let proc o = (new thing_processor)#process (o :> fbbq);; > proc et;; I've thought some more about this idea of wrapper functions, and actually, it doesn't seem simple at all. In an object-oriented program, *all* methods are potentially polymorphic; this is what makes object orientation useful. It means that you can always pass, to a method in class C, an instance of a class that didn't exist yet when C was written. A library's author therefore doesn't need to anticipate all the classes that will ever use the library. If you used wrapper functions to do coercions, you would need a wrapper function for every method in the program. This would be extremely cumbersome and ugly, and hardly object-oriented. Doing coercions at the call site is equally cumbersome, and you lose the ability to change the method so that it accepts a less derived class. It seems to me that the idea of interfaces (class types) is quite powerful, and would be a good solution, if only the syntax for using them in method definitions were not so complicated. Ben ------------------- 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] 30+ messages in thread
* Re: [Caml-list] does class polymorphism need to be so complicated? 2003-08-20 23:40 ` Benjamin Geer @ 2003-08-21 1:29 ` Jacques Garrigue 2003-08-21 9:19 ` Benjamin Geer 2003-08-21 18:44 ` Chris Clearwater 0 siblings, 2 replies; 30+ messages in thread From: Jacques Garrigue @ 2003-08-21 1:29 UTC (permalink / raw) To: ben; +Cc: caml-list From: Benjamin Geer <ben@socialtools.net> > > let proc o = (new thing_processor)#process (o :> fbbq);; > > proc et;; > > I've thought some more about this idea of wrapper functions, and > actually, it doesn't seem simple at all. > > In an object-oriented program, *all* methods are potentially > polymorphic; this is what makes object orientation useful. It means > that you can always pass, to a method in class C, an instance of a class > that didn't exist yet when C was written. A library's author therefore > doesn't need to anticipate all the classes that will ever use the library. This *all* is clearly wrong. Even in a purely object-oriented language like smalltalk, you have methods with no arguments. And since ocaml is not purely object-oriented (Java is not either), you have plenty of methods which take only non-object arguments. Even for object arguments, not all classes make sense when extended. So we are back to an often fairly small number of methods (in my experience one or two by big class, but it may depend heavily on your design) Another approach which was not described yet, and which I use in lablgtk for instance, is to add a coercion method to classes which form the top of a hierarchy. This way you just have to write printer#print obj#printable in place of a coercion, which may be shorter and avoid strange error messages when failing. To do this you just have to add the following to the printable virtual class: class virtual printable = object (self) method virtual ... method printable = (self :> printable) end This all depends of the number of methods taking printable as argument. If there are only a few of them, wrapping functions or polymorphic methods are probably a good idea. If there are lots of them, then adding such a coercion method may make your design clearer. > Doing coercions at the call site is equally cumbersome, and you lose the > ability to change the method so that it accepts a less derived class. Arguably true (even with coercions methods). However you should not overstate the problem. Clearly, you are talking of a case where recompilation is needed. This means that you have the source code. The great strength of ML is that the compiler is able to pinpoint all necessary modifications. Hardly a big deal. If you were using Java, you would have to write explicit types for all local variables. This often makes similar type changes much more cumbersome in practice. And if you're going to change the requirements of a method in the middle of your development, this means that there was something wrong in your design. Overall object-oriented languages are much weaker than functional languages at changing design afterwards. 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] 30+ messages in thread
* Re: [Caml-list] does class polymorphism need to be so complicated? 2003-08-21 1:29 ` Jacques Garrigue @ 2003-08-21 9:19 ` Benjamin Geer 2003-08-21 18:44 ` Chris Clearwater 1 sibling, 0 replies; 30+ messages in thread From: Benjamin Geer @ 2003-08-21 9:19 UTC (permalink / raw) To: Jacques Garrigue; +Cc: caml-list Jacques Garrigue wrote: > Even for object arguments, not all classes make sense when extended. The problem is that it is often difficult to know, at the outset, whether it makes sense for a class to be extended. Sometimes you start out with a concrete class, thinking 'no one will ever want to extend this', and later you realise that it should have been an interface, because there's a real need for different implementations. It's easier to change a class into an interface if all methods that use the class don't also have to change, i.e. if the syntax for using an interface is the same as the syntax for using a class. Another approach is to use interfaces for everything. But then you really need a lightweight syntax for handling interfaces in methods. > So we are back to an often fairly small number of methods (in my > experience one or two by big class, but it may depend heavily on your > design) See my last message, about writing and using libraries. > Another approach which was not described yet, and which I use in > lablgtk for instance, is to add a coercion method to classes which form > the top of a hierarchy. [...] I agree that this is a slight improvement; it's basically a shorter coercion syntax. Why did you decide to use this approach in lablgtk, instead of the other approach you suggested (using virtual methods that accept class types)? > This all depends of the number of methods taking printable as > argument. If there are only a few of them, wrapping functions or > polymorphic methods are probably a good idea. If there are lots of > them, then adding such a coercion method may make your design clearer. It's often difficult to know, at the outset, how many methods will ultimately use any given class, particularly if you're writing a library. Perhaps initially there will only be a few, and later on there will be many. Object-oriented programming should mean that you don't have to know or care. The point about clarity is interesting, though; you seem to be saying that coercions make code clearer. While this may be true, the whole philosophy of ML seems to be about giving the programmer a choice: type specifications are necessary only when there is ambiguity, and are optional elsewhere. Since there is no possible ambiguity in the case we're talking about, wouldn't you rather have the choice? This is why the approach using 'open rows' seems more appealing to me. > And if you're going to change the requirements of a method in the > middle of your development, this means that there was something wrong > in your design. Unfortunately, as hard as we may try to create the perfect design, designs always change over time. I think programming languages should try to make those changes as painless as possible. Ben ------------------- 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] 30+ messages in thread
* Re: [Caml-list] does class polymorphism need to be so complicated? 2003-08-21 1:29 ` Jacques Garrigue 2003-08-21 9:19 ` Benjamin Geer @ 2003-08-21 18:44 ` Chris Clearwater 1 sibling, 0 replies; 30+ messages in thread From: Chris Clearwater @ 2003-08-21 18:44 UTC (permalink / raw) To: caml-list On Thu, Aug 21, 2003 at 10:29:46AM +0900, Jacques Garrigue wrote: > Another approach which was not described yet, and which I use in > lablgtk for instance, is to add a coercion method to classes which form > the top of a hierarchy. This way you just have to write > printer#print obj#printable > in place of a coercion, which may be shorter and avoid strange error > messages when failing. > To do this you just have to add the following to the printable virtual > class: > class virtual printable = object (self) > method virtual ... > method printable = (self :> printable) > end Another approach if you are using abstract base classes to simulate an interface it to ditch the object system altogether and use closures. You might do something like this: printable.ml: type printable = { print: unit -> unit; } let create p print = { print = fun () -> print p } let print p = p.print () circle.ml: type circle = {radius: float} let create r = {radius=r} let print c = Printf.printf "Circle, radius: %f" c.radius let as_printable c = Printable.create c print main.ml: let c = Circle.create 10.0 let p = Circle.as_printable c let _ = Printable.print p Some nice side effects of this is that you get better type inference, non-virtual function calls when you dont need an abstract type (Circle.print) and probally better performance for virtual calls as well. Also keep in mind that for any virtual function that takes more arguments besides the object itself (most of them) you can avoid the unit hack and use partial application (say draw took a size and color arguments): drawable.ml: type drawable = { draw: int -> color -> unit } let create d draw = { draw = draw d } let draw d = d.draw etc.. main.ml somewhere: .. Drawable.draw c 10 red > And if you're going to change the requirements of a method in the > middle of your development, this means that there was something wrong > in your design. Overall object-oriented languages are much weaker > than functional languages at changing design afterwards. With this method you just change the as_printable functions :) ------------------- 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] 30+ messages in thread
* Re: [Caml-list] does class polymorphism need to be so complicated? 2003-08-20 16:05 ` Brian Hurt 2003-08-20 16:19 ` Richard Jones 2003-08-20 16:25 ` Benjamin Geer @ 2003-08-20 20:43 ` Issac Trotts 2 siblings, 0 replies; 30+ messages in thread From: Issac Trotts @ 2003-08-20 20:43 UTC (permalink / raw) To: caml-list Brian Hurt wrote: >On Wed, 20 Aug 2003, Benjamin Geer wrote: > > > >>class printer = >> object >> method print (obj : printable) = obj#print() >> end ;; >> >> > >Instead of declaring obj to be printable, why not just declare that it has >a function print? Like: > >class printer = > object > method print (obj: <print: unit->unit>) = obj#print (); > end;; > > You still have to up-cast objects having more methods than just 'print'. # class printer = object method print (o:<print:unit>) = o#print end;; # class foo = object method print = print_string "[foo]" end;; # class bar = object method print = print_string "[bar]" method frob = () end;; # let f = new foo;; val f : foo = <obj> # let p = new printer;; val p : printer = <obj> # let b = new bar;; val b : bar = <obj> # p#print f;; [foo]- : unit = () # p#print b;; Characters 8-9: p#print b;; ^ This expression has type bar = < frob : unit; print : unit > but is here used with type foo = < print : unit > Only the first object type has a method frob # p#print (b :> <print:unit>);; [bar]- : unit = () Issac >This removes the need for a coercion, as it gets around the need to >upcast. > >Brian > > >------------------- >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 ^ permalink raw reply [flat|nested] 30+ messages in thread
end of thread, other threads:[~2003-08-22 7:17 UTC | newest] Thread overview: 30+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2003-08-20 15:42 [Caml-list] does class polymorphism need to be so complicated? Benjamin Geer 2003-08-20 16:05 ` Brian Hurt 2003-08-20 16:19 ` Richard Jones 2003-08-20 16:25 ` Benjamin Geer 2003-08-20 17:09 ` brogoff 2003-08-20 17:25 ` Jacques Carette 2003-08-20 23:34 ` Jacques Garrigue 2003-08-21 13:27 ` Jacques Carette 2003-08-20 18:19 ` Benjamin Geer 2003-08-20 20:39 ` brogoff 2003-08-20 21:04 ` Benjamin Geer 2003-08-21 0:28 ` Jacques Garrigue 2003-08-21 8:17 ` Benjamin Geer 2003-08-21 8:58 ` Jacques Garrigue 2003-08-21 9:38 ` Benjamin Geer 2003-08-21 11:44 ` Remi Vanicat 2003-08-21 13:11 ` Richard Jones 2003-08-21 16:41 ` Remi Vanicat 2003-08-21 18:04 ` brogoff 2003-08-21 20:20 ` Benjamin Geer 2003-08-21 23:35 ` Benjamin Geer 2003-08-22 3:59 ` Jacques Garrigue 2003-08-22 7:12 ` Benjamin Geer 2003-08-21 13:38 ` Benjamin Geer 2003-08-21 0:58 ` brogoff 2003-08-20 23:40 ` Benjamin Geer 2003-08-21 1:29 ` Jacques Garrigue 2003-08-21 9:19 ` Benjamin Geer 2003-08-21 18:44 ` Chris Clearwater 2003-08-20 20:43 ` Issac Trotts
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox