* 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 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: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 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 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 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-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-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 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-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
* 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
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