From: skaller <skaller@users.sourceforge.net>
To: briand@aracnet.com
Cc: skaller@users.sourceforge.net,
Matt Gushee <mgushee@havenrock.com>,
caml-list@pauillac.inria.fr
Subject: Re: [Caml-list] examples of heterogenous collections (containers ?)
Date: 01 Apr 2004 17:20:26 +1000 [thread overview]
Message-ID: <1080804025.13854.225.camel@pelican> (raw)
In-Reply-To: <16491.37831.445677.611426@soggy.deldotd.com>
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
next prev parent reply other threads:[~2004-04-01 7:20 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2004-03-31 4:55 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 [this message]
2004-03-31 7:28 ` Martin Jambon
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1080804025.13854.225.camel@pelican \
--to=skaller@users.sourceforge.net \
--cc=briand@aracnet.com \
--cc=caml-list@pauillac.inria.fr \
--cc=mgushee@havenrock.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox