caml-list - the Caml user's mailing list
 help / color / mirror / Atom feed
From: Jacques Garrigue <garrigue@math.nagoya-u.ac.jp>
To: Christophe.Troestler+ocaml@umh.ac.be
Cc: caml-list@yquem.inria.fr
Subject: Re: [Caml-list] Polymorphic Variants
Date: Thu, 18 Jan 2007 11:12:19 +0900 (JST)	[thread overview]
Message-ID: <20070118.111219.71083947.garrigue@math.nagoya-u.ac.jp> (raw)
In-Reply-To: <20070117.042430.17384639.Christophe.Troestler@umh.ac.be>

From: Christophe TROESTLER <Christophe.Troestler+ocaml@umh.ac.be>
> > [...] I have some code using objects (visitor pattern), recursive
> > modules, lazyness, and private row types, in an utterly non trivial
> > way, just to do what can be done by standard recursive function
> > definitions using polymorphic variants...
> 
> Sounds like a good case to see & learn the power of polymorphic
> variants in action.  Are the two codes available somewhere (if
> possible with some explanations ?) or are you simply referring to your
> paper "Code reuse through polymorphic variants" ?

Well, I am. Other examples end up being much longer, even using
polymorphic variants :-)

But still, I include at the end of this mail the code to obtain a
fully extensible purely functional polymorphic visitor pattern.
(This is the polymorphic part that is particularly hard, even more
if you want to stay purely functional.)

For comparison, here is the equivalent complete code using polymorphic
variants. Note that there is no explicit visitor pattern: the match
construct provides it, with all the polymorphism needed.

type 'a eadd = [ `Num of int | `Add of 'a * 'a ]
let eval_add eval_rec : 'a eadd -> int = function
  | `Num i -> i
  | `Add (e1, e2) -> eval_rec e1 + eval_rec e2
let rec eval_add' e = eval_add eval_add' e
(* val eval_add' : ('a eadd as 'a) -> int *)
type 'a eaddneg = ['a eadd | `Neg of 'a]
let eval_add_neg eval_rec : 'a eaddneg -> int = function
  | #eadd as e -> eval_add eval_rec e
  | `Neg e -> - eval_rec e
let rec eval_add_neg' e = eval_add_neg eval_add_neg' e
(* val eval_add_neg' : ('a eaddneg as 'a) -> int *)
let n = eval_add_neg' (`Add (`Neg(`Add(`Num 3, `Num 2)), `Num 7))
(* val n : int = 2 *)

This also means that providing a method returning a polymorphic
variant describing the contents of an object is probably a simpler
approach to implementing the visitor pattern, even for objects. Here
is the code needed if you still want to use objects.

class type ['a] expr = object method repr : 'a end
let rec eval_add_expr (e : _ expr) = eval_add eval_add_expr e#repr
(* val eval_add_expr : ('a eadd expr as 'a) -> int *)
let rec eval_add_neg_expr (e : _ expr) = eval_add_neg eval_add_neg_expr e#repr
(* val eval_add_neg_expr : ('a eaddneg expr as 'a) -> int *)

Regards,

Jacques Garrigue

(* A fully extensible polymorphic visitor pattern.
   Code written with Keiko Nakata *)

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 (eval)
    method private visit (e : exp) = e#accept (Lazy.force X.eval)
    method visitNum (i : int) = i
    method visitAdd r l = eval#visit r + eval#visit l
  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 (eval)
    inherit Add.eval
    method visitNeg e = - eval#visit e
  end
  let eval = lazy (new eval)
end

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

open AddNeg
let n = (add (neg (add (num 3) (num 2))) (num 7))#accept (Lazy.force eval)


  reply	other threads:[~2007-01-18  2:12 UTC|newest]

Thread overview: 34+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2007-01-16 20:32 Tom
2007-01-16 20:49 ` [Caml-list] " Seth J. Fogarty
2007-01-16 21:05   ` Tom
2007-01-16 21:23     ` Seth J. Fogarty
2007-01-16 21:45       ` Edgar Friendly
2007-01-16 22:18       ` Lukasz Stafiniak
2007-01-17  5:55       ` skaller
2007-01-17  0:30 ` Jonathan Roewen
2007-01-17  2:19 ` Jacques GARRIGUE
2007-01-17  3:24   ` Christophe TROESTLER
2007-01-18  2:12     ` Jacques Garrigue [this message]
2007-01-17  6:09   ` skaller
2007-01-17 13:34     ` Andrej Bauer
2007-01-17 21:13   ` Tom
2007-01-17 22:53     ` Jon Harrop
2007-01-17 23:07       ` Tom
     [not found]         ` <200701172349.53331.jon@ffconsultancy.com>
     [not found]           ` <c1490a380701180407j670a7cccyb679c71fde20aa4b@mail.gmail.com>
2007-01-18 16:23             ` Fwd: " Tom
2007-01-18 21:14               ` Jon Harrop
2007-01-19  9:26                 ` Dirk Thierbach
2007-01-19 10:35                   ` Tom
2007-01-19 11:14                     ` Dirk Thierbach
2007-01-19 12:03                       ` Tom
2007-01-18 21:43       ` Christophe TROESTLER
2007-01-18  1:28     ` Jacques Garrigue
2007-01-18  1:46       ` Jon Harrop
2007-01-18  4:05       ` skaller
2007-01-18  6:20         ` Jacques Garrigue
2007-01-18  9:48           ` skaller
2007-01-18 12:23       ` Tom
  -- strict thread matches above, loose matches on Subject: below --
2002-04-17  9:49 [Caml-list] Polymorphic variants John Max Skaller
2002-04-17 10:43 ` Remi VANICAT
2002-04-17 23:49   ` John Max Skaller
2002-04-18  1:23     ` Jacques Garrigue
2002-04-18  9:04       ` John Max Skaller

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=20070118.111219.71083947.garrigue@math.nagoya-u.ac.jp \
    --to=garrigue@math.nagoya-u.ac.jp \
    --cc=Christophe.Troestler+ocaml@umh.ac.be \
    --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).