Version française
Home     About     Download     Resources     Contact us    

This site is updated infrequently. For up-to-date information, please visit the new OCaml website at

Browse thread
OO programming
[ Home ] [ Index: by date | by threads ]
[ Search: ]

[ Message by date: previous | next ] [ Message in thread: previous | next ] [ Thread: previous | next ]
Date: 2008-02-26 (06:18)
From: Jacques Garrigue <garrigue@m...>
Subject: Re: [Caml-list] OO programming
From: Tiphaine Turpin <>

> After a few unsuccessfull tries with using the object oriented features 
> of ocaml, I have been looking for ways to write classes that have a 
> chance to typecheck. The usual problem is that objects refer to other 
> objects, those objects may refer back to objects referring to them, then 
> objects refer to different objects, some of them having more methods 
> than others (subtyping), etc. and finally the programmer has to give a 
> type to all those beautifull mutable fields that are not fully 
> specified, or make them parametric. Of course, the resulting classes 
> should be reusable, that is, one should be able to extend a collection 
> of related classes simultaneously, such that the fields now have the 
> relevant subtype instead of the type they had before.

I must say first that I completely agree with Dirk Thierbach,
the easy way to do that is to connect actions rather than objects.
This is the signal/slot approach to GUI programming, and in my opinion
this works much better because you don't have to bother about
interface incompatibilities: you just do the plumbing by hand, without
needing strange wrapper classes.

> The best guidelines that I found are in the following course from Didier 
> Remy :
> He uses parametric classes  (parametric in the type of the related 
> objects) so that they can be extended. However I'm still unsatisfied 
> with this solution, because the related classes are written 
> independently, or, more precisely, their dependencies remain in the head 
> of the programmer and have no concretization in the language. For 
> example if a class refer to a method provided by a related class, this 
> way of writing them does not guarantee that the method is actually 
> defined. Only when creating and linking the objects together will the 
> type-checker reject the program, if for example, the method has been 
> misspelled in one class. So for me this coding scheme has the drawback 
> that it unplugs the type-checker and just call it at the end. For me the 
> "ideal" use of objects would use mutually recursive classes with fields 
> defined with a type referring to the name of the other class. The 
> problem is that simply using the closed type of the other classs often 
> prevent any further refinement.

No, the type checker is not unplugged. Internal coherence is still
verified for each class, this just the coherence of their combination
that has to wait until you put them together. I don't see why it's
wrong: if you want to check the compatibilty, just write a few lines
combining the objects, and you will catch all the errors you want, at
compile time.
But I think your disagreement lies at a  different level: OO
programmers often want to express their own view of things directly
into the language. So the problem is not about safety, or even
efficiency of debugging, but about writing down everything you want to
say where you want to write it. Reading the following paper made
clearer a feeling I already had before:

  Klaus Ostermann. Nominal and structural subtyping in component-based
  programming. Journal of Object Technology, 7(1):121 - 145, 2008.

There, he explains that he wants the ability both to declare that a
class implements an interface (when the class is defined), or that an
interface is implemented by a class (when the interface is
defined). He then goes on saying that structural subtyping in ocaml
provides neither. But this is false. The point in ocaml is that
you don't need to declare anything. But if you want to be sure, you
can check the subtyping relation:

  let _check x = (x : subtype :> supertype)

You can write this anywhere. This is a check, not a declaration.
If there are type parameters, you need to write a bit more:

  module Check : sig val f : 'a subtype -> 'a supertype end =
    struct let f x = (x : _ subtype :> _ supertype) end

This is because type annotations in ocaml do not enforce that variables
are kept polymorphic. I you want to check it, you have to repeat
yourself at the module level.

> Hence my question: does anyone knows a way of combining the reusability 
> of sets of related classes with a more modular (type/consistency)-checking ?

I'm not sure whether it helps, but I attach here the same example of
observer pattern as in the tutorial, but without using type
parameters. They are replaced by private row types.

Ideally, one would like to check coherence too by writing
  module rec Check : S' = Window(Check)
Unfortunately, this doesn't seem to work currently. I'm not yet sure
whether this is a bug or a limitation of recursive modules.

Jacques Garrigue      Nagoya University     garrigue at
		   <A HREF=>JG</A>

module type S = sig
  type event
  type subject
  type observer = private <notify: subject -> event -> unit; ..>

module Any(X:S) = struct
  class virtual observer =
      method virtual notify : X.subject ->  X.event -> unit
  class virtual subject =
    object (self)
      val mutable observers : list = []
      method add_observer obs = observers <- (obs :: observers)
      method notify_observers (e : X.event) = 
        List.iter (fun x -> x#notify self#self e) observers

module type S' = sig
  type event = private [> `Move]
  type subject = private <draw: unit; ..>
  type observer = private <notify: subject -> event -> unit; ..>

module Window(X:S') = struct
  module AX = Any(X)
  class observer =
      method notify s e = s#draw
  let count = ref 0
  class virtual subject =
    let id = count := succ !count; !count in
    object (self)
      inherit AX.subject
      val mutable position = 0
      method identity = id
      method move x = position <- position + x; self#notify_observers `Move
      method draw = Printf.printf "{Position = %d}\n"  position;

module WindowA = struct
  type event = [`Move]
  class type observer =
    object method notify : subject -> event -> unit end
  and subject =
      method add_observer : observer -> unit
      method draw : unit
      method identity : int
      method move : int -> unit
      method notify_observers : event -> unit

module WindowF : sig
  class observer :
  class subject : WindowA.subject
end = struct
  module WF = Window(WindowA)
  class observer =
  class subject = object (self)
    inherit WF.subject
    method private self = (self :> WindowA.subject)

let window = new WindowF.subject;;
let window_observer = new;;
window#add_observer window_observer;;
window#move 1;;

module WRichT = struct
  type event = [`Raise | `Resize | `Move]
  class type ['subject, 'event] observer =
    object method notify : 'subject -> 'event -> unit end
  and ['observer,'event] subject =
      method add_observer : 'observer -> unit
      method draw : unit
      method identity : int
      method move : int -> unit
      method notify_observers : 'event -> unit
      method raise : unit
      method resize : int -> unit

module type S'' = sig
  type event = private [> WRichT.event]
  type observer = private (subject, event)
  and subject = private (observer, event) #WRichT.subject

module WRich (X : S'') = struct
  module WRF = Window(X)
  class observer =
      inherit as super
      method notify s e = if e <> `Raise then s#raise; super#notify s e
  let string_of_event = function
      `Raise -> "Raise" | `Resize -> "Resize" | `Move -> "Move"
    | _ -> "Unknown"
  class trace = 
      method notify s e =
          "<Window %d <== %s>\n" s#identity (string_of_event e)
  class virtual subject =
    object (self)
      inherit WRF.subject
      val mutable size = 1
      method resize x = size <- size + x; self#notify_observers `Resize
      val mutable top = false
      method raise = top <- true; self#notify_observers `Raise
      method draw =
        Printf.printf "{Position = %d; Size = %d}\n"  position size;

module WRichA = struct
  type event = [`Raise | `Resize | `Move]
  class type observer = [subject, event]
  and subject = [observer, event] WRichT.subject
module WRichF : sig
  class observer :
  class trace :
  class subject : WRichA.subject
end = struct
  module WRF = WRich(WRichA)
  class observer =
  let string_of_event = WRF.string_of_event
  class trace = WRF.trace
  class subject = object (self)
    inherit WRF.subject
    method private self = (self :> WRichA.subject)

let window = new WRichF.subject ;;
window#add_observer (new;;
window#add_observer (new WRichF.trace);;
window#move 1; window#resize 2;;