caml-list - the Caml user's mailing list
 help / color / mirror / Atom feed
From: Jacques Garrigue <garrigue@math.nagoya-u.ac.jp>
To: brogoff@speakeasy.net
Cc: caml-list@yquem.inria.fr
Subject: Re: [Caml-list] Polymorphic method question
Date: Tue, 11 Jul 2006 11:09:16 +0900 (JST)	[thread overview]
Message-ID: <20060711.110916.22502254.garrigue@math.nagoya-u.ac.jp> (raw)
In-Reply-To: <Pine.LNX.4.58.0607101215080.5875@shell2.speakeasy.net>

From: brogoff <brogoff@speakeasy.net>
Subject: [Caml-list] Polymorphic method question
Date: Mon, 10 Jul 2006 12:21:26 -0700 (PDT)

> Hi,
>     I'm sure I'll slap my forehead in disgust when someone lifts the
> scales from my eyes, but I'm once again perplexed by a type error
> from OCaml when using objects. Can someone tell me why I get the
> error from the second case (with the classes connected by "and") when
> the first case is OK?
> # class virtual ['a] bar =
>     object
>       method virtual get : 'a
>     end
>   and foobar =
>     object
>       method virtual f : 'a . 'a bar -> 'a
>     end;;
>               Characters 115-132:
>         method virtual f : 'a . 'a bar -> 'a
>                            ^^^^^^^^^^^^^^^^^
> This type scheme cannot quantify 'a :
> it escapes this scope.

Because bar is not yet completely defined when you use it in foobar.
This is all related with parameter constraint inference: we don't know
yet wether bar's parameter is really polymorphic or is constraint (e.g.
'a = 'b * 'c).
Note that the problem does not occur with direct type definitions, as
constraint inference is not done in that case (this is one of the
reasons you cannot combine type and class definitions.)

# type 'a bar = < get: 'a > and foobar = < f : 'a. 'a bar -> 'a >;;
type 'a bar = < get : 'a >
and foobar = < f : 'a. 'a bar -> 'a >

The only way to solve this problem would be to remove parameter
constraint inference from classes. It would certainly break some
programs, so this seems difficult.

By the way, if you're ready to define your types first, then you
can do this properly.
 
# class virtual ['a] bar = object method virtual get : 'a end
  and virtual foobar = object method virtual f : 'a . 'a bar_t -> 'a end;;
class virtual ['a] bar : object method virtual get : 'a end
and virtual foobar : object method virtual f : 'a bar_t -> 'a end

>    BTW, This example was distilled from one in which I could not cleanly
> remove the class recursion, one involving an "extensible visitor" in
> which there is a recursion between the visited and visitor classes.
> Assuming there's an obvious answer to my first question, is there a
> nice workaround in this case?

Extensible visitor is more complex. You can already find the
polymorphic variant solution at
   http://www.math.nagoya-u.ac.jp/~garrigue/papers/
There is a new solution using private rows and recursive modules
inside "Private rows: abstracting the unnamed".

With Keiko Nakata, we found another solution using objects in place of
polymorhic variants. This is rather verbose, but here is the code.
This is close to the Odersky&Zenger approach in Scala, but here our
visitor is purely functional.
Note how we are not able to use eval's self directly for recursion, as
we need an abstract row.

Jacques Garrigue

(* OO decomposition in Objective Caml *)

class type ['a,'exp] visit_add = object
  method visitNum : int -> 'a
  method visitAdd : 'exp -> 'exp -> 'a
end

module type AddTE = sig
  type ('a,'exp) t
  type exp = < accept : 'a. ('a, exp) t -> 'a >
  val num : int -> exp
  val add : exp -> exp -> exp
end

module type AddT = sig
  include AddTE
  val eval : (int, exp) t Lazy.t
end

module AddF(X : AddT with type ('a,'exp) t = private ('a,'exp) #visit_add) =
struct
  type ('a, 'exp) t = ('a, 'exp) X.t
  class type exp = object ('e) method accept : 'a. ('a, 'e) t -> 'a end
  class num x = object (_ : #exp) method accept v = v#visitNum x end
  let num x = new num x
  class add a b = object (_ : #exp) method accept v = v#visitAdd a b end
  let add x = new add x
  class eval = object
    method visitNum (i : int) = i
    method visitAdd (r : exp) (l : exp) =
      r#accept (Lazy.force X.eval) + l#accept (Lazy.force X.eval)
  end
  let eval = lazy (new eval)
end

module rec Add : AddT with type('a,'exp) t = ('a,'exp) visit_add = AddF(Add)

class type ['a,'exp] visit_add_neg = object
  inherit ['a,'exp] visit_add
  method visitNeg : 'exp -> 'a
end

module type AddNegT = sig
  include AddT
  val neg : exp -> exp
end

module AddNegF(X : AddNegT with
               type ('a,'exp) t = private ('a,'exp) #visit_add_neg) =
struct
  module Add = AddF(X)
  include (Add : AddTE with type ('a,'e) t = ('a,'e) X.t)
  class neg x = object (_ : #Add.exp) method accept v = v#visitNeg x end
  let neg x = new neg x
  class eval = object
    inherit Add.eval
    method visitNeg (e : exp) = - e#accept (Lazy.force X.eval)
  end
  let eval = lazy (new eval)
end

module rec AddNeg : AddNegT with type ('a,'exp) t = ('a,'exp) visit_add_neg =
  AddNegF(AddNeg)


# open Add;;
# let e1 = (add (num 2) (num 3));;
val e1 : Add.exp = <obj>
# e1#accept (Lazy.force eval);;
- : int = 5
# open AddNeg;;
# let e2 = neg (e1 : Add.exp :> exp);;
val e2 : AddNeg.exp = <obj>
# e2#accept (Lazy.force eval);;
- : int = -5


  parent reply	other threads:[~2006-07-11  2:09 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2006-07-10 19:21 brogoff
2006-07-10 20:05 ` [Caml-list] " Richard Jones
2006-07-10 23:25   ` brogoff
2006-07-11  2:24     ` skaller
2006-07-11  4:56       ` brogoff
2006-07-11  2:09 ` Jacques Garrigue [this message]
2006-07-11  5:22   ` brogoff
2006-07-11  7:32     ` Jacques Garrigue
2006-07-11 18:20       ` brogoff
2006-07-12  0:37         ` Jacques Garrigue
2006-07-12 19:26           ` brogoff
  -- strict thread matches above, loose matches on Subject: below --
2002-08-21 21:49 [Caml-list] polymorphic " nadji
2002-08-21 22:57 ` Jacques Garrigue

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=20060711.110916.22502254.garrigue@math.nagoya-u.ac.jp \
    --to=garrigue@math.nagoya-u.ac.jp \
    --cc=brogoff@speakeasy.net \
    --cc=caml-list@yquem.inria.fr \
    /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).