Mailing list for all users of the OCaml language and system.
 help / color / mirror / Atom feed
* ocaml, inheritance vs. subtyping?
@ 1996-06-01 23:19 Ian T Zimmerman
  1996-06-03 17:52 ` Didier Remy
       [not found] ` <199606031649.SAA14505@pauillac.inria.fr>
  0 siblings, 2 replies; 3+ messages in thread
From: Ian T Zimmerman @ 1996-06-01 23:19 UTC (permalink / raw)
  To: caml-list


Hi, I am just starting with ocaml, so please have mercy on a newbie
:-)

I am somewhat perplexed by the passage in the manual on virtual
methods (pp. 29-30).  Let's start with the first paragraph on p.30: it
says that int_comparable2 is not a subtype of int_comparable, even
though the former inherits from the latter!  Now for a person with an
experience in a non-functional OO language (C++ or Eiffel or Sather)
it seems _very strange_ that the subtype and inheritance (derivation)
relations aren't the same.  It even seems that not having such an
overlap defeats the purpose of having OO features in the first place.
How is the programmer to predict where a subtype relation actually
exists?

Now the manual goes on to say that this occurs because `the self type
appears in contravariant position in the type of method leq'.  First,
am I right in decoding this as `the types of the argument of
int_comparable#leq and int_comparable2#leq are in covariant relation,
but they would have to be in contravariant relation for a subtyping
relation to exist between int_comparable and int_comparable2'?
Second, and this may be the heart of the issue, _WHY_ is type 'a ->
bool inferred for leq, and not < x: unit -> int; .. > -> bool , as
it surely would for a global function?

If these are stupid questions, I apologize again.  Maybe it's a sign
that the OO features need more explanation in the manual, though.

Best,

-- 
Ian T Zimmerman           +-------------------------------------------+
Box 13445                 I When the dead are left to bury the dead,  I
Berkeley CA 94712 USA     I the living remain alone.  Arthur Koestler I
mailto:itz@rahul.net      +-------------------------------------------+





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

* Re: ocaml, inheritance vs. subtyping?
  1996-06-01 23:19 ocaml, inheritance vs. subtyping? Ian T Zimmerman
@ 1996-06-03 17:52 ` Didier Remy
       [not found] ` <199606031649.SAA14505@pauillac.inria.fr>
  1 sibling, 0 replies; 3+ messages in thread
From: Didier Remy @ 1996-06-03 17:52 UTC (permalink / raw)
  To: Ian T Zimmerman; +Cc: caml-list


> I am somewhat perplexed by the passage in the manual on virtual
> methods (pp. 29-30).  Let's start with the first paragraph on p.30: it
> says that int_comparable2 is not a subtype of int_comparable, even
> though the former inherits from the latter!  Now for a person with an
> experience in a non-functional OO language (C++ or Eiffel or Sather)

> it seems _very strange_ that the subtype and inheritance (derivation)
> relations aren't the same.  

No, the two relations are not the same.  This is one of the main
problem with object orientation: subtyping and subclassing should not
be identified.  The are different notions.

Inheritance is the ability to build a class from some older ones, and in
particular to share its source and compiled code. The subclassing relation
is defined between classes, and is purely syntactic. 

On the opposite, the subtyping relation is defined between types, and the
coercions are defined between values, usually objects.  The subtyping
relation tells wether an object of some type can safely be considered as an
object of another type.  This is a more semantic concept. 

There is *no* relation between sub-classing and sub-typing as illustrated by
the two following examples (You may read [1] for a longer discussion of
this problem):

Let first see an example of subclassing were subtyping fails:           ;;

    class point x as self : 'mytype = 
      val x = (x:int)
      method getx = x
      method equal (q:'mytype) = (self#getx = q#getx)
    end;;

    class color_point x c as self = 
      inherit point x as super
      val c = (c : string)
      method getc = c
      method equal q = super#equal q &&  (self#getc = q#getc)
    end;;

    let p = new point 1;;
    let cp = new color_point 1 "color";;

But,                                                                    ;;

    (cp : color_point :> point);;

complains with the error message:                                       

    color_point is not a subtype of point

Indeed, point is not a subtype of point_color. Otherwise, you could send the
message equal to cp seen as a point and then pass it a point as argument, as
in the following program:
                                                                         ;;
    (cp : color_point :> point)#equal p;;

where the execution of method equal of cp would attempt to access the c
field of the point p.

Conversely, you can easily define two classes that are not in a subclass
relation, while the types of objets of those classes are always
in a subtype relation: For example, define
                                                                        ;;
    class very_efficient_point x as self : 'mytype = 
      val x = string_of_int x
      method getx = int_of_string x
      method equal (q:'mytype) = (self#getx = q#getx)
    end;;

    let q = new very_efficient_point 1;;

Then the two classes point and very_efficient_point are unrelated. 
However, objects of those classes always have the very same type, and 
a fortiori have types in a subtype relation! Indeed, you can safely test
them for equality:

     p # equal q;;

> It even seems that not having such an
> overlap defeats the purpose of having OO features in the first place.

I would rather say that identifying the two notions fools the user.  In the
past, the identification of the two notions resulted in big holes in some
type systems of languages that were widely used (in the industry), generally by
accepting the above incorrect example.

More recently, other languages choose to weaken their type system, thus
forcing the user touse dynamic type cases (i.e. allowing a runtime failure)
on examples that can be statically typed in O'caml.

> How is the programmer to predict where a subtype relation actually
> exists?

The subtyping relation in ocaml is quite standard [2]. 
A simple and efficient algorithm can be found in [3]. 

> Now the manual goes on to say that this occurs because `the self type
> appears in contravariant position in the type of method leq'.  First,
> am I right in decoding this as `the types of the argument of
> int_comparable#leq and int_comparable2#leq are in covariant relation,
> but they would have to be in contravariant relation for a subtyping
> relation to exist between int_comparable and int_comparable2'?

You should rather read:

In order for the type of int_comparable2 to be a subtype of int_comparable,

    1/ int_comparable2#leq must be a subtype of int_comparable#leq, i.e. 
    (int_comparable2 -> bool) must be a subtype of (int_comparable -> bool)

    2/ by contra-variant propagation on the left side of the arrow, 
    int_comparable must be a subtype of int_comparable2

    3/ since set_x is a method of int_comparable2, 
    set_x must be a method of int_comparable

    4/ which is not the case.
    
Therefore, int_comparable2 is not a subtype of int_comparable. 


> Second, and this may be the heart of the issue, _WHY_ is type 'a ->
> bool inferred for leq, and not < x: unit -> int; .. > -> bool , as
> it surely would for a global function?

the type 'a -> bool was not inferred. It was a constraint put in the source
code:

        #class virtual comparable () : 'a =
        #  virtual leq : 'a -> bool
        #end;;
        class virtual comparable (unit) : 'a =
          virtual leq : 'a -> bool
        end

In this particular case, if you define, as you suggest:
                                                                        ;;
    class virtual 'a comparable () =
      constraint 'a = < x : int; .. > 
      virtual leq : 'a -> bool
    end;;

and leave int_comparable and int_comprable2 classes unchanged, then 
int_comparable2 would be a subtype of int_comparable. 

> that the OO features need more explanation in the manual, though.

Presently, the chapter on objects in the "Introduction in Objective Caml"
part of the manual is more a safety kit than an extended course on
objects. Still, it will probably be extended in a future release.

    -Didier.

[1] @incollection  (
  author       =   "William R. Cook and Walter L. Hill and Peter S. Canning",
  title        =   "Inheritance is not Subtyping",
  booktitle =    "Theoretical Aspects Of Object-Oriented Programming. 
                  Types, Semantics and Language Design",
  publisher =    "MIT Press",
  year =         1993,
  editor =    "Carl A. Gunter and John C. Mitchell")


[2] @inproceedings   (
author       =   "Roberto M. Amadio and Luca Cardelli",
title        =   "Subtyping Recursive Types",
booktitle    =   "Proceedings of the Eighteenth ACM Symposium on
                  Principles of Programming Languages", 
address      =   "Orlando, FL",
month        =   "January",
year         =   "1991",
pages        =   "104--118",
note         =   "Also available as DEC Systems Research Center
                  Research Report number 62, August 1990.  
                  To appear in TOPLAS")

[3] 
@inproceedings{Kozen-Palsberg/efficient-recursive-subtyping,
        author =        "Dexter Kozen and Jens Palsberg",
        title =         "Efficient Recursive Subtyping",
        booktitle =     "Proc. 20th symp. Principles of Programming Languages",
        year =          "1993",
        pages =         "419--428",
        publisher =     "ACM press"}





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

* Re: ocaml, inheritance vs. subtyping?
       [not found] ` <199606031649.SAA14505@pauillac.inria.fr>
@ 1996-06-04  6:51   ` Ian T Zimmerman
  0 siblings, 0 replies; 3+ messages in thread
From: Ian T Zimmerman @ 1996-06-04  6:51 UTC (permalink / raw)
  To: Didier.Remy; +Cc: caml-list



In article <199606031649.SAA14505@pauillac.inria.fr>
Didier.Remy@inria.fr (Didier Remy) writes:

> I (itz) wrote this...:

> > I am somewhat perplexed by the passage in the manual on virtual
> > methods (pp. 29-30).  Let's start with the first paragraph on p.30: it
> > says that int_comparable2 is not a subtype of int_comparable, even
> > though the former inherits from the latter!  Now for a person with an
> > experience in a non-functional OO language (C++ or Eiffel or Sather)
> > it seems _very strange_ that the subtype and inheritance (derivation)
> > relations aren't the same.  
> 

> No their are intendedly not the same.  This is one of the main
> problem with object orientation: subtyping and subclassing should
> not be identified.  The are different notions.  Inheritance is the
> ability to build a class from some older ones, and in particular to
> share its source and compiled code. The subclassing relation is
> defined between classes, and is purely syntactic.  On the opposite,
> the subtyping relation is defined between types, and the coercions
> are defined between values, usually objects.  The subtyping relation
> tells wether an object of some type can safely be considered as an
> object of another type.  This is a more semantic concept.


OK, I now see that this is mainly a matter of terminology.  What
you're saying is that subtyping and subclassing _as used by ocaml_ are
distinct.  I already knew that when I wrote my post :-) But ocaml's
notion of subclassing is different from the one in C++ and Sather, and
the latter two simply DON'T HAVE this extra notion.  So, the question
really is, what advantage is there in having it?  Please read on
before responding.

> There is *no* relation between sub-classing and sub-typing as illustrated by
> the two following examples (You may read [1] for a longer discussion of
> this problem):
> 
> Let first see an example of subclassing were subtyping fails:           ;;
> 
>     class point x as self : 'mytype = 
>       val x = (x:int)
>       method getx = x
>       method equal (q:'mytype) = (self#getx = q#getx)
>     end;;
> 
>     class color_point x c as self = 
>       inherit point x as super
>       val c = (c : string)
>       method getc = c
>       method equal q = super#equal q &&  (self#getc = q#getc)
>     end;;
> 
>     let p = new point 1;;
>     let cp = new color_point 1 "color";;
> 
> But,                                                                    ;;
> 
>     (cp : color_point :> point);;
> 
> complains with the error message:                                       
> 
>     color_point is not a subtype of point
> 
> Indeed, point is not a subtype of point_color. Otherwise, you could send the
> message equal to cp seen as a point and then pass it a point as argument, as
> in the following program:
>                                                                          ;;
>     (cp : color_point :> point)#equal p;;
> 
> where the execution of method equal of cp would attempt to access the c
> field of the point p.
> 

In C++ and Sather, a class _is a type_.  The type of an object is
determined by its declaration, which mentions the class name.  The
situation above cannot arise, because the class=type notion is
_enforced_ by the language semantics and you cannot override a method
by one with the wrong variance: this is done differently in each
language.  Sather will just flag such a construction as erroneous,
which IMHO is by far the least error-prone way.  C++ on the other side
will silently accept it, but will consider the method in the derived
class (the analogue of color_point#equal above) a completely _new_ and
semantically unrelated method (not an override).  I can't say that I
like this way :-( but it does the job of keeping type safety.


> Conversely, you can easily define two classes that are not in a subclass
> relation, while the types of objets of those classes are always
> in a subtype relation: For example, define
>                                                                         ;;
>     class very_efficient_point x as self : 'mytype = 
>       val x = string_of_int x
>       method getx = int_of_string x
>       method equal (q:'mytype) = (self#getx = q#getx)
>     end;;
> 
>     let q = new very_efficient_point 1;;
> 
> Then the two classes point and very_efficient_point are unrelated. 
> However, objects of those classes always have the very same type, and 
> a fortiori have types in a subtype relation! Indeed, you can safely test
> them for equality:
> 
>      p # equal q;;
> 

Hmmmm...strange...strange :-) I guess my misgivings are caused by
being used to _explicit_ typing and considering it The Good Thing, and
therefore also considering explicit _sub_typing Another Good Thing.
For a language based on type inference, other ehtics may be
appropriate :-)

> > It even seems that not having such an
> > overlap defeats the purpose of having OO features in the first place.

>  I would rather say that identifying the two notions fools the user.
> In the past, the identification of the two notions resulted in big
> holes in some type systems of languages that were widely used (in
> the industry), generally by accepting the above incorrect example.

Like which languages?  Not C++, as I noted above (although of course
C++ is full of other holes, so to speak :-))

> More recently, other languages choose to weaken their type system,
> thus forcing the user touse dynamic type cases (i.e. allowing a
> runtime failure) on examples that can be statically typed in O'caml.

Eiffel allows the example of covariant override above.  I am not aware
of others.  Sather, on the contrary, _tightened_ typing by disallowing
the renaming trap of C++ and permitting only real contravariant
overrides.


> > How is the programmer to predict where a subtype relation actually
> > exists?

>  The subtyping relation in ocaml is quite standard [2].  A simple
> and efficient algorithm can be found in [3].

I have actually read [2].  But my point is not whether subtyping is
abstractly definable or mechanizable, but whether it is obvious and
explicit.  If it isn't, programmers will rely on the compiler to
typecheck their code, instead of trying to get it right before
submitting it for compilation.

> > Second, and this may be the heart of the issue, _WHY_ is type 'a ->
> > bool inferred for leq, and not < x: unit -> int; .. > -> bool , as
> > it surely would for a global function?
> 

> the type 'a -> bool was not inferred. It was a constraint put in the
> source code:

Of course, you're right here.  My whole rave was based on an
oversight.  

To finish let me return to the first paragraph.  My question (and it's
just that, not a start of a language war) is what ocaml gains by
having a separate `structural' (as in structural vs. name equivalence)
notion of subtyping.  You can allow covariant overrides like
color_point#equal.  Is that good?  The C++ person in me screams, NO
WAY!  But one of these days, I'll kill him and become a happy ocamler.

-- 
Ian T Zimmerman           +-------------------------------------------+
Box 13445                 I When the dead are left to bury the dead,  I
Berkeley CA 94712 USA     I the living remain alone.  Arthur Koestler I
mailto:itz@rahul.net      +-------------------------------------------+




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

end of thread, other threads:[~1996-06-04  7:11 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
1996-06-01 23:19 ocaml, inheritance vs. subtyping? Ian T Zimmerman
1996-06-03 17:52 ` Didier Remy
     [not found] ` <199606031649.SAA14505@pauillac.inria.fr>
1996-06-04  6:51   ` Ian T Zimmerman

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox