* [Caml-list] examples of heterogenous collections (containers ?) @ 2004-03-31 4:55 briand 2004-03-31 5:41 ` Matt Gushee 0 siblings, 1 reply; 8+ messages in thread From: briand @ 2004-03-31 4:55 UTC (permalink / raw) To: caml-list I'm embarking on that most typical of ewexamples, the heterogenous list. So I have objects: base_class virtual method derived_class_A inherit base_class derived_class_B inherit base_class ... And I would like to collect instances of derived_class_B, derived_class_B etc.. into a data structure. The obvious example is that I'd like to collect the objects into a list and do something like : List.iter (f obj -> obj#method) list_of_objs or is (obj :> base_class)#method ? I've done quite a bit of searching but I cannot seem to find a "clean" example of doing the above, just bits and pieces. Can anyone point me to any examples or code ? Thanks Brian ------------------- To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/ Beginner's list: http://groups.yahoo.com/group/ocaml_beginners ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [Caml-list] examples of heterogenous collections (containers ?) 2004-03-31 4:55 [Caml-list] examples of heterogenous collections (containers ?) briand @ 2004-03-31 5:41 ` Matt Gushee 2004-03-31 6:45 ` briand 2004-03-31 7:28 ` Martin Jambon 0 siblings, 2 replies; 8+ messages in thread From: Matt Gushee @ 2004-03-31 5:41 UTC (permalink / raw) To: caml-list On Tue, Mar 30, 2004 at 08:55:36PM -0800, briand@aracnet.com wrote: > > I'm embarking on that most typical of ewexamples, the heterogenous list. > > So I have objects: > > base_class > virtual method > > derived_class_A > inherit base_class > > derived_class_B > inherit base_class > > ... > > And I would like to collect instances of derived_class_B, > derived_class_B etc.. into a data structure. > > The obvious example is that I'd like to collect the objects into a > list and do something like : > > List.iter > (f obj -> > obj#method) > list_of_objs This will work if all the classes have identical signatures. Of course, that's not the case for most non-trivial examples. > or is (obj :> base_class)#method ? Probably not. First of all, you will need to cast the objects at the point where you put them into a list together ... thus, at the point where you do the method call, the cast has already been done. Second, you don't necessarily want to cast them to the base class. As some people never tire of pointing out ;-), inheritance is not subtyping. Actually, I haven't myself figured out all the implications of that statement, nor learned the theoretical justification for it--but at any rate, in OCaml class types are completely independent of inheritance relationships. That's a good thing because it means you can define a class type and arbitrarily apply it to any class that provides *at least* the methods specified in the type signature, regardless of inheritance. E.g.: class type x = object method a : unit method b : int -> bool end ... and so on. > I've done quite a bit of searching but I cannot seem to find a "clean" > example of doing the above, just bits and pieces. Can anyone point me > to any examples or code ? Hmm ... I recently wrote a text editor that uses a lot of class casts in order to be able to manipulate groups of GUI objects that are instances of different classes. I'm not sure it's a great learning example, though. It works, but its code is hardly clean ... in fact it's easily the ugliest thing I've done in OCaml. But if you can't find any better examples, I could send you a tarball of it (it's not released yet). -- Matt Gushee When a nation follows the Way, Englewood, Colorado, USA Horses bear manure through mgushee@havenrock.com its fields; http://www.havenrock.com/ When a nation ignores the Way, Horses bear soldiers through its streets. --Lao Tzu (Peter Merel, trans.) ------------------- To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/ Beginner's list: http://groups.yahoo.com/group/ocaml_beginners ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [Caml-list] examples of heterogenous collections (containers ?) 2004-03-31 5:41 ` Matt Gushee @ 2004-03-31 6:45 ` briand 2004-03-31 9:04 ` skaller 2004-03-31 7:28 ` Martin Jambon 1 sibling, 1 reply; 8+ messages in thread From: briand @ 2004-03-31 6:45 UTC (permalink / raw) To: Matt Gushee; +Cc: caml-list >>>>> "Matt" == Matt Gushee <mgushee@havenrock.com> writes: Matt> This will work if all the classes have identical signatures. Of course, Matt> that's not the case for most non-trivial examples. The classes have identical signatures, i.e. all have exactly the same methods and variables ?? Matt> Probably not. First of all, you will need to cast the objects at the Matt> point where you put them into a list together ... thus, at the point Matt> where you do the method call, the cast has already been done. Second, Matt> you don't necessarily want to cast them to the base class. Well that makes sense, if I can figure out the part about all the classes havin g the same signature. Matt> As some people never tire of pointing out ;-), inheritance is Matt> not subtyping. Actually, I haven't myself figured out all the Matt> implications of that statement, nor learned the theoretical Yes, I've been seeing that, and amazingly it's actually starting to sink in. There are some notes from the ocaml page which discuss this relatively well : Didier Remy's APPSEM course notes on Objective Caml. Matt> justification for it--but at any rate, in OCaml class types Matt> are completely independent of inheritance Matt> relationships. That's a good thing because it means you can Matt> define a class type and arbitrarily apply it to any class that Matt> provides *at least* the methods specified in the type Matt> signature, regardless of inheritance. E.g.: Matt> class type x = Matt> object Matt> method a : unit Matt> method b : int -> bool Matt> end Matt> ... and so on. ??? So it's example time : class virtual base = ... end class A1 = object(self) inherit base method x1 = ... method x2 = ... end class A2 = object(self) inherit base method x1 = ... method x2 = ... method x3 = ... end So I should be able to do : let a = new A1 in let b = new A2 in (a :> base) :: (b :> base) :: [] ;; right ? now my list has type "base list". So I immediately see the problem here. My iter code could try to invoke method x3 on the A1 object in the list as I'm iterating through. oops. So it seems to me that I need a "stronger" method to enforce safety, perhaps implementing x1 as a virtual method in base ? Brian ------------------- To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/ Beginner's list: http://groups.yahoo.com/group/ocaml_beginners ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [Caml-list] examples of heterogenous collections (containers ?) 2004-03-31 6:45 ` briand @ 2004-03-31 9:04 ` skaller 2004-04-01 4:00 ` briand 0 siblings, 1 reply; 8+ messages in thread From: skaller @ 2004-03-31 9:04 UTC (permalink / raw) To: briand; +Cc: Matt Gushee, caml-list On Wed, 2004-03-31 at 16:45, briand@aracnet.com wrote: > So it seems to me that I need a "stronger" method to enforce safety, > perhaps implementing x1 as a virtual method in base ? You are thinking about this problem *backwards*! :) Forget about how to declare the classes etc. Focus on the list. You would like a list of some type "elt" for which you can write: List.iter (fun x -> x#f) the_list; Now define the type which 'the_list' must have. Define the 'minimal' such type! class type elt = object method f : unit end val the_list : elt list That's it. All you need to do now is cast ANY object to type "elt" and it will fit into your list. The cast will always work if the object has a method named 'f' which when invoked with no arguments returns unit. Obviously, because Ocaml is wonderful, your cast will fail on objects without a suitable 'f' method. The KEY idea here is: the class types you need for algorithms should be declared. But forget the idea classes have types. They do, but it isn't important. A class is just a function which creates an object. Class types are *views* of objects. The class type of a class is the 'maximal' view of an object which it constructs, and isn't really that important, in fact, it should probably never be exposed in any interfaces because the full class type will include data members due to a problem needing to allocate space for them when constructing an object. The solution is to use a factory function in a single module, and return an abstraction of the class type you wish to deal with. -- John Skaller, mailto:skaller@users.sf.net voice: 061-2-9660-0850, snail: PO BOX 401 Glebe NSW 2037 Australia Checkout the Felix programming language http://felix.sf.net ------------------- To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/ Beginner's list: http://groups.yahoo.com/group/ocaml_beginners ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [Caml-list] examples of heterogenous collections (containers ?) 2004-03-31 9:04 ` skaller @ 2004-04-01 4:00 ` briand 2004-04-01 5:38 ` Issac Trotts 2004-04-01 7:20 ` skaller 0 siblings, 2 replies; 8+ messages in thread From: briand @ 2004-04-01 4:00 UTC (permalink / raw) To: skaller; +Cc: Matt Gushee, caml-list >>>>> "skaller" == skaller <skaller@users.sourceforge.net> writes: skaller> You are thinking about this problem *backwards*! :) Well that's why it's giving me so much trouble. :-) skaller> The cast will always work if the object has a method named skaller> 'f' which when invoked with no arguments returns unit. skaller> Obviously, because Ocaml is wonderful, your cast will fail skaller> on objects without a suitable 'f' method. I LIKE IT. No inheritance required. Objects which have the same method with the same signature work "automagically". skaller> The solution is to use a factory function in a single skaller> module, and return an abstraction of the class type you skaller> wish to deal with. I'm not sure I followed that, can you expand ? For future readers of the list here and appropriate example. It turns out I was silly and never even bothered to look in the _manual_ of all places. I looked everywhere else. This is covered in the section called "Using Coercions", go figure. Anyway, enough blather, here's the example : class elt = object method f x = 2 * x end ;; class elt_A = object method f x = 3 *x method g x = 3. *. x end ;; let the_list = [ new elt; new elt ; ((new elt_A) :> elt) ] ;; List.iter (fun x -> print_int (x#f 2);) the_list ;; It does the right thing. Here is a nice example to show type safety. Re-define elt_A as: class elt_A = object method f x = 3. *. x method g x = 3. *. x end And get the error : This expression cannot be coerced to type elt = < f : int -> int >; it has type elt_A = < f : float -> float; g : float -> float > but is here used with type #elt as 'a = < f : int -> int; .. > Thanks to all who answered. Brian ------------------- To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/ Beginner's list: http://groups.yahoo.com/group/ocaml_beginners ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [Caml-list] examples of heterogenous collections (containers ?) 2004-04-01 4:00 ` briand @ 2004-04-01 5:38 ` Issac Trotts 2004-04-01 7:20 ` skaller 1 sibling, 0 replies; 8+ messages in thread From: Issac Trotts @ 2004-04-01 5:38 UTC (permalink / raw) To: caml-list On Wed, Mar 31, 2004 at 08:00:07PM -0800, briand@aracnet.com wrote: > let the_list = [ new elt; new elt ; ((new elt_A) :> elt) ] I wish I understood why this cast is necessary. It seems like OCaml should be able to construct a class type whose method signatures are the ones common to all objects in the list. -- Issac Trotts ------------------- To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/ Beginner's list: http://groups.yahoo.com/group/ocaml_beginners ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [Caml-list] examples of heterogenous collections (containers ?) 2004-04-01 4:00 ` briand 2004-04-01 5:38 ` Issac Trotts @ 2004-04-01 7:20 ` skaller 1 sibling, 0 replies; 8+ messages in thread From: skaller @ 2004-04-01 7:20 UTC (permalink / raw) To: briand; +Cc: skaller, Matt Gushee, caml-list On Thu, 2004-04-01 at 14:00, briand@aracnet.com wrote: > >>>>> "skaller" == skaller <skaller@users.sourceforge.net> writes: > > skaller> You are thinking about this problem *backwards*! :) > > Well that's why it's giving me so much trouble. :-) :-) > I LIKE IT. No inheritance required. you can write (inside a class definition): inherit X; which is just a safe form of 'macro' that pulls in the methods of X to save you keyboarding. In the OO world this is known as 'implementation inheritance'. > skaller> The solution is to use a factory function in a single > skaller> module, and return an abstraction of the class type you > skaller> wish to deal with. > > I'm not sure I followed that, can you expand ? Yeah, I'll explain below .. > Anyway, enough blather, here's the example : > > class elt = object > method f x = 2 * x > end > ;; > > class elt_A = object > method f x = 3 *x > method g x = 3. *. x > end > ;; > > let the_list = [ new elt; new elt ; ((new elt_A) :> elt) ] > ;; Right, but this example glosses over an important distinction. In Ocaml .. and in all class based OO languages for that matter .. a class is NOT a type, its a function which constructs an object. In OO languages like C++ and Java, there is an associated type with the same name as the constructor. In Ocaml, there is ALSO an associated type .. BUT you can make class types independently. In C++ one uses an 'abstract class' for this purpose, in Java it is called an interface. In Ocaml, its called a 'class type'. For example: class type elt_t = object method f: int -> int end ;; Here, elt_t is a class type, notice the declaration is for 'class type ...', and that only the signature of the method f is provided, and no implementation. When you write: class elt_A = object method f x = 3 * x end ;; you are defining two things: a function to make an object, and also a type alias 'elt_A'. It is poor practice to use mix up the class type and the constructor. To ensure it is impossible to do this consider a factory function: let make_A () : elt_t = class elt_A object method f x = 3 * x end; (new elt_A :> elt_t) ;; The idea is to completely isolate the implementation of the constructors of objects from their types, because in Ocaml the two things are separable. In practice, what you do is: (1) Put the class type specification in the file elt.mli. (2) In elt_A.mli, you put the signature: open Elt val make_A: unit -> elt_A (3) In elt_A.ml put the class declaration and the factory function: open Elt class elt_A = object method f x = 3 * x end let make_A () : elt_t =(new elt_A :> elt_t) ;; Note that the type of the class 'elt_A' is invisible to the outside world because the signature is not present in the elt_A.mli file. All we know is that there is a constructor named 'make_A' which can make some kind of elt. The effect of all this is to completely separate the way you think about the type of object required to make your algorithms and functions work, from the details of implementing the objects carrying the information; that is: you make the algorithms representation independent, and you can write and type check them before thinking about implementing one or more objects. Note now your example works, but it is confusing because elt and elt_A are BOTH class types and class constructor functions. In the coercion: new elt_A :> elt_t which I wrote above it is manifest that the type of the object on the LHS is irrelevant, except that it is a subtype of elt_t. All the algorithms work with the abstracted supertype elt_t: the actual type elt_A of the object is hidden. There is a 'golden rule' in OO that your subtyping lattices should be expressesed independently of you inheritance lattices. In Ocaml, you can do this explicitly and should. In particular, inheritance is clearly just a hack to save keyboarding: your inheritance lattice is an implementation detail. But much more surprising, the subtyping lattice is IMPLICIT. It is not explicitly based on names, but entirely based on signatures. For example: class type b = object method f:unit end class type d1 = object method f: unit method g: unit end class type d2 = object method f : unit method h : unit end class type d = object method f : unit method g : unit method h : unit end and now we have d1 :> b d2 :> b d :> b1 d :> b2 d :> b where I use :> to mean 'is subtype of'. [FEATURE REQUEST: there is no way to assert a subtyping relation in an interface file. It would be nice to check this, with something like: assert d1 :> b; ] It is clear that the other kind of 'OO' in C++ and Java is bogus. It doesn't work properly. Because the typing is connected to inheritance, the programmer is totally confused by the distinction between subtyping and specification. Worse though, subtyping relations are INVASIVE in C++ and Java. You have to declare a class d is derived from b1 in order to use d 'as a b1'. So when you write an algorithm which requires a new kind of type b2, your d which already has the right signature still cannot be used 'as a b2', you have to go and invade the definition, breaking the open/closed principle. In Ocaml, you don't. If your d already has the right signature is can be used immediately 'as a b2'. You can create new abstractions that apply to existing objects 'after the fact' and it all works. Once you use a properly constructed system you're going to hate the idea of going back to using Java or C++: Ocaml is not perfect, but Java and C++ are just plain wrong :D BTW: it is possible to use a dummy method to 'state' an invariant. For example, there is an intended difference between class type pair = object method x : int method y : int end class type gauss = object method x: int method y: int end class type rational = object method x: int method y: int end but there is no sugar for stating it: but we really need to prevent coercing a pair into a rational! To solve this problem, you can use a dummy method. For example to the rational type you add the method: method rational_invariant : unit which you can implement to check the invariant, or if you're lazy just return (). The method name is used as a generative tag to prevent a pair being cast to a rational (but you can always forget the invariant and cast the rational to a pair). I wonder if there is a better way to encode this idea? -- John Skaller, mailto:skaller@users.sf.net voice: 061-2-9660-0850, snail: PO BOX 401 Glebe NSW 2037 Australia Checkout the Felix programming language http://felix.sf.net ------------------- To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/ Beginner's list: http://groups.yahoo.com/group/ocaml_beginners ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [Caml-list] examples of heterogenous collections (containers ?) 2004-03-31 5:41 ` Matt Gushee 2004-03-31 6:45 ` briand @ 2004-03-31 7:28 ` Martin Jambon 1 sibling, 0 replies; 8+ messages in thread From: Martin Jambon @ 2004-03-31 7:28 UTC (permalink / raw) To: Matt Gushee; +Cc: caml-list On Tue, 30 Mar 2004, Matt Gushee wrote: > On Tue, Mar 30, 2004 at 08:55:36PM -0800, briand@aracnet.com wrote: > > > > I'm embarking on that most typical of ewexamples, the heterogenous list. > > > > So I have objects: > > > > base_class > > virtual method > > > > derived_class_A > > inherit base_class > > > > derived_class_B > > inherit base_class > > > > ... > > > > And I would like to collect instances of derived_class_B, > > derived_class_B etc.. into a data structure. > > > > The obvious example is that I'd like to collect the objects into a > > list and do something like : > > > > List.iter > > (f obj -> > > obj#method) > > list_of_objs > > This will work if all the classes have identical signatures. Of course, > that's not the case for most non-trivial examples. > > > or is (obj :> base_class)#method ? > > Probably not. First of all, you will need to cast the objects at the > point where you put them into a list together ... thus, at the point > where you do the method call, the cast has already been done. And to keep trace of the original object, we would use polymorphic variants: class virtual ['a] base_class = object method virtual variant : 'a end class ['a] derived_class_A = object (self) inherit ['a] base_class method variant = `A self end and ['a] derived_class_B = object (self) inherit ['a] base_class method variant = `B self end type variant = [ `A of variant derived_class_A | `B of variant derived_class_B ] let a = new derived_class_A let b = new derived_class_B let l = [ (a :> variant base_class); (b :> variant base_class) ] let list_of_variants = List.map (fun o -> o#variant) l But maybe it is more convenient to not define a variant method, just build a list of specialized objects: let list_of_variants = [ `A obj1; `B obj2; `C anything; ... ] and then: let l = List.map (function `A o | `B o | `C o -> (o :> base_class)) (but here you loose the reference to the specialized version) Martin ------------------- To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/ Beginner's list: http://groups.yahoo.com/group/ocaml_beginners ^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2004-04-01 12:25 UTC | newest] Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2004-03-31 4:55 [Caml-list] examples of heterogenous collections (containers ?) briand 2004-03-31 5:41 ` Matt Gushee 2004-03-31 6:45 ` briand 2004-03-31 9:04 ` skaller 2004-04-01 4:00 ` briand 2004-04-01 5:38 ` Issac Trotts 2004-04-01 7:20 ` skaller 2004-03-31 7:28 ` Martin Jambon
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox