From mboxrd@z Thu Jan 1 00:00:00 1970 Received: (from majordomo@localhost) by pauillac.inria.fr (8.7.6/8.7.3) id JAA14169; Thu, 1 Apr 2004 09:20:41 +0200 (MET DST) X-Authentication-Warning: pauillac.inria.fr: majordomo set sender to owner-caml-list@pauillac.inria.fr using -f Received: from concorde.inria.fr (concorde.inria.fr [192.93.2.39]) by pauillac.inria.fr (8.7.6/8.7.3) with ESMTP id JAA13878 for ; Thu, 1 Apr 2004 09:20:39 +0200 (MET DST) Received: from smtp1.adl2.internode.on.net (smtp1.adl2.internode.on.net [203.16.214.181]) by concorde.inria.fr (8.12.10/8.12.10) with ESMTP id i317KZYM000443 for ; Thu, 1 Apr 2004 09:20:37 +0200 Received: from [192.168.1.200] (ppp113-158.lns1.syd3.internode.on.net [150.101.113.158]) by smtp1.adl2.internode.on.net (8.12.9/8.12.9) with ESMTP id i317KQwn012401; Thu, 1 Apr 2004 16:50:28 +0930 (CST) Subject: Re: [Caml-list] examples of heterogenous collections (containers ?) From: skaller Reply-To: skaller@users.sourceforge.net To: briand@aracnet.com Cc: skaller@users.sourceforge.net, Matt Gushee , caml-list@pauillac.inria.fr In-Reply-To: <16491.37831.445677.611426@soggy.deldotd.com> References: <16490.20296.852406.613011@soggy.deldotd.com> <20040331054113.GC19538@swordfish> <16490.26870.331855.463270@soggy.deldotd.com> <1080723895.13269.143.camel@pelican.wigram> <16491.37831.445677.611426@soggy.deldotd.com> Content-Type: text/plain Message-Id: <1080804025.13854.225.camel@pelican> Mime-Version: 1.0 X-Mailer: Ximian Evolution 1.2.2 (1.2.2-4) Date: 01 Apr 2004 17:20:26 +1000 Content-Transfer-Encoding: 7bit X-Miltered: at concorde by Joe's j-chkmail ("http://j-chkmail.ensmp.fr")! X-Loop: caml-list@inria.fr X-Spam: no; 0.00; caml-list:01 sourceforge:01 2004:99 00,:99 sourceforge:01 abstraction:01 elt:01 elt:01 mli:01 mli:01 val:01 'make:01 abstracted:01 supertype:01 subtyping:01 Sender: owner-caml-list@pauillac.inria.fr Precedence: bulk X-Status: X-Keywords: X-UID: 6 On Thu, 2004-04-01 at 14:00, briand@aracnet.com wrote: > >>>>> "skaller" == skaller 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