* OO programming
@ 2008-02-21 9:31 Tiphaine Turpin
2008-02-21 9:42 ` [Caml-list] " Erik de Castro Lopo
` (3 more replies)
0 siblings, 4 replies; 32+ messages in thread
From: Tiphaine Turpin @ 2008-02-21 9:31 UTC (permalink / raw)
To: caml-list
Hello,
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.
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.
Hence my question: does anyone knows a way of combining the reusability
of sets of related classes with a more modular (type/consistency)-checking ?
Tiphaine Turpin
P.S. : Sorry for not providing any example myself ; the above link has
very clear ones, that express exactly what I mean.
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [Caml-list] OO programming
2008-02-21 9:31 OO programming Tiphaine Turpin
@ 2008-02-21 9:42 ` Erik de Castro Lopo
2008-02-21 13:38 ` Remi Vanicat
` (2 subsequent siblings)
3 siblings, 0 replies; 32+ messages in thread
From: Erik de Castro Lopo @ 2008-02-21 9:42 UTC (permalink / raw)
To: Tiphaine Turpin; +Cc: caml-list
Tiphaine Turpin wrote:
> 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.
OO in Ocaml is vastly different from OO in languages you might be used
to like Java, C++, Python etc.
If you are new to Ocaml, I highly recommend you forget about using Ocaml's
OO features until you feel comfortable with the base features of the
language.
Erik
--
-----------------------------------------------------------------
Erik de Castro Lopo
-----------------------------------------------------------------
"Projects promoting programming in natural language are intrinsically
doomed to fail." -- Edsger Dijkstra
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: OO programming
2008-02-21 9:31 OO programming 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-26 6:17 ` Jacques Garrigue
3 siblings, 0 replies; 32+ messages in thread
From: Remi Vanicat @ 2008-02-21 13:38 UTC (permalink / raw)
To: caml-list
Tiphaine Turpin <Tiphaine.Turpin@irisa.fr> writes:
> Hello,
>
> 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.
>
> 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.
>
> Hence my question: does anyone knows a way of combining the
> reusability of sets of related classes with a more modular
> (type/consistency)-checking ?
something like that might work (from the Dider Remy example)
class ['observer] subject =
object (self : 'mytype)
val mutable observers : 'observer list = []
method add obs = observers <- obs :: observers
method notify (message : 'observer -> 'mytype -> unit) =
List.iter (fun obs -> message obs self) observers
end;;
class ['subject] observer =
object
constraint 'subject = 'a #subject
end;;
Note that it doesn't solve completely the problem (as #subject is
still an open type) but it might catch some problem.
--
Rémi Vanicat
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [Caml-list] OO programming
2008-02-21 9:31 OO programming 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 ` Dirk Thierbach
2008-02-25 9:23 ` Tiphaine.Turpin
2008-02-26 6:17 ` Jacques Garrigue
3 siblings, 1 reply; 32+ messages in thread
From: Dirk Thierbach @ 2008-02-24 16:33 UTC (permalink / raw)
To: caml-list
On Thu, Feb 21, 2008 at 10:31:42AM +0100, Tiphaine Turpin wrote:
> 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 way I handle this is the following:
> The usual problem is that objects refer to other objects,
I avoid this as much as possible. Instead of "connecting" objects
to other objects, I "connect" an objects to "actions" (functions),
which may be closures that include other objects. The typical example
which has already been given is the observer-pattern, which I use
as follows:
class ['a] observer =
object(self)
val mutable actions = []
method register action =
actions <- action :: actions
method notify (arg : 'a) =
List.iter (fun f -> f arg) actions
end;;
No class for subject needed. Many of the objects I use in the GUI layer
just inherit from observer, and it's also handy if the action you want
doesn't refer to an explicit object in the OCaml layer (because the
state is kept in the external GUI layer)
If possible, I try to make each object as independently from the rest
of the world as I can (after all, grouping a related part of the world
state together is very central to OO), and then I "plug together" all
these objects at a higher level.
> those objects may refer back to objects referring to them,
No problem with the above way of using "actions".
> then objects refer to different objects, some of them having more
> methods than others (subtyping),
I also use objects as parameters, both for classes and functions.
That all works as it is intended, the type just collects any method
calls that are used in the parameter. The only disadvantage is that
a type error is then usually discovered only at the time when I "plug"
all these things together. That's somewhat annoying, and if I really
cannot figure out what's going on, introducing type declarations into
a few places usually helps.
> 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.
That problem often goes away if these fields are initialized, or used
in a method (which can be annoying if the class is not yet completely
written, but you want to test the stuff you have so far).
And of course, I try to use as little mutable state as possible (after
all, it's a functional programming language :-)
Much more annoying is that one also has to give types to arguments in
methods that are unused (they are present make them compatible with
another class, which uses them; or they are required by the GUI
layer), and that OCaml has now way to seperate method/function
declaration and type declaration (unless one uses a mli file, which is
again inconvenient because it splits information that belongs together
into two complete different places). I'd really appreciate it if one
could just mix "val" type declcarations with "let" or "method" code
declarations.
> 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.
Not sure what exactly you mean here. Could you give an example?
> The best guidelines that I found are in the following course from
> Didier Remy [...] 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.
Yes, see above. You can use class types to get around this problem,
but if you keep the classes small, one can live with.
> 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.
Ugh. No, thanks :-)
- Dirk
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [Caml-list] OO programming
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 20:10 ` Dirk Thierbach
0 siblings, 2 replies; 32+ messages in thread
From: Tiphaine.Turpin @ 2008-02-25 9:23 UTC (permalink / raw)
To: caml-list
Dirk Thierbach a écrit :
> On Thu, Feb 21, 2008 at 10:31:42AM +0100, Tiphaine Turpin wrote:
>
>> The usual problem is that objects refer to other objects,
>>
>
> I avoid this as much as possible.
No connecting of objects, therefore no mutually recursive methods, (and
many arguments added to every function to carry the missing context),
little mutable state, and no use of class names to specify types... I'm
sure that many problems go away in this setting, and there are still
advantages to using objects (inheritance, etc.) but isn't this a
somewhat degraded version of the object paradigm ?
> Instead of "connecting" objects
> to other objects, I "connect" an objects to "actions" (functions),
> which may be closures that include other objects. The typical example
> which has already been given is the observer-pattern, which I use
> as follows:
>
> class ['a] observer =
> object(self)
> val mutable actions = []
> method register action =
> actions <- action :: actions
> method notify (arg : 'a) =
> List.iter (fun f -> f arg) actions
> end;;
>
> No class for subject needed. Many of the objects I use in the GUI layer
> just inherit from observer, and it's also handy if the action you want
> doesn't refer to an explicit object in the OCaml layer (because the
> state is kept in the external GUI layer)
> [...]
>
> Much more annoying is that one also has to give types to arguments in
> methods that are unused (they are present make them compatible with
> another class, which uses them;
which is where I think mutually recursive classes would be usefull, if
their typing was easier.
> or they are required by the GUI
> layer), and that OCaml has now way to seperate method/function
> declaration and type declaration (unless one uses a mli file, which is
> again inconvenient because it splits information that belongs together
> into two complete different places). I'd really appreciate it if one
> could just mix "val" type declcarations with "let" or "method" code
> declarations.
>
I don't understand what you want here.
>
>> 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.
>>
>
> Not sure what exactly you mean here. Could you give an example?
>
I will try to state at an abstract level what I would like to do :
- define parametric virtual classes A and B so that every A has a Bs
(one or many...) and vice-versa.
- possibly extend A and B to A' and B' respectively, so that every A'
has B's (and not just Bs), etc.
- subtyping: possibly extend B to B1 and B2 so that their objects have
As, but those As still have Bs, so that I can associate the same A to
objects of class B, B1 or B2.
- and the ultimate goal combining all that: define A and B as above,
other virtual classes C and D in a similar way and E and F too, and
define concrete classes X Y Z1 Z2 just by saying that X extends A, Y
will play the role of B in the first two classes and the role of C in
the last two ones, and Z1 Z2 extends D (both) and E and F
(respectively). It happens that some of the types that were left
parametric (respectively methods defined as virtual) in B are made
concrete in C, (and so on), so I obtain my final concrete classes.
Finally, I want the typing to be done step by step, so that for example
the incompatibilities that exist already between A and B are detected
independently of the rest.
I understand that what I'm asking for is a lot. The subtyping is
especially problematic, and I begin to wonder if it's not related to the
famous cases of inheritance that do not imply subtyping...
For now, I found many introductions on ocaml objects using and how to
actually use them (Ocaml manual, Didier Remy's course, the "Developing
applications with Objective Caml" book, as suggested by Julien Moutinho,
and also Philippe Narbel's book) but none of them went that far.
Tiphaine Turpin
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [Caml-list] OO programming
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:10 ` Dirk Thierbach
1 sibling, 1 reply; 32+ messages in thread
From: Edgar Friendly @ 2008-02-25 15:48 UTC (permalink / raw)
To: Tiphaine.Turpin; +Cc: caml-list
Tiphaine.Turpin wrote:
>> or they are required by the GUI
>> layer), and that OCaml has now way to seperate method/function
>> declaration and type declaration (unless one uses a mli file, which is
>> again inconvenient because it splits information that belongs together
>> into two complete different places). I'd really appreciate it if one
>> could just mix "val" type declcarations with "let" or "method" code
>> declarations.
>>
> I don't understand what you want here.
I read him as asking for .ml files to contain what .mli files now
contain - i.e. method/function declarations
Example - string_annex.ml:
val init: int -> (int -> char) -> string
let init n f =
let s = make n (f 0) in
for i=1 to n-1 do
set s i (f i);
done;
s
OCaml doesn't permit this at the moment because val statements go in
.mli files (or in sig blocks) only.
E.
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [Caml-list] OO programming
2008-02-25 15:48 ` Edgar Friendly
@ 2008-02-25 16:02 ` Berke Durak
2008-02-25 20:12 ` Dirk Thierbach
0 siblings, 1 reply; 32+ messages in thread
From: Berke Durak @ 2008-02-25 16:02 UTC (permalink / raw)
To: Edgar Friendly; +Cc: Tiphaine.Turpin, caml-list
Edgar Friendly a écrit :
> Tiphaine.Turpin wrote:
>>> or they are required by the GUI
>>> layer), and that OCaml has now way to seperate method/function
>>> declaration and type declaration (unless one uses a mli file, which is
>>> again inconvenient because it splits information that belongs together
>>> into two complete different places). I'd really appreciate it if one
>>> could just mix "val" type declcarations with "let" or "method" code
>>> declarations.
>>>
>> I don't understand what you want here.
>
> I read him as asking for .ml files to contain what .mli files now
> contain - i.e. method/function declarations
>
> Example - string_annex.ml:
>
> val init: int -> (int -> char) -> string
>
> let init n f =
> let s = make n (f 0) in
> for i=1 to n-1 do
> set s i (f i);
> done;
> s
>
>
> OCaml doesn't permit this at the moment because val statements go in
> .mli files (or in sig blocks) only.
>
Yes that could be nice but it's no biggie IMHO.
let init n f =
let s = make n (f 0) in
for i=1 to n-1 do
set s i (f i);
done;
s
let init : int -> (int -> char) -> string = init
--
Berke DURAK
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [Caml-list] OO programming
2008-02-25 9:23 ` Tiphaine.Turpin
2008-02-25 15:48 ` Edgar Friendly
@ 2008-02-25 20:10 ` Dirk Thierbach
2008-02-25 21:49 ` Tiphaine.Turpin
2008-02-29 14:22 ` Tiphaine.Turpin
1 sibling, 2 replies; 32+ messages in thread
From: Dirk Thierbach @ 2008-02-25 20:10 UTC (permalink / raw)
To: caml-list
On Mon, Feb 25, 2008 at 10:23:09AM +0100, Tiphaine.Turpin wrote:
> Dirk Thierbach a écrit :
> >On Thu, Feb 21, 2008 at 10:31:42AM +0100, Tiphaine Turpin wrote:
> No connecting of objects, therefore no mutually recursive methods, (and
> many arguments added to every function to carry the missing context),
> little mutable state, and no use of class names to specify types... I'm
> sure that many problems go away in this setting, and there are still
> advantages to using objects (inheritance, etc.)
Let me repeat: The main advantage of OO is that you can group related
state together, and that you can "push" changes (events, secondary
changes caused by events, whatever) onto this network of state.
While the functional way (especially with lazy computations) is to
"pull" a result from a network of computations.
> but isn't this a somewhat degraded version of the object paradigm ?
Well, people can't seem to agree what exactly the "object paradigm"
actually is in the first place :-). Personally, I don't care. I want
clean, flexible, decoupled code. I am not interested in doing something
just because eomeone tells me, possible mistakenly, that I have to
follow "the" paradigm.
>> Much more annoying is that one also has to give types to arguments in
>> methods that are unused (they are present make them compatible with
>> another class, which uses them;
> which is where I think mutually recursive classes would be usefull, if
> their typing was easier.
Mutual recursive classes won't solve this problem at all. Maybe an
example helps: In a GUI, one can get various sorts of events, say,
a "resize" event. If the method connected to that event is only
interested in the new width and not in the height, so it doesn't
use this value at all, you have to declare the type. No other class
involved at all.
>> I'd really appreciate it if one could just mix "val" type
>> declcarations with "let" or "method" code declarations.
> I don't understand what you want here.
See Edgar's response. But that was only just a side-remark.
>>> 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.
>> Not sure what exactly you mean here. Could you give an example?
> I will try to state at an abstract level what I would like to do :
>
> - define parametric virtual classes A and B so that every A has a Bs
> (one or many...) and vice-versa.
> - possibly extend A and B to A' and B' respectively, so that every A'
> has B's (and not just Bs), etc.
> - subtyping: possibly extend B to B1 and B2 so that their objects have
> As, but those As still have Bs, so that I can associate the same A to
> objects of class B, B1 or B2.
>
> - and the ultimate goal combining all that: define A and B as above,
> other virtual classes C and D in a similar way and E and F too, and
> define concrete classes X Y Z1 Z2 just by saying that X extends A, Y
> will play the role of B in the first two classes and the role of C in
> the last two ones, and Z1 Z2 extends D (both) and E and F
> (respectively). It happens that some of the types that were left
> parametric (respectively methods defined as virtual) in B are made
> concrete in C, (and so on), so I obtain my final concrete classes.
If you manage to find a simple type system for that, preferably with
type inference and principal typings, I'd like to see it :-)
> For now, I found many introductions on ocaml objects using and how to
> actually use them (Ocaml manual, Didier Remy's course, the "Developing
> applications with Objective Caml" book, as suggested by Julien Moutinho,
> and also Philippe Narbel's book) but none of them went that far.
So maybe there's a reason they are doing it differently. :-) You
can fight the system, and try to make everything difficult because you
think "it ought to be this way", or you can go the easy route. Your
choice. I can only tell you what I found that works for me.
- Dirk
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [Caml-list] OO programming
2008-02-25 16:02 ` Berke Durak
@ 2008-02-25 20:12 ` Dirk Thierbach
2008-02-25 20:51 ` Tiphaine.Turpin
0 siblings, 1 reply; 32+ messages in thread
From: Dirk Thierbach @ 2008-02-25 20:12 UTC (permalink / raw)
To: caml-list
On Mon, Feb 25, 2008 at 05:02:22PM +0100, Berke Durak wrote:
> Yes that could be nice but it's no biggie IMHO.
>
> let init n f =
> let s = make n (f 0) in
> for i=1 to n-1 do
> set s i (f i);
> done;
> s
>
> let init : int -> (int -> char) -> string = init
Nice. I hadn't seen this trick before. You don't happen to know a
similar trick that works with methods?
- Dirk
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [Caml-list] OO programming
2008-02-25 20:12 ` Dirk Thierbach
@ 2008-02-25 20:51 ` Tiphaine.Turpin
2008-02-25 23:03 ` Dirk Thierbach
0 siblings, 1 reply; 32+ messages in thread
From: Tiphaine.Turpin @ 2008-02-25 20:51 UTC (permalink / raw)
To: Dirk Thierbach; +Cc: caml-list
Dirk Thierbach a écrit :
> On Mon, Feb 25, 2008 at 05:02:22PM +0100, Berke Durak wrote:
>
>> Yes that could be nice but it's no biggie IMHO.
>>
>> let init n f =
>> let s = make n (f 0) in
>> for i=1 to n-1 do
>> set s i (f i);
>> done;
>> s
>>
>> let init : int -> (int -> char) -> string = init
>>
>
> Nice. I hadn't seen this trick before. You don't happen to know a
> similar trick that works with methods?
>
I get the idea. Would the following satisfy your needs (you may place
the code and the type in any order) ?
let _ = object (self)
method init n f =
let s = make n (f 0) in
for i=1 to n-1 do
set s i (f i);
done;
s
method virtual init : int -> (int -> char) -> string
end
Tiphaine Turpin
> - Dirk
>
> _______________________________________________
> 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
>
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [Caml-list] OO programming
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
1 sibling, 1 reply; 32+ messages in thread
From: Tiphaine.Turpin @ 2008-02-25 21:49 UTC (permalink / raw)
To: Dirk Thierbach; +Cc: caml-list
Dirk Thierbach a écrit :
> I am not interested in doing something
> just because eomeone tells me, possible mistakenly, that I have to
> follow "the" paradigm.
>
I won't blame you for that. I'm not either :-). The few (2-3) programs
of "not-toy" size that I did using objects (either in Java or in Ocaml)
both extensively used many linked objects implementing lots of methods
in a mutually recursive way, so I consider this an crucial features of
objects. And really, I code things the way I think I'm able to do them
the best (I have the chance that my programs only need to look nice to
me): I do what I can. I choose functional or imperative style according
to my best understanding of the domain, or objects (in Ocaml, I consider
objects only if I think that I cannot achieve my goals in a satisfactory
way with functions).
>
>>> Much more annoying is that one also has to give types to arguments in
>>> methods that are unused (they are present make them compatible with
>>> another class, which uses them;
>>>
>
>
>> which is where I think mutually recursive classes would be usefull, if
>> their typing was easier.
>>
>
> Mutual recursive classes won't solve this problem at all.
Yes it does, I think (wether it's a good way of solving it is
debatable). If you declare the classes generating events together with
classes that handle them, and if it appears (in the declaration, which
is where fields are usefull) that the method used in the generating
objects will be invoked on the handler objects, then the types of the
two methods will need to be compatible. Of course, in your example, you
would probably want to use either a class type for handler objects, or
have them inherit from a virtual handler class that declare the type of
the method once and for all. My point is that using in one of the
classes the type associated with the other class for one of its fields
(and invoking methods on that fields) puts the necessary constraints to
free you from many declarations (mutual recursive classes are actually
only needed if you want links in both directions, which is not
necessarily the case of your example, I agree).
> Maybe an
> example helps: In a GUI, one can get various sorts of events, say,
> a "resize" event. If the method connected to that event is only
> interested in the new width and not in the height, so it doesn't
> use this value at all, you have to declare the type. No other class
> involved at all.
>
>> I will try to state at an abstract level what I would like to do :
>>
>> - define parametric virtual classes A and B so that every A has a Bs
>> (one or many...) and vice-versa.
>> - possibly extend A and B to A' and B' respectively, so that every A'
>> has B's (and not just Bs), etc.
>> - subtyping: possibly extend B to B1 and B2 so that their objects have
>> As, but those As still have Bs, so that I can associate the same A to
>> objects of class B, B1 or B2.
>>
>> - and the ultimate goal combining all that: define A and B as above,
>> other virtual classes C and D in a similar way and E and F too, and
>> define concrete classes X Y Z1 Z2 just by saying that X extends A, Y
>> will play the role of B in the first two classes and the role of C in
>> the last two ones, and Z1 Z2 extends D (both) and E and F
>> (respectively). It happens that some of the types that were left
>> parametric (respectively methods defined as virtual) in B are made
>> concrete in C, (and so on), so I obtain my final concrete classes.
>>
>
> If you manage to find a simple type system for that, preferably with
> type inference and principal typings, I'd like to see it :-)
>
I wish I could do that in Ocaml...
>> For now, I found many introductions on ocaml objects using and how to
>> actually use them (Ocaml manual, Didier Remy's course, the "Developing
>> applications with Objective Caml" book, as suggested by Julien Moutinho,
>> and also Philippe Narbel's book) but none of them went that far.
>>
>
> So maybe there's a reason they are doing it differently. :-)
They aren't doing differently. They're just not treating an exhaustive
list of the software architecture problems that people may encouter (or,
pose to themselve). Which does not scandalize me in itself ;-).
Tiphaine Turpin
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [Caml-list] OO programming
2008-02-25 20:51 ` Tiphaine.Turpin
@ 2008-02-25 23:03 ` Dirk Thierbach
0 siblings, 0 replies; 32+ messages in thread
From: Dirk Thierbach @ 2008-02-25 23:03 UTC (permalink / raw)
To: caml-list
On Mon, Feb 25, 2008 at 09:51:14PM +0100, Tiphaine.Turpin wrote:
> I get the idea. Would the following satisfy your needs (you may place
> the code and the type in any order) ?
>
> let _ = object (self)
>
> method init n f =
> let s = make n (f 0) in
> for i=1 to n-1 do
> set s i (f i);
> done;
> s
>
> method virtual init : int -> (int -> char) -> string
>
> end
Yes, cool. Didn't think of virtual methods. Thanks.
- Dirk
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [Caml-list] OO programming
2008-02-25 21:49 ` Tiphaine.Turpin
@ 2008-02-25 23:07 ` Dirk Thierbach
0 siblings, 0 replies; 32+ messages in thread
From: Dirk Thierbach @ 2008-02-25 23:07 UTC (permalink / raw)
To: caml-list
On Mon, Feb 25, 2008 at 10:49:49PM +0100, Tiphaine.Turpin wrote:
> The few (2-3) programs of "not-toy" size that I did using objects
> (either in Java or in Ocaml) both extensively used many linked
> objects implementing lots of methods in a mutually recursive way,
Then maybe one should look at one of those as a concrete example, and
it might be possible to decouple those interdependencies.
>>>> Much more annoying is that one also has to give types to arguments in
>>>> methods that are unused (they are present make them compatible with
>>>> another class, which uses them;
>> Mutual recursive classes won't solve this problem at all.
> Yes it does, I think (wether it's a good way of solving it is
> debatable). If you declare the classes generating events together with
> classes that handle them,
I can't. First, they are not necessarily classes. Second, all this
stuff is provided by the GUI framework, which I cannot change.
- Dirk
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [Caml-list] OO programming
2008-02-21 9:31 OO programming Tiphaine Turpin
` (2 preceding siblings ...)
2008-02-24 16:33 ` [Caml-list] " Dirk Thierbach
@ 2008-02-26 6:17 ` Jacques Garrigue
2008-02-26 9:36 ` Julien Signoles
2008-02-27 0:25 ` Tiphaine.Turpin
3 siblings, 2 replies; 32+ messages in thread
From: Jacques Garrigue @ 2008-02-26 6:17 UTC (permalink / raw)
To: Tiphaine.Turpin; +Cc: caml-list
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;;
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [Caml-list] OO programming
2008-02-26 6:17 ` Jacques Garrigue
@ 2008-02-26 9:36 ` Julien Signoles
2008-02-27 0:25 ` Tiphaine.Turpin
1 sibling, 0 replies; 32+ messages in thread
From: Julien Signoles @ 2008-02-26 9:36 UTC (permalink / raw)
To: Jacques Garrigue; +Cc: caml-list
>> 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.
As far as I understand the Jacques' code, the following check seems to
work
===
module rec Check : sig
type event = private [> `Move]
type subject = private <draw: unit; ..>
type observer = private < notify : Check.subject -> Check.event -> unit; .. >
end = struct
include Window(Check)
type event = Check.event
end
===
I don't know exactly why one has to write "Check.subject" and
"Check.event" in the signature. The consequence is that the following
code just does not work.
===
module rec Check : S' = struct
include Window(Check)
type event = Check.event
end
===
As Jacques said, it is either a bug or a limitation of recursive modules.
I don't know.
--
Julien
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [Caml-list] OO programming
2008-02-26 6:17 ` Jacques Garrigue
2008-02-26 9:36 ` Julien Signoles
@ 2008-02-27 0:25 ` Tiphaine.Turpin
2008-02-27 1:37 ` Jacques Garrigue
2008-02-27 7:40 ` Dirk Thierbach
1 sibling, 2 replies; 32+ messages in thread
From: Tiphaine.Turpin @ 2008-02-27 0:25 UTC (permalink / raw)
To: Jacques Garrigue; +Cc: Tiphaine.Turpin, caml-list
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
>
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [Caml-list] OO programming
2008-02-27 0:25 ` Tiphaine.Turpin
@ 2008-02-27 1:37 ` Jacques Garrigue
2008-02-28 8:34 ` Keiko Nakata
2008-02-27 7:40 ` Dirk Thierbach
1 sibling, 1 reply; 32+ messages in thread
From: Jacques Garrigue @ 2008-02-27 1:37 UTC (permalink / raw)
To: Tiphaine.Turpin, caml-list
From: "Tiphaine.Turpin" <Tiphaine.Turpin@free.fr>
> 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.
My point was that it's not because it is a good concept that it is
necessarily a good programming technique. That is, you might want to
right your class diagram with links between classes, but use an
indirection at the connection level in your implementation, if it
makes things simpler. I see no contradiction there. But as I wrote in
my previous mail, OO people often prefer to have their concepts
directly apparent in their programs.
> > 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 ?
Yes, but the ocaml approach is to define entities independently, and
let types handle the interconnections. This does not fit well with
class diagrams. So if you want to keep your class diagram apparent,
you need painful contorsions.
Note also that your original goal, being able to extend families of
classes at the type level, is an active research area. When ocaml was
created, there was no OO language able to do it explicitly, or even
implicitly (as ocaml does). Now you might have your chance with Scala
or gBeta.
> > 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).
Not really. He build his nominal subtyping as an extension of
structural subtyping. And I'm personally not sure that the nominal
part gets him much (out of better error messages, which do matter)
> >> 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 ?
Section 5.3 of the reference manual, which is a variation on the one
you were mentionning.
> > 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.
Verbose indeed. I didn't say this was the right way to do it, just
that it lets you express more relations in the interfaces.
> > 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 : ...).
No, the typing of recursive modules is much more clever, otherwise it
wouldn't be useful. So, conceptually, this could work.
For instance, if you drop the ability to add methods to the observer,
the check goes through:
module type S' = sig
type event = private [> `Move]
type subject = private <draw: unit; ..>
type observer = <notify: subject -> event -> unit>
end
module Window(X:S') = struct
module AX = Any(X)
type event = X.event
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 rec Check : S' = Window(Check)
This way you can see that subject and observer are compatible without
providing concrete instances.
However, when the observer and subject have both extensible types,
there seems to be an interaction between nominal and structural types,
and the knot cannot be tied.
> As for extension, I'm fully satisfied. But the verbosity level is
> annoying for scalability...
Well, yes, that's always the problem with functors...
Jacques Garrigue
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [Caml-list] OO programming
2008-02-27 0:25 ` Tiphaine.Turpin
2008-02-27 1:37 ` Jacques Garrigue
@ 2008-02-27 7:40 ` Dirk Thierbach
2008-02-27 14:04 ` Tiphaine.Turpin
1 sibling, 1 reply; 32+ messages in thread
From: Dirk Thierbach @ 2008-02-27 7:40 UTC (permalink / raw)
To: caml-list
On Wed, Feb 27, 2008 at 01:25:44AM +0100, Tiphaine.Turpin wrote:
> First, my desired use of objects is not (reduced to) programming a gui.
When I said "let's look at a concrete example", I meant it. It might
be very instructive. If your program is too long, just give an high-level
overview of the situation, this should be enough for a start.
- Dirk
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [Caml-list] OO programming
2008-02-27 7:40 ` Dirk Thierbach
@ 2008-02-27 14:04 ` Tiphaine.Turpin
0 siblings, 0 replies; 32+ messages in thread
From: Tiphaine.Turpin @ 2008-02-27 14:04 UTC (permalink / raw)
To: caml-list
Dirk Thierbach a écrit :
> On Wed, Feb 27, 2008 at 01:25:44AM +0100, Tiphaine.Turpin wrote:
>
>> First, my desired use of objects is not (reduced to) programming a gui.
>>
>
> When I said "let's look at a concrete example", I meant it. It might
> be very instructive. If your program is too long, just give an high-level
> overview of the situation, this should be enough for a start.
>
I'm affraid my existing "real" code wouldn't be any usefull: it is very
long indeed (>1000loc), fits mainly in one recursive definition of
classes (with heavy and unstructured use of multiple inheritance and
virtual classes), and is unreadable (application domain already complex,
+ all features (gui, etc.) mixed in the same objects...), which led me
to drop the project until I find new ideas for structuring it (which I
am seeking here). The very abstract extension cases that stated earlier
are the best solution that I can think of to put some structure in all
that, and that's why I'm trying to get the possible best way of
implementing this "pattern".
Tiphaine Turpin
> - Dirk
>
> _______________________________________________
> 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
>
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [Caml-list] OO programming
2008-02-27 1:37 ` Jacques Garrigue
@ 2008-02-28 8:34 ` Keiko Nakata
2008-02-28 13:30 ` Andrej Bauer
2008-02-29 14:35 ` Tiphaine.Turpin
0 siblings, 2 replies; 32+ messages in thread
From: Keiko Nakata @ 2008-02-28 8:34 UTC (permalink / raw)
To: garrigue; +Cc: Tiphaine.Turpin, caml-list
Hello,
> > As for extension, I'm fully satisfied. But the verbosity level is
> > annoying for scalability...
>
> Well, yes, that's always the problem with functors...
Since there are some people (including me)
who are interested in using functors and recursive modules
in the style of object-oriented context,
I thought that it could be useful to devise
a (camlp4) syntax extension which mitigates this a bit painful verbosity.
At the moment, I have no idea which syntax is general enough and intuitive for us,
but as far as I understand we always follow similar encodings.
With best regards,
Keiko
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [Caml-list] OO programming
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
1 sibling, 2 replies; 32+ messages in thread
From: Andrej Bauer @ 2008-02-28 13:30 UTC (permalink / raw)
To: Caml
Keiko Nakata wrote:
>>> As for extension, I'm fully satisfied. But the verbosity level is
>>> annoying for scalability...
>> Well, yes, that's always the problem with functors...
>
> Since there are some people (including me)
> who are interested in using functors and recursive modules
> in the style of object-oriented context,
> I thought that it could be useful to devise
> a (camlp4) syntax extension which mitigates this a bit painful verbosity.
>
> At the moment, I have no idea which syntax is general enough and intuitive for us,
> but as far as I understand we always follow similar encodings.
I have three wishes related to the case when a functor accepts a
structure that contains a single type or a single value:
1) To be able to write
module F(type t) = struct ...t... end
instead of
module F(T : sig type t end) = struct ... T.t ... end
and to write
F(s)
instead of
F(struct type t = s end)
2) Similarly for values, to be able to write
module F(val x : t) = struct ... x ... end
instead of
module F(T : sig val x : t end) = struct ... T.x ... end
3) Similarly for signatures:
module type F = functor type t -> sig ... end
module type F = functor val x : t -> sig ... end
I believe these are campl4 trivialities. There may be some hoxus-pocus
related to generating suitable names for T's above.
Andrej
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [Caml-list] OO programming
2008-02-28 13:30 ` Andrej Bauer
@ 2008-02-28 15:18 ` Keiko Nakata
2008-02-28 16:02 ` Edgar Friendly
1 sibling, 0 replies; 32+ messages in thread
From: Keiko Nakata @ 2008-02-28 15:18 UTC (permalink / raw)
To: caml-list
> I have three wishes related to the case when a functor accepts a
> structure that contains a single type or a single value:
>
> 1) To be able to write
>
> module F(type t) = struct ...t... end
>
> instead of
>
> module F(T : sig type t end) = struct ... T.t ... end
>
> and to write
>
> F(s)
>
> instead of
>
> F(struct type t = s end)
>
> 2) Similarly for values, to be able to write
>
> module F(val x : t) = struct ... x ... end
>
> instead of
>
> module F(T : sig val x : t end) = struct ... T.x ... end
>
> 3) Similarly for signatures:
>
> module type F = functor type t -> sig ... end
>
> module type F = functor val x : t -> sig ... end
>
> I believe these are campl4 trivialities. There may be some hoxus-pocus
> related to generating suitable names for T's above.
This is merely my personal preference, but I usually do not prefer
implicit construction of names; for instance I may well get confused
by type error messages.
Besides in this scenario:
> module F(type t) = struct ...t... end
we need to decide which take precedence if the structure declares
a type component named t.
On the other hand, since the functor parameter
has the role of the super class in the functor&fix-point approach
to OO-style programming, it may well be useful
to have an implicit way to refer to components of the parameter,
as you proposed. Yet this may be confusing again if we consider
multi-parameter functors a la multiple inheritance.
What do you think?
With best regards,
Keiko
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [Caml-list] OO programming
2008-02-28 13:30 ` Andrej Bauer
2008-02-28 15:18 ` Keiko Nakata
@ 2008-02-28 16:02 ` Edgar Friendly
1 sibling, 0 replies; 32+ messages in thread
From: Edgar Friendly @ 2008-02-28 16:02 UTC (permalink / raw)
To: Andrej.Bauer; +Cc: Caml
Andrej Bauer wrote:
> Keiko Nakata wrote:
>>>> As for extension, I'm fully satisfied. But the verbosity level is
>>>> annoying for scalability...
>>> Well, yes, that's always the problem with functors...
>>
>> Since there are some people (including me) who are interested in using
>> functors and recursive modules in the style of object-oriented
>> context, I thought that it could be useful to devise a (camlp4) syntax
>> extension which mitigates this a bit painful verbosity.
>> At the moment, I have no idea which syntax is general enough and
>> intuitive for us,
>> but as far as I understand we always follow similar encodings.
>
> I have three wishes related to the case when a functor accepts a
> structure that contains a single type or a single value:
>
> 1) To be able to write
>
> module F(type t) = struct ...t... end
>
> instead of
>
> module F(T : sig type t end) = struct ... T.t ... end
>
> and to write
>
> F(s)
>
> instead of
>
> F(struct type t = s end)
>
I wonder what use you would put this to where normal 'a polymorphism
wouldn't suffice.
> 2) Similarly for values, to be able to write
>
> module F(val x : t) = struct ... x ... end
>
> instead of
>
> module F(T : sig val x : t end) = struct ... T.x ... end
>
Similarly, an additional parameter on each of the contained functions
seems not too unreasonable. I guess I can see more use for this than
#1, but a global ref inside F might also work, as long as you had a way
to set it and you had a good default.
E.
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [Caml-list] OO programming
2008-02-25 20:10 ` Dirk Thierbach
2008-02-25 21:49 ` Tiphaine.Turpin
@ 2008-02-29 14:22 ` Tiphaine.Turpin
1 sibling, 0 replies; 32+ messages in thread
From: Tiphaine.Turpin @ 2008-02-29 14:22 UTC (permalink / raw)
To: caml-list
[-- Attachment #1: Type: text/plain, Size: 11367 bytes --]
Dirk Thierbach a écrit :
> On Mon, Feb 25, 2008 at 10:23:09AM +0100, Tiphaine.Turpin wrote:
>
>> I will try to state at an abstract level what I would like to do :
>>
>> - define parametric virtual classes A and B so that every A has a Bs
>> (one or many...) and vice-versa.
>> - possibly extend A and B to A' and B' respectively, so that every A'
>> has B's (and not just Bs), etc.
>> - subtyping: possibly extend B to B1 and B2 so that their objects have
>> As, but those As still have Bs, so that I can associate the same A to
>> objects of class B, B1 or B2.
>>
>> - and the ultimate goal combining all that: define A and B as above,
>> other virtual classes C and D in a similar way and E and F too, and
>> define concrete classes X Y Z1 Z2 just by saying that X extends A, Y
>> will play the role of B in the first two classes and the role of C in
>> the last two ones, and Z1 Z2 extends D (both) and E and F
>> (respectively). It happens that some of the types that were left
>> parametric (respectively methods defined as virtual) in B are made
>> concrete in C, (and so on), so I obtain my final concrete classes.
>>
>
> If you manage to find a simple type system for that, preferably with
> type inference and principal typings, I'd like to see it :-)
>
I made more extensive tests with my class extension problem, using only
classes, and with a quite acceptable overhead for the programmer (no
modules, no need to write row types by hand). So I'm giving the state of
my understanding in this very long mail. I hope this will be of interest
to the many of you who answered my questions and therefore contributed
to it. The following is about achieving the programming schemes
described above ; don't expect any concrete examples here, as I still
have to try that and see wether it is any usefull for programming.
Actually there are even almost no methods (especially virtual) in what
follows, but I think this doesn't change the rules too much.
First, here is a way to define two classes a and b as above (the kind of
link here is one to one association, but this is not important since the
concern is about typing).
class ['b] a = object
constraint 'b = _ #b
val b : 'b option ref = ref None
method set_b b' = b := Some b'
method get_b = match ! b with
| Some b -> b
| None -> raise Not_found
end
and ['a] b = object
constraint 'a = _ #a
val a : 'a option ref = ref None
method set_a a' = a := Some a'
method get_a = match ! a with
| Some a -> a
| None -> raise Not_found
end
So the two classes are parameterized by the type of the objects they are
associated with. Furthermore this type parameter is constrained to be an
instance of the open type of the other class, as suggested by Remi
Vanicat. This seemed very counter-intuitive to me to give an open type
to the content of a reference (which I expect to be invariant). But it
works with that, and not with the close type (the definition works, but
extension doesn't).
More generally, when defining a set of related classes, each one should
be parameterized by the classes it is direcly linked with.
A very strange thing happens when type-checking this: the constraint 'b
= _ #b is replaced with the stronger constraint 'b = 'b #a #b (and
symetrically in class b). I still don't understand why it happens, but
it is close to what we would like to express. A simpler (stronger)
constraint that one could think of is something like 'self = 'self b a
(which is not allowed) or writing unparameterized recursive classes, but
this is too strong for some usages, because then we cannot associate
objects of type a with objects of a subtype of b. So I view the
constraint 'a = 'a #a #b as meaning a similar thing except that "an
object a may see itself with a stronger type than its associated object
b sees a". So, this definition will allow subtyping of either of the two
classes, or both.
Note that instead of the above choice of parameters, one can use the
whole set of all related classes to parameterize each one. This works
too. However, parameterizing each class just by itself (which should
concentrate all parameters in a single one) leads to very different
results: first, the above "magic" constraint does not appear, even if we
declare (an adapted version of) it explicitely. Is it somewhat implied ?
Anyway, this version produces a typing bug when using the resulting
classes with coercions (bug #00004505). So, for me, the above version
seems to be the best solution at this time.
The above classes work (i.e., type-checks) as expected:
let x = new a
and y = new b
let _ =
x#set_b y;
y#set_a x;
x = x#get_b#get_a && y = y#get_a#get_b
The compatibility of the two classes is checked too at some level, but
not completely. Precisely the methods used on one side and declared on
the other side are checked to be compatible (so that we cannot forget
argumets or things like that) but the actual existence of a used method
is not checked. Conjecture: if any concrete classes declared as above
type-check, then they may be extended in such a way that the connection
of two new objects will type-check). However, there is a way to enforce
the existence of used methods, for example with the following code:
let _check _ : 'b a * ('b a b as 'b) = new a, new b
This is similar to the ckecks suggested by Jacques Garrigue and Julien
Signoles (except that no functors are involved). This works only for
concrete classes, and this is not ideal since this only appears as a
challenge and not in the types of the classes themselves, but this is a
good start. Note that the onstraint 'a = 'self a would work if the two
classes were not recursive. However, the obtained classes could not be
extended. With mutually recursive classes, we get a "Self type cannot
escape its class" error.
Things like the following may also work (but I'm not sure wether it is
equivalent). Extension of a' and b' is not possible then.
class virtual ['b] a' = object (self : 'self)
inherit ['b] a
constraint 'b = 'self b
end
class virtual ['a] b' = object (self : 'self)
inherit ['a] b
constraint 'a = 'self a
end
The following code extends a and b "simultaneously", in the sense that
a2 knows that the object it would get by calling self#get_b is of type
b2, and thus has a compatible method bar.
class ['b] a2 = object
constraint 'b = _ #b2
inherit ['b] a
method foo = ()
end
and ['a] b2 = object
constraint 'a = _ #a2
inherit ['a] b
method bar = ()
end
The constraints are re-stated in terms of the new classes in order to
check the compatibility of new methods. Here is an example of error that
would have been detected:
method bar = self#get_a#foo ()
^^^^^^^^^^^^^^
This expression is not a function, it cannot be applied
We can also repeat the second check:
let _check _ : 'b a2 * ('b a2 b2 as 'b) = new a2, new b2
The two new classes may be used without trouble, as for the first two
ones. Another extension we can do is to make several subclasses of one
of our classes, say b:
class ['a] b' = object
inherit ['a] b
method foo = ()
end
and ['a] b'' = object
inherit ['a] b
method bar = ()
end
This time, we don't add any new constraint to class a, since it must
still accept b objects of class b, as we want to be able to connect
objects of classes both b' and b''. Here is an example:
let x = new a
and y1 = new b'
and y2 = new b''
let _ =
x#set_b (y1 :> _ b);
y1#set_a x;
x#set_b (y2 :> _ b);
y2#set_a x
let _ =
x = x#get_b#get_a && (y2 :> _ b) = y2#get_a#get_b
Of course we don't even need to define classes for that. We could have
used immediate objects. Back to extension, the following is an example
where a and b are the same class:
class ['c] c = object
inherit ['c] b
inherit ['c] a
end
Here is an example of use with subtyping:
let x = object inherit [_] c method foo = () end
and y = object inherit [_] c method bar = () end
let _ =
x#set_b (y :> _ c);
x#set_a (y :> _ c);
y#set_b (x :> _ c);
y#set_a (x :> _ c)
let _ =
(y :> _ c) = x#get_a
&& (y :> _ c) = x#get_b
&& (x :> _ c) = y#get_a
&& (x :> _ c) = y#get_b
let _ =
(x :> _ c) <> (y :> _ c)
Rather that closing the association link on itself we may also combine
two instances of it (this would also work for two differents pairs of
classes):
class ['j] i = object
inherit ['j] a
constraint 'j = (_, _) #j
end
and ['i, 'k] j = object
inherit ['i] b
inherit ['k] a
constraint 'i = _ #i
constraint 'k = _ #k
end
and ['j] k = object
inherit ['j] b
constraint 'j = (_, _) #j
end
let _check _ : 'j i * (('j i, 'j k) j as 'j) * 'j k = new i, new j, new k
This time, the check looks more complex. And finally, the complete
example described at the top:
class ['b] a = object
constraint 'b = 'b #a #b
val b : 'b option ref = ref None
method set_b b' = b := Some b'
method get_b = match ! b with
| Some b -> b
| None -> raise Not_found
end
and virtual ['a] b = object
constraint 'a = 'a #b #a
val a : 'a option ref = ref None
method set_a a' = a := Some a'
method get_a = match ! a with
| Some a -> a
| None -> raise Not_found
method virtual foo : _
end
(* The following cannot be checked, because b is virtual
let _check _ : 'b a * ('b a b as 'b) = new a, new b
*)
class ['d] c = object
constraint 'd = _ #d
val d : 'd option ref = ref None
method set_d d' = d := Some d'
method get_d = match ! d with
| Some d -> d
| None -> raise Not_found
method foo = ()
end
and ['c] d = object
constraint 'c = _ #c
val c : 'c option ref = ref None
method set_c c' = c := Some c'
method get_c = match ! c with
| Some c -> c
| None -> raise Not_found
end
let _check _ : 'd c * ('d c d as 'd) = new c, new d
class ['f] e = object
constraint 'f = _ #f
val f : 'f option ref = ref None
method set_f f' = f := Some f'
method get_f = match ! f with
| Some f -> f
| None -> raise Not_found
end
and ['e] f = object
constraint 'e = _ #e
val e : 'e option ref = ref None
method set_e e' = e := Some e'
method get_e = match ! e with
| Some e -> e
| None -> raise Not_found
end
let _check _ : 'f e * ('f e f as 'f) = new e, new f
class ['y] x = object
inherit ['y] a
constraint 'y = (_, _) #y
end
and ['x, 'z] y = object
inherit ['x] b
inherit ['z] c
constraint 'x = _ #x
constraint 'z = _ #z
end
and ['y] z = object
inherit ['y] d
constraint 'y = (_, _) #y
end
and ['y, 'z2] z1 = object
inherit ['y] z
inherit ['z2] e
end
and ['y, 'z1] z2 = object
inherit ['y] z
inherit ['z1] f
end
let _check _ : 'y x * (('y x, 'y z) y as 'y) * ('y, 'z2) z1 * (('y, ('y,
'z2) z1) z2 as 'z2) = new x, new y, new z1, new z2
let x = new x
and y = new y
and z1 = new z1
and z2 = new z2
let _ =
x#set_b y;
y#set_a x;
y#set_d (z1 :> _ z);
z1#set_c y;
y#set_d (z2 :> _ z);
z2#set_c y;
z1#set_f z2;
z2#set_e z1
Here, the checks are becomming hard to write however. Note that both the
type and implementation of foo were unspecified in b, and this was later
fixed by inheriting from c.
In a real use, each of the successive extensions would probably go in a
dedicated file. I hope that this will allow me to modularize my code
some day :-).
Tiphaine Turpin
[-- Attachment #2: Type: text/html, Size: 13911 bytes --]
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [Caml-list] OO programming
2008-02-28 8:34 ` Keiko Nakata
2008-02-28 13:30 ` Andrej Bauer
@ 2008-02-29 14:35 ` Tiphaine.Turpin
2008-02-29 15:58 ` Keiko Nakata
1 sibling, 1 reply; 32+ messages in thread
From: Tiphaine.Turpin @ 2008-02-29 14:35 UTC (permalink / raw)
To: Keiko Nakata; +Cc: garrigue, caml-list
Keiko Nakata a écrit :
> Hello,
>
>
>>> As for extension, I'm fully satisfied. But the verbosity level is
>>> annoying for scalability...
>>>
>> Well, yes, that's always the problem with functors...
>>
>
> Since there are some people (including me)
> who are interested in using functors and recursive modules
> in the style of object-oriented context,
>
Do you mean including classes in functors, as Jacques Garrigue
described, or using modules like classes ?
> I thought that it could be useful to devise
> a (camlp4) syntax extension which mitigates this a bit painful verbosity.
camlp4 extensions may help. I already used some for objects (related to
initializers), and I plan to investigate it further, possibly borrowing
code from Jacques Garrigue. In the context of functors, the problem is
that a lot of code would probably remain specific and still need to be
written by hand, for example, the row types for classes...
Tiphaine Turpin
>
>
> At the moment, I have no idea which syntax is general enough and intuitive for us,
> but as far as I understand we always follow similar encodings.
>
> With best regards,
> Keiko
>
> _______________________________________________
> 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
>
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [Caml-list] OO programming
2008-02-29 14:35 ` Tiphaine.Turpin
@ 2008-02-29 15:58 ` Keiko Nakata
2008-03-03 9:40 ` Tiphaine.Turpin
0 siblings, 1 reply; 32+ messages in thread
From: Keiko Nakata @ 2008-02-29 15:58 UTC (permalink / raw)
To: caml-list
> > Since there are some people (including me)
> > who are interested in using functors and recursive modules
> > in the style of object-oriented context,
> >
> Do you mean including classes in functors, as Jacques Garrigue
> described, or using modules like classes ?
The former; to include classes in functors.
> > I thought that it could be useful to devise
> > a (camlp4) syntax extension which mitigates this a bit painful verbosity.
> camlp4 extensions may help. I already used some for objects (related to
> initializers), and I plan to investigate it further, possibly borrowing
> code from Jacques Garrigue. In the context of functors, the problem is
> that a lot of code would probably remain specific and still need to be
> written by hand, for example, the row types for classes...
As I see Jacques's code, he gradually extends the module type S
to S' and S''. Type declarations in module types and type definitions
in structures involving types event, observer and subject are duplicated
everywhere with slight modifications.
Why can we not extend the previously defined module type
in a less verbose way?
We may still need to write row types by hand.
But I think we should (ideally) do it in a extensible way without duplications.
With best regards,
Keiko
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [Caml-list] OO programming
2008-02-29 15:58 ` Keiko Nakata
@ 2008-03-03 9:40 ` Tiphaine.Turpin
2008-03-03 10:20 ` Jacques Garrigue
2008-03-03 15:18 ` Keiko Nakata
0 siblings, 2 replies; 32+ messages in thread
From: Tiphaine.Turpin @ 2008-03-03 9:40 UTC (permalink / raw)
To: Keiko Nakata; +Cc: caml-list
Keiko Nakata a écrit :
>>> Since there are some people (including me)
>>> who are interested in using functors and recursive modules
>>> in the style of object-oriented context,
>>>
>>>
>> Do you mean including classes in functors, as Jacques Garrigue
>> described, or using modules like classes ?
>>
>
> The former; to include classes in functors.
>
>
>>> I thought that it could be useful to devise
>>> a (camlp4) syntax extension which mitigates this a bit painful verbosity.
>>>
>> camlp4 extensions may help. I already used some for objects (related to
>> initializers), and I plan to investigate it further, possibly borrowing
>> code from Jacques Garrigue. In the context of functors, the problem is
>> that a lot of code would probably remain specific and still need to be
>> written by hand, for example, the row types for classes...
>>
>
> As I see Jacques's code, he gradually extends the module type S
> to S' and S''. Type declarations in module types and type definitions
> in structures involving types event, observer and subject are duplicated
> everywhere with slight modifications.
> Why can we not extend the previously defined module type
> in a less verbose way?
>
I agree. Maybe the idea of using parametric class type definitions (in
WRichT) and defining unparameterized types afterwards (in S'') could be
helpfull, as such definitions can be extended with the "inherit
<class-type>" construct. Otherwise you would need to keep syntaxically
the successive declarations with camlp4 for future use, which is not
very handy. I don't know if there is an equivalent to inherit for
polymorphic variants: "type t' = private [> t | ...] doesn't seem to
work. And using objects to encode variants is possible, but you may be
used to more serious solutions...
Tiphaine Turpin
> We may still need to write row types by hand.
> But I think we should (ideally) do it in a extensible way without duplications.
>
> With best regards,
> Keiko
>
> _______________________________________________
> 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
>
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [Caml-list] OO programming
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
1 sibling, 1 reply; 32+ messages in thread
From: Jacques Garrigue @ 2008-03-03 10:20 UTC (permalink / raw)
To: Tiphaine.Turpin; +Cc: keiko, caml-list
From: "Tiphaine.Turpin" <Tiphaine.Turpin@free.fr>
> I don't know if there is an equivalent to inherit for
> polymorphic variants: "type t' = private [> t | ...] doesn't seem to
> work. And using objects to encode variants is possible, but you may be
> used to more serious solutions...
It's actually simple with polymorphic variants than with objects.
You just write
type t' = private [> t ]
but t has to be a non-private polymorphic variant type.
So you have to maintain a hierarchy of non-private types, to be
able to build your private types. This should be already apparent in
my code for event.
Jacques
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [Caml-list] OO programming
2008-03-03 10:20 ` Jacques Garrigue
@ 2008-03-03 10:30 ` Tiphaine.Turpin
0 siblings, 0 replies; 32+ messages in thread
From: Tiphaine.Turpin @ 2008-03-03 10:30 UTC (permalink / raw)
To: Jacques Garrigue; +Cc: caml-list
Jacques Garrigue a écrit :
> From: "Tiphaine.Turpin" <Tiphaine.Turpin@free.fr>
>
>
>> I don't know if there is an equivalent to inherit for
>> polymorphic variants: "type t' = private [> t | ...] doesn't seem to
>> work. And using objects to encode variants is possible, but you may be
>> used to more serious solutions...
>>
>
> It's actually simple with polymorphic variants than with objects.
> You just write
> type t' = private [> t ]
> but t has to be a non-private polymorphic variant type.
> So you have to maintain a hierarchy of non-private types, to be
> able to build your private types. This should be already apparent in
> my code for event.
>
Yes, indeed. So, no need for another bad hack :-).
> Jacques
>
> _______________________________________________
> 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
>
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [Caml-list] OO programming
2008-03-03 9:40 ` Tiphaine.Turpin
2008-03-03 10:20 ` Jacques Garrigue
@ 2008-03-03 15:18 ` Keiko Nakata
2008-03-03 19:25 ` Tiphaine Turpin
1 sibling, 1 reply; 32+ messages in thread
From: Keiko Nakata @ 2008-03-03 15:18 UTC (permalink / raw)
To: caml-list
> > As I see Jacques's code, he gradually extends the module type S
> > to S' and S''. Type declarations in module types and type definitions
> > in structures involving types event, observer and subject are duplicated
> > everywhere with slight modifications.
> > Why can we not extend the previously defined module type
> > in a less verbose way?
> >
> I agree. Maybe the idea of using parametric class type definitions (in
> WRichT) and defining unparameterized types afterwards (in S'') could be
> helpfull, as such definitions can be extended with the "inherit
> <class-type>" construct. Otherwise you would need to keep syntaxically
> the successive declarations with camlp4 for future use, which is not
> very handy.
Parametric class type definitions should be helpful.
We might need as many type parameters as (class) type definitions involved;
do you think this can be problematic,
particularly in respect of type error messages?
Hopefully we want to start with S and
derive its refinements by extending S bit by bit.
But here is one problem: module type declarations (e.g. S) and
structures (e.g. WindowA) are completely different entities;
it can be handy in practice if we can include S in WihdowA
and refine the included types by giving concrete representations
to abstract types (e.g. event).
Well, I know that module types and structures are
indeed completely different
from the theoretical point of view....
Here is another thing that may be worth attention.
The types observer and subject in WRichT are not recursive,
but we take their fix-point in S''.
Unfortunately we cannot do this kind of refinement
via the "with" construct.
Anyway, I think we are almost exactly following OO-programming style
in a more explicit thus more verbose way.
So I conjecture that if we come up with good syntactic sugar,
we can be as concise as OOP; as you suggest it may well be
a good idea to expand the sugar into parametric class definitions,
possibly combined with functors and private row types to increase modularity.
Best,
Keiko
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [Caml-list] OO programming
2008-03-03 15:18 ` Keiko Nakata
@ 2008-03-03 19:25 ` Tiphaine Turpin
2008-03-04 14:00 ` Keiko Nakata
0 siblings, 1 reply; 32+ messages in thread
From: Tiphaine Turpin @ 2008-03-03 19:25 UTC (permalink / raw)
To: Keiko Nakata; +Cc: caml-list
Keiko Nakata a écrit :
>>> As I see Jacques's code, he gradually extends the module type S
>>> to S' and S''. Type declarations in module types and type definitions
>>> in structures involving types event, observer and subject are duplicated
>>> everywhere with slight modifications.
>>> Why can we not extend the previously defined module type
>>> in a less verbose way?
>>>
>>>
>> I agree. Maybe the idea of using parametric class type definitions (in
>> WRichT) and defining unparameterized types afterwards (in S'') could be
>> helpfull, as such definitions can be extended with the "inherit
>> <class-type>" construct. Otherwise you would need to keep syntaxically
>> the successive declarations with camlp4 for future use, which is not
>> very handy.
>>
>
> Parametric class type definitions should be helpful.
> We might need as many type parameters as (class) type definitions involved;
> do you think this can be problematic,
> particularly in respect of type error messages?
>
Only experiments can tell us. But I suspect that using a systematic
scheme for defining classes and relating them to each other should avoid
users to make too many errors that come from a misunderstanding of the
type system ('self escaping its scoope, or unified wit a closed type,
etc.), thus allowing "advanced" use of caml objects by non type systems
experts (including me).
> Hopefully we want to start with S and
> derive its refinements by extending S bit by bit.
> But here is one problem: module type declarations (e.g. S) and
> structures (e.g. WindowA) are completely different entities;
> it can be handy in practice if we can include S in WihdowA
> and refine the included types by giving concrete representations
> to abstract types (e.g. event).
>
> Well, I know that module types and structures are
> indeed completely different
> from the theoretical point of view....
>
In this case where they are only made of (the same) types with a class
type on one side and a private row type on the other side, this
theoretical difference is far from obvious in practise, and this is one
of the reasons why such code is so hard to read (as the same thing seem
to be repeated twice). This would be nice if you could write your types
once (possibly in a very small subset of the type language) and have the
module and the module type be generated from the same code.
> Here is another thing that may be worth attention.
> The types observer and subject in WRichT are not recursive,
> but we take their fix-point in S''.
> Unfortunately we cannot do this kind of refinement
> via the "with" construct.
>
> Anyway, I think we are almost exactly following OO-programming style
> in a more explicit thus more verbose way.
> So I conjecture that if we come up with good syntactic sugar,
> we can be as concise as OOP; as you suggest it may well be
> a good idea to expand the sugar into parametric class definitions,
> possibly combined with functors and private row types to increase modularity.
>
I plan to do some reasonable scale "OOcaml" coding (in my spare time)
for some project. I will first see if I can use some systematic scheme
successfully before I try anything with camlp4. That said, some of us
tend to think of everything only from within ocaml, and I know that some
day I should give a try to other systems, like Scala and its "traits".
Tiphaine Turpin
> Best,
> Keiko
>
> _______________________________________________
> 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
>
>
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [Caml-list] OO programming
2008-03-03 19:25 ` Tiphaine Turpin
@ 2008-03-04 14:00 ` Keiko Nakata
0 siblings, 0 replies; 32+ messages in thread
From: Keiko Nakata @ 2008-03-04 14:00 UTC (permalink / raw)
To: caml-list
Hello.
> > Parametric class type definitions should be helpful.
> > We might need as many type parameters as (class) type definitions involved;
> > do you think this can be problematic,
> > particularly in respect of type error messages?
> >
> Only experiments can tell us. But I suspect that using a systematic
> scheme for defining classes and relating them to each other should avoid
> users to make too many errors that come from a misunderstanding of the
> type system ('self escaping its scoope, or unified wit a closed type,
> etc.), thus allowing "advanced" use of caml objects by non type systems
> experts (including me).
Boilerplates that help us avoid typing errors...
That sounds nice.
> I plan to do some reasonable scale "OOcaml" coding (in my spare time)
> for some project. I will first see if I can use some systematic scheme
> successfully before I try anything with camlp4.
I also look for how I can minimize in a (hopefully) intuitive way
Jacques's code, avoiding bizarre code duplication.
Please let me know when you have good news.
> That said, some of us
> tend to think of everything only from within ocaml, and I know that some
> day I should give a try to other systems, like Scala and its "traits".
I am sure you can enjoy exotic time if you try to exploit Scala's goodies :-)
In respect of the exact subject we have been discussing, Scala may be more adapted.
As far as I am concerned, fortunately(?), polymorphic variants and type inference
and other many many goodies of OCaml
keep me from turning to another language.
Best,
Keiko
^ permalink raw reply [flat|nested] 32+ messages in thread
end of thread, other threads:[~2008-03-04 14:00 UTC | newest]
Thread overview: 32+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2008-02-21 9:31 OO programming 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
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
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox