caml-list - the Caml user's mailing list
 help / color / mirror / Atom feed
From: John Prevost <j.prevost@cs.cmu.edu>
To: Alessandro Baretta <alex@baretta.com>
Cc: Gerd Stolpmann <info@gerd-stolpmann.de>, Ocaml <caml-list@inria.fr>
Subject: Re: [Caml-list] Recursive classes are impossible?
Date: 25 Jun 2002 22:43:04 -0400 (11.324 UMT)	[thread overview]
Message-ID: <86fzzaiwuf.fsf@laurelin.dementia.org> (raw)
In-Reply-To: <3D19015B.6030105@baretta.com>

>>>>> "ab" == Alessandro Baretta <alex@baretta.com> writes:

    ab> Why is it that an explicit upcast is needed?

In this case, the explicit cast is needed to "remove" the color method
and make it into a real tree.

Something to note in this case is that you may not get what you
intended with the classes you defined.  You said:

class tree =
  object (s)
    val mutable ch = ( [] : tree list )
    method get = ch
    method set x = ch <- x
  end

class broccoli =
   object (s)
     inherit tree
     method color = "green"
   end

In this case, you'll notice that the children of broccoli are a tree
list, not a broccoli list.  So while broccoli can hold broccolis, you
can't call the color method on its children.  And, you can put normal
trees into broccolis as well.

As for the broader view, contravariance begins to show up whenever you
have a set method.  One way to get around it in the case of broccoli
and trees might be this:

class type ['a] read_tree =
  object ('s)
    method children : 's list
    method contents : 'a
  end

class type ['a] read_broccoli =
  object ('s)
    method children : 's list
    method contents : 'a
    method color : string
  end

class ['a] tree x =
  object (_ : 's)
    val mutable ch = ([] : 's list)
    val mutable co = (x : 'a)
    method children = ch
    method set_children ch' = ch <- ch'
    method contents = x
    method set_contents co' = co <- co'
  end

class ['a] broccoli x =
  object
    inherit ['a] tree x
    method color = "green"
  end


Now there are several ways to work with variance issues.  First, while
you can't cast a broccoli to a tree, you *can* cast it to a read_tree:


# let a = new broccoli 10;;
val a : int broccoli = <obj>
# (a :> 'a read_tree);;
- : int read_tree = <obj>


This is because a read_tree has no way to set the value, so we've
excluded the contravariant case.  The other good thing about this is
that we've removed two contravariant cases.  One case is the
set_children call, the other is the set_contents call.  This allows us
to go a step further--'a read_tree is covariant now in 'a, so we can
take a function that expects a "'c read_tree" and hand it a "'d
read_tree", assuming that 'c :> 'd.  (Or coerce it.)

Finally, there's a certain distinction between methods and functions
when it comes to these things.  When you define a function, you'll see
polymorphism work better.  For example:

let rec count_nodes x =
  List.fold_left (+) 1 (List.map count_nodes (x #children))

With this function, you can do:

# let a = new broccoli 10;;
val a : int broccoli = <obj>
# let b = new tree 10;;
val b : int tree = <obj>
# count_nodes a;;
- : int = 1
# count_nodes b;;
- : int = 1

You still need explicit coercions if you want to make a list
containing both broccolis and trees:

# List.map count_nodes [(a :> 'a read_tree); (b :> 'a read_tree)];;
- : int list = [1; 1]

but noticing this feature of row-polymorphism goes a long way towards
making good use of the object features of O'Caml.  O'Caml's more
powerful type discipline means we can step on our own feet with these
co- vs. contravariance issues, but it also means we can do more
powerful interesting things: since subtyping isn't bound to
inheritance, polymorphic functions can be very very powerful.

John.


-------------------
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


      reply	other threads:[~2002-06-26  2:37 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2002-06-24 13:59 Alessandro Baretta
2002-06-24 21:34 ` Gerd Stolpmann
2002-06-24 23:55   ` james woodyatt
2002-06-25 23:48   ` Alessandro Baretta
2002-06-26  2:43     ` John Prevost [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=86fzzaiwuf.fsf@laurelin.dementia.org \
    --to=j.prevost@cs.cmu.edu \
    --cc=alex@baretta.com \
    --cc=caml-list@inria.fr \
    --cc=info@gerd-stolpmann.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).