From: Jacques Garrigue <garrigue@math.nagoya-u.ac.jp>
To: Tiphaine.Turpin@irisa.fr
Cc: caml-list@yquem.inria.fr
Subject: Re: [Caml-list] OO programming
Date: Tue, 26 Feb 2008 15:17:50 +0900 (JST) [thread overview]
Message-ID: <20080226.151750.16504093.garrigue@math.nagoya-u.ac.jp> (raw)
In-Reply-To: <47BD44FE.3050001@irisa.fr>
From: Tiphaine Turpin <Tiphaine.Turpin@irisa.fr>
> 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 :
>
> http://caml.inria.fr/pub/docs/u3-ocaml/ocaml-objects.html#toc13
>
> 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 math.nagoya-u.ac.jp
<A HREF=http://www.math.nagoya-u.ac.jp/~garrigue/>JG</A>
module type S = sig
type event
type subject
type observer = private <notify: subject -> event -> unit; ..>
end
module Any(X:S) = struct
class virtual observer =
object
method virtual notify : X.subject -> X.event -> unit
end
class virtual subject =
object (self)
val mutable observers : X.observer list = []
method add_observer obs = observers <- (obs :: observers)
method notify_observers (e : X.event) =
List.iter (fun x -> x#notify self#self e) observers
end
end
module type S' = sig
type event = private [> `Move]
type subject = private <draw: unit; ..>
type observer = private <notify: subject -> event -> unit; ..>
end
module Window(X:S') = struct
module AX = Any(X)
class observer =
object
inherit AX.observer
method notify s e = s#draw
end
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;
end
end
module WindowA = struct
type event = [`Move]
class type observer =
object method notify : subject -> event -> unit end
and subject =
object
method add_observer : observer -> unit
method draw : unit
method identity : int
method move : int -> unit
method notify_observers : event -> unit
end
end
module WindowF : sig
class observer : WindowA.observer
class subject : WindowA.subject
end = struct
module WF = Window(WindowA)
class observer = WF.observer
class subject = object (self)
inherit WF.subject
method private self = (self :> WindowA.subject)
end
end
let window = new WindowF.subject;;
let window_observer = new WindowF.observer;;
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 =
object
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
end
end
module type S'' = sig
type event = private [> WRichT.event]
type observer = private (subject, event) #WRichT.observer
and subject = private (observer, event) #WRichT.subject
end
module WRich (X : S'') = struct
module WRF = Window(X)
class observer =
object
inherit WRF.observer as super
method notify s e = if e <> `Raise then s#raise; super#notify s e
end
let string_of_event = function
`Raise -> "Raise" | `Resize -> "Resize" | `Move -> "Move"
| _ -> "Unknown"
class trace =
object
inherit WRF.observer
method notify s e =
Printf.printf
"<Window %d <== %s>\n" s#identity (string_of_event e)
end
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;
end
end
module WRichA = struct
type event = [`Raise | `Resize | `Move]
class type observer = [subject, event] WRichT.observer
and subject = [observer, event] WRichT.subject
end
module WRichF : sig
class observer : WRichA.observer
class trace : WRichA.observer
class subject : WRichA.subject
end = struct
module WRF = WRich(WRichA)
class observer = WRF.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)
end
end
let window = new WRichF.subject ;;
window#add_observer (new WRichF.observer);;
window#add_observer (new WRichF.trace);;
window#move 1; window#resize 2;;
next prev parent reply other threads:[~2008-02-26 6:18 UTC|newest]
Thread overview: 32+ messages / expand[flat|nested] mbox.gz Atom feed top
2008-02-21 9:31 Tiphaine Turpin
2008-02-21 9:42 ` [Caml-list] " Erik de Castro Lopo
2008-02-21 13:38 ` Remi Vanicat
2008-02-24 16:33 ` [Caml-list] " Dirk Thierbach
2008-02-25 9:23 ` Tiphaine.Turpin
2008-02-25 15:48 ` Edgar Friendly
2008-02-25 16:02 ` Berke Durak
2008-02-25 20:12 ` Dirk Thierbach
2008-02-25 20:51 ` Tiphaine.Turpin
2008-02-25 23:03 ` Dirk Thierbach
2008-02-25 20:10 ` Dirk Thierbach
2008-02-25 21:49 ` Tiphaine.Turpin
2008-02-25 23:07 ` Dirk Thierbach
2008-02-29 14:22 ` Tiphaine.Turpin
2008-02-26 6:17 ` Jacques Garrigue [this message]
2008-02-26 9:36 ` Julien Signoles
2008-02-27 0:25 ` Tiphaine.Turpin
2008-02-27 1:37 ` Jacques Garrigue
2008-02-28 8:34 ` Keiko Nakata
2008-02-28 13:30 ` Andrej Bauer
2008-02-28 15:18 ` Keiko Nakata
2008-02-28 16:02 ` Edgar Friendly
2008-02-29 14:35 ` Tiphaine.Turpin
2008-02-29 15:58 ` Keiko Nakata
2008-03-03 9:40 ` Tiphaine.Turpin
2008-03-03 10:20 ` Jacques Garrigue
2008-03-03 10:30 ` Tiphaine.Turpin
2008-03-03 15:18 ` Keiko Nakata
2008-03-03 19:25 ` Tiphaine Turpin
2008-03-04 14:00 ` Keiko Nakata
2008-02-27 7:40 ` Dirk Thierbach
2008-02-27 14:04 ` Tiphaine.Turpin
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=20080226.151750.16504093.garrigue@math.nagoya-u.ac.jp \
--to=garrigue@math.nagoya-u.ac.jp \
--cc=Tiphaine.Turpin@irisa.fr \
--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