Multi-parameter OOP

Richard Uhtenwoldt greon@best.com
Sun, 28 Oct 2001 12:32:03 -0800


Ashley Yakeley:

>I think existential types are arranged so that Haskell never needs to
>store type information in them at run-time. So you'll never be able to do
>dynamic OOP with them.
>
>One possible extension to Haskell for dynamic OOP, which I never tire of
>suggesting, is the extensible datatype, for instance:
>
>    module P
>    data BaseType = B1 | B2 | _
>
>    module Q
>    data DerivedType = D1 | D2
>    data BaseType |= BD DerivedType

That rubs me the wrong way in that it invents new notions, eg, "| _" and
"|=" when I get the feeling extant notions can be reused.  Eg, why not
try to replace the equality with an implication (both equality and
implication being notions we already need to know), eg,

>data BaseType <= B1 | B2

>data BaseType <= D1 | D2 

Of course, that is just an untested idea.  My reason for posting is to
bring attention to how something similar is done in the language merd,
whose designer posts here under the name pixel, and which
is actually implemented I think.  Having just found this, I do not 
understand it, but it sure has the look, tone and feel of a good design.

merd has a notation a !< b which means that values of type "a" can be
used when expecting a value of type "b" --ie, it is a subtype.
(note the similarity to "<=".)

Haskell's data declaration makes a sum-of-products type, eg,

data Maybe a=Nothing|Just a

In contrast, merd has sum types written a|b and product types written (a,b)
and data constructors which are capitalized, eg, True, Just.  So this
next is the definition of Maybe in merd:

(Maybe,x) = Nothing | (Just,x)

(I've simplified and renamed constructors a little.)  I believe that
factoring Haskell's datatypes into constituent parts, which merd does,
is essential to a good subtyping design.  Haskell's datatypes are too
specialized for particular patterns of uses.

In addition, merd has a type called a struct, written a |&| b.
For reasons I do not understand, instead of defining, eg,

point = (double,double)

or 

point = (Point,double,double)

pixel prefers to define

point   = (X,double) |&| (Y,double)

That last way is the standard way of declaring a record type, says
pixel. 

The fact that none of the types introduced so far
can be built from the other 3 types is made clear by the rules
for subtypes, some of which are

(a0, ..., an)   !< (b0, ..., bn)   <=> ai !< bi  (forall i)
a !< a | b
a |&| b !< a
i !< a and i !< b  <=>  i !< a |&| b
i !< a  or i !< b  <=>  i !< a  |  b
i !> a and i !> b  <=>  i !> a  |  b
i !> a  or i !> b  <=>  i !> a |&| b
i !< a | b|&|c  <=>  i !< a|b |&| a|c
i !> a |&| b|c  <=>  i !> a|&|b | a|&|c

i !> i  |  a  =>  i !> a
i !< i  |  a  =>  i !> a    
i !< i |&| a  =>  i !< a
i !> i |&| a  =>  true

i !> i|&|a | b  =>  i !> b
i !< i|a |&| b  =>  i !> a and i !< b

Oh, yeah, there is a function type of course --which obeys

a|c->b !< a->b !< a->b|d
A->B !> x->x <=> (exists x. A !< x and B !> x)

The words "covariant" and "contravariant" are mentioned, whatever they are.

More at 

http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/~checkout~/merd/
merd/subtyping.me?content-type=text/plain

and 

http://merd.sourceforge.net/types.png