This document is also available in Postscript.
Table of modules
- 
Exp: data-centric encoding.
 - Exp1: operation-centric encoding.
 - Exp2: another operation-centric encoding.
 
This is another solution in OCaml to the Independently Extensible
Solutions to the Expression Problem. as described by
Zenger and
Odersky in their Technical Report
Nr. 200433,
March 2004.
This code has been check with OCaml 3.08. It uses anonymous classes in
DblePlusNegTest. This could have been expanded into toplevel class
definitions in an earlier version of OCaml, which allowed closed classes
(classes where self had a closed object type).
The recent versions of OCaml do not allow this but for anonymous classes).
The code is then a mere translation of the original code in Scala,
where type annotations have been removed. Hence, the code is in general
shorter than in scala with the only exception of using private methods for 
sharable instance variables discussed above.
Jacques Garrigue and Didier Rémy. 
Module Exp
3  Object-oriented style
3.2  Framework
module Base = struct
  
class exp = object end
  
class num v = object
    
inherit exp
    
val value : int = v
    
method eval = value
  
end
end
module BaseTest = struct
  
let e = new Base.num 7
  
let _ = Printf.printf "e is %d\n" (e#eval)
end
3.3  Data extensions
 
module BasePlus = struct
  
class plus l r = object
    
inherit Base.exp
    
val left = l
    
val right = r
    
method eval = left#eval + right#eval
  
end
end
module BaseNeg = struct
  
class neg t = object
    
inherit Base.exp
    
val term = t
    
method eval = 0 - term#eval
  
end
end
Combining Independent Extensions
 
module BasePlusNeg = struct
  
include Base
  
include BasePlus
  
include BaseNeg
end
3.4  Operation Extensions
 
module Show = struct
  
class num v = object 
    
inherit Base.num v
    
method show = string_of_int value
  
end
end
Linear extensions
 
module ShowPlusNeg = struct
  
include Show
  
class plus l r = object 
    
inherit BasePlusNeg.plus l r
    
method show = 
      
Printf.sprintf "(%s + %s)" (left#show) (right#show)
  
end
  
class neg t = object 
    
inherit BasePlusNeg.neg t
    
method show = Printf.sprintf "-%s" (term#show)
  
end
end
module ShowPlusNegTest = struct
  
open ShowPlusNeg
  
let e = new neg (new plus (new num 7) (new num 6))
  
let _ = Printf.printf "%s = %d\n" (e#show) (e#eval)
end
Tree-transformer extensions
 
module DblePlusNeg = struct
  
class virtual num v = object (self : 'a)
    
inherit BasePlusNeg.num v
    
method private virtual num : int ® 'a
    
method dble = self#num (value × 2)
  
end
  
class virtual plus l r = object (self : 'a)
    
inherit BasePlusNeg.plus l r
    
method private virtual plus : 'a ® 'a ® 'a
    
method dble = self#plus (left#dble) (right#dble)
  
end
  
class virtual neg t = object (self : 'a)
    
inherit BasePlusNeg.neg t
    
method private virtual neg : 'a ® 'a
    
method dble = self#neg (term#dble)
  
end
end
module DblePlusNegTest = struct
  
open DblePlusNeg
  
let rec num v = object inherit num v method private num = num end 
  
and plus l r = object inherit plus l r method private plus = plus end
  
and neg t = object inherit neg t method private neg = neg end
  
let e = plus (neg (plus (num 1) (num 3))) (num 2)
  
let _ = Printf.printf "e * 2 is -4 ? %d\n" (e#dble#eval)
end
It is useless to check for type errors... OCaml is sound! 
Combining independent extensions
 
module ShowDblePlusNeg = struct
  
class virtual num v = object
      
inherit ShowPlusNeg.num v
      
inherit DblePlusNeg.num v
  
end
  
class virtual plus l r = object
      
inherit ShowPlusNeg.plus l r
  
inherit DblePlusNeg.plus l r
  
end
  
class virtual neg t = object
      
inherit ShowPlusNeg.neg t
      
inherit DblePlusNeg.neg t
  
end
end
ShowDblePlusNeg uses multiple inheritance of two classes built from a
common ancestor. This is a known difficulty when using multiple inheritance,
since the state of the common ancestor is being dupplicated in the two
subclasses. For instance, objects of the class ShowDblePlusNeg.num will
contained two occurrences of field value. This is fine here because
instance variables are not mutable and fields are not updated, so they can
be freely dupplicated: the two fields are filled with (and retain) the
same initial value v.
OCaml does not offer any primitive construct to deal with this situation.
However, a simple solution is to make all read, write and update to instance
variables of the shared class go indirectly through private methdods. Since
methods definitions are overridden during inheritance, all methods will then
refer to the same instance variable---the one defined last---and unused
dupplicates will passively sit in the state of the object. Ths is not very
elegant, but it works well. A small extension of the language with
annotations on instance variables could be used to drive the inheritance
of instance variables and avoid the use of private methods.
We have not used this schema here because objects are purely functional.
Section 5: Binary methods
 
Binary methods are rarely a problem in OCaml... 
module Equals = struct
  
class exp = object 
    
inherit Base.exp
    
method isNum (v : int) = false
  
end
  
class num v = object (self : 'a)
    
inherit exp
    
inherit Base.num v
    
method eql (other : 'a) = other#isNum v
    
method isNum v = v = value
  
end
end
5.1  Data extensions
module EqualsPlusNeg = struct
  
class exp = object (self : 'a)
    
inherit Equals.exp
    
method isNum (v : int) = false
    
method isPlus (l : 'a) (r : 'a) = false
    
method isNeg (t : 'a) = false
  
end
  
class num v = object
    
inherit exp
    
inherit Equals.num v
  
end
  
class plus l r = object (self : 'a)
    
inherit exp
    
inherit BasePlusNeg.plus l r
    
method isPlus l r = left#eql l & right#eql r
    
method eql (other : 'a) = other#isPlus (left) (right)
  
end
  
class neg t = object (self : 'a)
    
inherit exp
    
inherit BasePlusNeg.neg t
    
method isNeg t = term#eql t
    
method eql (other : 'a) = other#isNeg (term)
  
end
end
5.2  Operation extensions
module EqualsShowPlusNeg = struct
  
class num v = object
    
inherit EqualsPlusNeg.num v
    
inherit ShowPlusNeg.num v
  
end
  
class plus l r = object
    
inherit EqualsPlusNeg.plus l r
    
inherit ShowPlusNeg.plus l r 
  
end
  
class neg t = object
    
inherit EqualsPlusNeg.neg t
    
inherit ShowPlusNeg.neg t
  
end
end
module EqualsShowPlusNegTest = struct
  
open EqualsShowPlusNeg
  
let t1 = new plus (new num 1) (new num 2)
  
let t2 = new plus (new num 1) (new num 2)
  
let t3 = new neg (new num 2)
  
let _ =
    
Printf.printf "%s = %s ? %b\n" (t1#show) (t2#show) (t2#eql t2);
    
Printf.printf "%s = %s ? %b\n" (t1#show) (t3#show) (t2#eql t3)
end
This module provides a functional decomposition (operation-centric view) 
of the expression problem. 
Many class type definitions and type annotations could 
be omitted. We keep them to provide an early check on the interfaces of 
the classes we define. 
module FBase = struct
  
class type ['v] exp = object
    
method accept : 'v ® unit
  
end
  
class ['v] num value = object (_ : 'v #exp)
    
method accept v = v#visitNum value
  
end
  
class type visitor = object
    
method visitNum : int ® unit
  
end
  
class ['e] eval = object (self : #visitor)
    
val mutable result = 0
    
method private return x =
      
result ¬ x
    
method apply (t : _ #exp as 'e) =
      
t#accept self; result
    
method visitNum value =
      
self#return value
  
end
end
In visitors (such as eval), we used a private method #return to store 
 the result. This way the pair apply/return will work properly even if the field
 result is shadowed. 
module FBasePlus = struct
  
class type ['e] visitor = object
    
inherit FBase.visitor
    
method visitPlus : 'e ® 'e ® unit
  
end
  
class ['v] plus l r = object (_ : ('e #visitor as 'v) #FBase.exp as 'e)
    
method accept v =
      
v#visitPlus l r
  
end
  
class ['e] eval = object (self : 'e #visitor)
    
inherit ['e] FBase.eval
    
method visitPlus l r =
      
self#return (self#apply l + self#apply r)
  
end
end
module FBaseNeg = struct
  
class type ['e] visitor = object
    
inherit FBase.visitor
    
method visitNeg : 'e ® unit
  
end
  
class ['v] neg t = object (_ : ('e #visitor as 'v) #FBase.exp as 'e)
    
method accept v =
      
v#visitNeg t
  
end
  
class ['e] eval = object (self : 'e #visitor)
    
inherit ['e] FBase.eval
    
method visitNeg t =
      
self#return (- (self#apply t))
  
end
end
module FBasePlusNeg = struct
  
class type ['e] visitor = object
    
inherit ['e] FBasePlus.visitor
    
inherit ['e] FBaseNeg.visitor
  
end
  
class ['e] eval = object (self : 'e #visitor)
    
inherit ['e] FBasePlus.eval
    
inherit ['e] FBaseNeg.eval
  
end
end
The definition of class eval raises warnings. We can ignore them as
result is only used by the apply/return pair of methods. 
module FShowPlusNeg = struct
  
open FBasePlusNeg
  
class ['e] show = object (self : 'e #visitor)
    
val mutable result = ""
    
method private return x =
      
result ¬ x
    
method apply (t : 'e) =
      
t#accept self; result
    
method visitNum v =
      
self#return (string_of_int v)
    
method visitPlus l r =
      
self#return ("("^ self#apply l ^"+"^ self#apply r ^")")
    
method visitNeg t =
      
self#return ("(-" ^ self#apply t ^")")
  
end
end
module FShowTest = struct
  
open FBase
  
open FBasePlusNeg
  
open FShowPlusNeg
  
let eval =
    
let e = new eval in
    
(e#apply : ('a eval exp as 'a) ® _ :> ('b visitor exp as 'b) ® _)
  
let show =
    
let s = new show in
    
(s#apply : ('a show exp as 'a) ® _ :> ('b visitor exp as 'b) ® _)
  
open FBasePlus
  
open FBaseNeg
  
let e = new plus (new neg (new plus (new num 1) (new num 2))) (new num 3)
  
let () = Printf.printf "%s = %d\n" (show e) (eval e)
end
The eval and show classes above contain the method #apply, with
different types. Since expressions and vistors have mutually
recursive types, this would make it impossible to use both visitors
on the same expression. Fortunately, the recursion is covariant,
and we can coerce to forget the apply method in expression types. 
Below, a new problem arises in class dble, as visitors return 
expressions. We can 
no longer use the covariance of the recursion. We choose to make
#apply private, so that it no longer appears in the object type.
We can extract the #apply method by using an out parameter. 
module FDblePlusNeg = struct
  
open FBasePlusNeg
  
class ['e] dble apply = object (self : 'e #visitor)
    
val mutable result = None
    
method private apply (t : 'e) : 'e =
      
t#accept self;
      
match result with Some x ® x | None ® assert false
    
initializer apply := self#apply
    
method private return x =
      
result ¬ Some x
    
method visitNum v =
      
self#return (new FBase.num (v×2))
    
method visitPlus l r =
      
self#return (new FBasePlus.plus (self#apply l) (self#apply r))
    
method visitNeg t =
      
self#return (new FBaseNeg.neg (self#apply t))
  
end
end
module FDbleTest = struct
  
open FBase
  
open FBasePlusNeg
  
open FDblePlusNeg
  
let dble =
    
let apply = ref (fun _ ® assert false) in
    
ignore (new dble apply);
    
!apply
  
open FShowTest
  
(* We reuse the expression from the previous test, multiplying nums by 2 *)
  
let e = dble e
  
let () = Printf.printf "%s = %d\n" (show e) (eval e)
end
We create a stub for the #apply method, and extract it as a
side-effect of object creation 
This is another approach to the functional decompsition of the expression problem. 
Rather than using coercions later, we hide the #apply method from
the beginning. All visitors inherit from accumulators, and are
called via extract 
module FBase = struct
  
class type ['v] exp = object
    
method accept : 'v ® unit
  
end
  
class ['v] num value = object (_ : 'v #exp)
    
method accept v = v#visitNum value
  
end
  
(* f is an out parameter *)
  
class virtual ['a,'e] accumulator f = object (self : 's)
    
val mutable result = None
    
method private return x =
      
result ¬ Some x
    
method private apply (t : 's #exp as 'e) =
      
t#accept self;
      
match result with Some x ® x | None ® assert false
    
initializer f := self#apply
  
end
  
class ['e] eval f = object (self)
    
inherit [int,'e] accumulator f
    
method visitNum value =
      
self#return value
  
end
  
let extract cons =
    
let f = ref (fun _ ® assert false) in
    
cons f; !f
  
(* We could use this as let eval = extract (new eval) *)
end
module FBasePlus = struct
  
class ['e] eval f = object (self)
    
inherit ['e] FBase.eval f
    
method visitPlus l r =
      
self#return (self#apply l + self#apply r)
  
end
  
(* Since we have hidden #apply, #eval is the same as #visitor *)
  
class ['v] plus l r = object (_ : ('e #eval as 'v) #FBase.exp as 'e)
    
method accept v =
      
v#visitPlus l r
  
end
end
module FBaseNeg = struct
  
class ['e] eval f = object (self)
    
inherit ['e] FBase.eval f
    
method visitNeg t =
      
self#return(- (self#apply t))
  
end
  
class ['v] neg t = object (_ : ('e #eval as 'v) #FBase.exp as 'e)
    
method accept v =
      
v#visitNeg t
  
end
end
module FBasePlusNeg = struct
  
class ['e] eval f = object (self)
    
inherit ['e] FBasePlus.eval f
    
inherit ['e] FBaseNeg.eval f
  
end
end
module FShowPlusNeg = struct
  
open FBasePlusNeg
  
class ['e] show f = object (self)
    
inherit [string,'e] FBase.accumulator f
    
method visitNum v =
      
self#return (string_of_int v)
    
method visitPlus l r =
      
self#return ("("^ self#apply l ^"+"^ self#apply r ^")")
    
method visitNeg t =
      
self#return ("(-" ^ self#apply t ^")")
  
end
end
module FShowTest = struct
  
open FBase
  
open FBasePlusNeg
  
open FShowPlusNeg
  
let eval = extract (new eval)
  
let show = extract (new show)
  
open FBasePlus
  
open FBaseNeg
  
let e = new plus (new neg (new plus (new num 1) (new num 2))) (new num 3)
  
let () = Printf.printf "%s = %d\n" (show e) (eval e)
end
module FDblePlusNeg = struct
  
class ['e] dble f = object (self)
    
inherit ['e,'e] FBase.accumulator f
    
method visitNum v =
      
self#return (new FBase.num (v×2))
    
method visitPlus l r =
      
self#return (new FBasePlus.plus (self#apply l) (self#apply r))
    
method visitNeg t =
      
self#return (new FBaseNeg.neg (self#apply t))
  
end
end
module FDbleTest = struct
  
open FBase
  
open FBasePlusNeg
  
open FDblePlusNeg
  
let dble = extract (new dble)
  
open FShowTest
  
let e = dble e
  
let () = Printf.printf "%s = %d\n" (show e) (eval e)
end
This document was translated from LATEX by
HEVEA.