From: "Tiphaine.Turpin" <Tiphaine.Turpin@free.fr>
To: Jacques Garrigue <garrigue@math.nagoya-u.ac.jp>
Cc: Tiphaine.Turpin@irisa.fr, caml-list@yquem.inria.fr
Subject: Re: [Caml-list] OO programming
Date: Wed, 27 Feb 2008 01:25:44 +0100 [thread overview]
Message-ID: <47C4AE08.5000604@free.fr> (raw)
In-Reply-To: <20080226.151750.16504093.garrigue@math.nagoya-u.ac.jp>
Jacques Garrigue a écrit :
> 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.
>
First, my desired use of objects is not (reduced to) programming a gui.
I can imagine that, in guis, the actions that may be invoked on objects
or that may originate from them are just signals with rather simple
types, which form a simple case of object communication for which action
connection is enough, but when it comes to using objects to model the
objects of the application domain, stronger links (i.e., fields) are
sometimes natural. Note that, in class diagrams, links between classes
are one of the most basic concepts.
>
>> 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.
Yes, of course. The word (unplugged was a provocation ;-)).
> 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.
>
Yes, writing such a "challenge" just after the definition (and before
continuing to extend things) looks less beautifull than we would like,
but it should work (I didn't try it).
> 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.
Yes. Clearly, I want the abstractions, patterns, etc. I have in mind to
be expressed themselves as language objects, and not just their
"instanciation". Isn't this the very goal of high-level languages after
all ?
> 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.
>
>
An interesting reading. The expressive power that he adds to nominal
subtyping gives me more arguments to consider structural subtyping not
very usefull (but I understand that structural is much more consistent
with everything else in ocaml).
> here, 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,
which tutorial ?
> but without using type
> parameters. They are replaced by private row types.
>
which is very verbose (having to explicitely write class types), and I'm
not sure I understand the benefits of doing this.
> 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.
>
If the typing of recursive modules just types the body of each module in
the environment given by the declared module types of all modules
involved in the recursive definition, then this cannot work, since
Window(Check) would have to be of type S' for any Check : S', which is
clearly false (e.g. Check = struct ... type observer = <foo: unit;
notify : ...).
I have a hard time understanding your code, and'm still unsure of the
rationale. So, instead of recursive classes, you write a functor of
which the classes are the fix point ? And the private row types are here
just to allow you to add structural contraints on types in a module type
? The functors have the advantage that they group the classes together
in "something", and forcing the relation between their parameters in S''
is interesting. Too bad that your module rec doesn't work...
As for extension, I'm fully satisfied. But the verbosity level is
annoying for scalability...
Tiphaine Turpin
> ---------------------------------------------------------------------------
> 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;;
>
> _______________________________________________
> 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
>
next prev parent reply other threads:[~2008-02-27 0:29 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
2008-02-26 9:36 ` Julien Signoles
2008-02-27 0:25 ` Tiphaine.Turpin [this message]
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=47C4AE08.5000604@free.fr \
--to=tiphaine.turpin@free.fr \
--cc=Tiphaine.Turpin@irisa.fr \
--cc=caml-list@yquem.inria.fr \
--cc=garrigue@math.nagoya-u.ac.jp \
/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