caml-list - the Caml user's mailing list
 help / color / mirror / Atom feed
* [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;
as well as URLs for NNTP newsgroup(s).