Version française
Home     About     Download     Resources     Contact us    
Browse thread
Re: [Caml-list] Re: OO programming
[ Home ] [ Index: by date | by threads ]
[ Search: ]

[ Message by date: previous | next ] [ Message in thread: previous | next ] [ Thread: previous | next ]
Date: -- (:)
From: Tiphaine.Turpin <Tiphaine.Turpin@f...>
Subject: Re: [Caml-list] Re: OO programming
This is a nice account on how to write those things in a clean and 
modular way. It does not meet all of my requirements however...

The 'msg parameter seems to allow the extension of the set of messages 
that may be used at the interface, and the use of closed types enforce 
strong consistency checks between the two classes. However, a first 
problem is that the communication can only be extended through the  'msg 
parameter: although we may add methods to both classes, it will not be 
possible to for a class to use the new methods of the other (at least, 
if the objects are taken from the existing "subject" and "observers" 
fields). For instance, the following extension to your first design 
fails to typecheck:

class  ['msg] observer'
  (subject: 'msg subject) = object
    inherit ['msg'] observer subject
    method send' = ()
  end
and ['msg] subject' = object
  inherit ['msg] subject
  method notify' =
    List.iter
      (fun obs -> obs#foo)
      observers
end

Second, if We try to be more concrete, we certainly don't want to write

    method send' : 'msg -> unit = function _ -> ()

but instead (as soon as we don't want the method to be virtual 
anymore),  something like:

    method send' : 'msg -> unit = function `HELLO -> ()

which prevents any further extension, as we get: constraint 'a = [ `HELLO ].

A solution that works is

    method send' : 'msg -> unit = function `HELLO -> () | _ -> assert false

because this only implies constraint 'a = [> `HELLO ], but then we loose 
the guarantee that `HELLO is indeed handled (we could have said fun _ -> 
assert false).

In fact, the use of a parameter message type allows to separate the type 
of the interface between the two objects from the "structure" of the 
link between the two classes. So the pattern may be "applied", but not 
really "extended" in the most general sense. The interesting point is 
that successive extension (i.e., adding methods) can be simulated by 
adding messages and extending the handler, but we loose the strong 
consistency guarantee that the messages are actually handled. 
Interestingly, we will still detect a use of a message with different 
number or type of parameters : exactly as when using constraints with 
open types as suggested by Remi Vanicat.

Tiphaine Turpin



Julien Moutinho a écrit :
> On Thu, Feb 21, 2008 at 08:47:17PM +0100, Tiphaine.Turpin wrote:
>   
>> [...]
>>     
>
> Below is a couple of design patterns which may be of interest to you.
> The first one uses the [and] keyword with [class].
> The second one uses the [and] keyword with [class type].
>
> One advantage of the later being its capacity to be split
> into several files (namely: header.ml, observer.ml and subject.ml),
> but it is a little bit more verbose.
>
> BTW, See also this chapter focusing on POO with OCaml:
>   http://caml.inria.fr/pub/docs/oreilly-book/html/index.html#chap-POO
>
> HTH.
>
>
> # First design: implementation
> # -----------------------------
>
> % cat tiph_oo_and.ml
> class ['msg] observer
>   (subject: 'msg subject) =
>   object
>     method subject = subject
>     method send : 'msg -> unit = fun _ -> ()
>   end
> and ['msg] subject =
>   object (self)
>     method private coerce =
>         (self :> 'msg subject)
>     val mutable observers : 'msg observer list = []
>     method add () =
>         let o = new observer self#coerce in
>         observers <- o :: observers; o
>     method notify (msg: 'msg) =
>         List.iter
>           (fun obs -> obs#send msg)
>           observers
>   end
>
> let s = new subject
> let o = s#add ()
> let () = o#send `HELLO
>
> # First design: interface
> # -----------------------------
>
> % ocamlc -i tiph_oo.ml
> class ['a] observer :
>   'a subject ->
>   object
>     method send : 'a -> unit
>     method subject : 'a subject
>   end
>
> and ['a] subject :
>   object
>     val mutable observers : 'a observer list
>     method add : unit -> 'a observer
>     method private coerce : 'a subject
>     method notify : 'a -> unit
>   end
>
> val s : _[> `HELLO ] subject
> val o : _[> `HELLO ] observer
>
> # Second design: implementation
> # -----------------------------
>
> % cat tiph_oo_mod.ml
> module Header =
>   struct
>     class type ['msg] observer =
>       object
>         method subject : 'msg subject
>         method send    : 'msg -> unit
>       end
>     and ['msg] subject =
>       object
>         method add    : unit -> 'msg observer
>         method notify : 'msg -> unit
>       end
>   end
>
> module Observer =
>   struct
>     class ['msg] observer :
>       'msg Header.subject ->
>       ['msg] Header.observer =
>       fun subject ->
>       object
>         method subject = subject
>         method send = fun _ -> ()
>       end
>   end
>
> module Subject =
>   struct
>     class ['msg] subject :
>       ['msg] Header.subject =
>       object (self)
>         method private coerce =
>             (self :> 'msg subject)
>         val mutable observers = []
>         method add () =
>             let o = (new Observer.observer self#coerce :> 'msg Header.observer) in
>             observers <- o :: observers; o
>         method notify (msg: 'msg) =
>             List.iter
>               (fun obs -> obs#send msg)
>               observers
>       end
>   end
>
> let s = new Subject.subject
> let o = s#add ()
> let () = o#send `HELLO
>
> module Subject__alternative =
>   (* NOTE: in this alternative, a double coercion is used
>    * in order to have a [subject] class bigger than
>    * [Header.subject] (a public method [some_method] here). *)
>   struct
>     class ['msg] subject =
>       object (self)
>         method private coerce =
>             ((self :> 'msg subject) :> 'msg Header.subject)
>         val mutable observers = []
>         method add () =
>             let o = (new Observer.observer self#coerce :> 'msg Header.observer) in
>             observers <- o :: observers; o
>         method notify (msg: 'msg) =
>             List.iter
>               (fun obs -> obs#send msg)
>               observers
>         method some_method = ()
>       end
>   end
>
> let s_a = new Subject__alternative.subject
> let o_a = s_a#add ()
> let () = o_a#send `HI
>
>
> # Second design: interface
> # -----------------------------
>
> % ocamlc -i tiph_oo_mod.ml
> module Header :
>   sig
>     class type ['a] observer =
>       object method send : 'a -> unit method subject : 'a subject end
>     and ['a] subject =
>       object method add : unit -> 'a observer method notify : 'a -> unit end
>   end
>
> module Observer :
>   sig class ['a] observer : 'a Header.subject -> ['a] Header.observer end
>
> module Subject : sig class ['a] subject : ['a] Header.subject end
>
> val s : _[> `HELLO ] Subject.subject
> val o : _[> `HELLO ] Header.observer
>
>
> module Subject__alternative :
>   sig
>     class ['a] subject :
>       object
>         val mutable observers : 'a Header.observer list
>         method add : unit -> 'a Header.observer
>         method private coerce : 'a Header.subject
>         method notify : 'a -> unit
>         method some_method : unit
>       end
>   end
>
> val s_a : _[> `HI ] Subject__alternative.subject
> val o_a : _[> `HI ] Header.observer
>
> _______________________________________________
> Caml-list mailing list. Subscription management:
> http://yquem.inria.fr/cgi-bin/mailman/listinfo/caml-list
> Archives: http://caml.inria.fr
> Beginner's list: http://groups.yahoo.com/group/ocaml_beginners
> Bug reports: http://caml.inria.fr/bin/caml-bugs
>