* [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: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 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
* 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 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 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 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-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 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 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 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 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-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-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-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 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-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
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