[Haskell] PROPOSAL: class aliases

John Meacham john at repetae.net
Thu Oct 13 08:51:36 EDT 2005


On Thu, Oct 13, 2005 at 12:21:41PM +0100, Simon Peyton-Jones wrote:
> | This is a proposal for a language extension which will hopefully
> mitigate the
> | issues holding back evolution of the standard prelude as well as
> provide
> | useful class abstraction capabilities in general.
> 
> A short summary would be "type synonyms for class constraints".   You'd
> definitely want the syntax to look as much like a type synonym decl as
> possible.

> I've considered this before, but never done anything about it because
> superclasses are so close.  Specifically, what is the difference between

Actually I think it is pretty orthogonal to superclasses, class aliases
are about composing classes, not building hierarchies.


> 
> (i)	class (C a, D a) => CD a
> and
> (ii)	class alias CD a = (C a, D a)
> 
> Note that (i) is Haskell 98.
> 
> * In both cases one can write
> 	f :: (CD a) => ...
>    instead of the more voluminous
> 	f :: (C a, D a)
> 
> * However with (i), for each type T one must write
> 	instance C T where { ...meths for C... }
> 	instance D T where { ...meths for D... }
> 	instance CD T where {}
> 
> whereas with (ii) one can write
> 	instance CD T where { ...meths for C...
> 				  ...meths for D... }
> 
> I believe that this latter is the sole difference.  Am I right?

No, there are a number of differences that allow class aliases to be
used for true class abstraction rather than just a shortcut to writing
instances.

> If so, than rather than invent a whole new mechanism, why not simply
> extend the existing superclass mechanism to allow a single instance decl
> to declare instances for several classes?  For example, one add to
> Haskell 98 the following:
> 	an instance declaration for a class CD with superclasses C and D
> may 
> 	give the instances for its superclasses C and D

this does not actually solve the problems mentioned in the proposal.

in particular,

(CD a) => a and (C a,D a) => a are distinct types. this means that
you cannot use the aliases as abreviations or alternate names, which is
a very nice side effect. with fine grained class hierarchies, type
signatures get big fast. having a shorthand is very nice.

but worse, it ruins the symmetry. declaring an instance for CD a will
create instances for (C a,D a) but declaring instances  for C a and D a
will not create one for CD. A key point of my design is that you can
declare instances in the new Num hierarchy, or in the haskell 98 one, and
the instances will be propagated both directions. things get much more
complicated when you realize that you might want more than just 2 views
of the same hierarchy and there is not a clear order among them. if you
constantly have to remember to declare instances for the old haskell 98
classes too then there is really no benefit.

Another illustrative example is one that combines aliases with
superclasses.

class alias Num a = Show a => (Additive a, Multiplicative a)

now, Show is a superclass, but Num is an alias for Additive and
Multiplicative. 

if we declare something an instance of Num, we are declaring instances
for precisely Additive and Multiplicative. but not Show, there must already be an
existing instance for Show since it is a superclass and not part of the
alias, if this distinction were not made then several bad things happen:

it is obvious you cannot emulate the old haskell 98 behavior and thus
cannot get true abstraction.

declaring an instance for Num where you left out 'show' would rather
than give an error as it should, use the default method for show (which
is undefined). this is definitly what you don't want for Show, but it
might be what you want for an alternate class with a useful default.

with the superclass method you mentioned, how do we control exactly
which classes we are creating instances for? all the way up the
hierarchy back to the base? just one level? neither rules gives us what
we want and if we put that explicitly in the instance declaration we
ruin the whole point of class abstraction. (plus, it seems like the
wrong place to put it anyway). An instance for a class alias always and
exactly declares instances for each of its components and nothing else
and is orthogonal to the superclass hierarchy.


Another key way in which it is different is that it is truely a
composition of classes rather than a ordering on them. with a
superclass relationship, classes are forced to build on top of one
another, you cannot have mutual recursion between class default
methods..

for instance, consider this useful little alias:

class alias EqOrd a = (Eq a, Ord a) where
        a == b = compare a b == EQ

now you can declare something as an EqOrd and just provide a 'compare'
method and it will derive everything else including the Eq methods.

notice that the default method is declared the wrong way in the class
hierachy. this is a very handy thing, but is actually necessary to
create the abstraction benefits we want. if we look at the Num example
from my previous proposal:


> class (Addititive a, AdditiveNegation a,
>        Multiplicative a, FromInteger a) => Num a where
>    one = fromInteger 1
>    zero = fromInteger 0
>    negate x = zero - x

notice that one and zero are given definitons in terms of fromInteger,
if these defaulting methods could not be done, then a standard haskell
98 instance for Num could not create proper instances for Additive and
Multiplicative. we would basically be forced to only extend the class
hierarchy by creating subsets of the current hierarchy, we could not
add functions at the 'base' and expect them to get defined properly. 


Also, it should be noted that while I am using the Num hierarchy as an
example, I think this is much more generally useful than just rewriting
the prelude. the Lattice example I gave is right out of my toolbox and
my anoyances with it are part of what motivated me to write this.

> Anyway, my main point it: would a smaller change not suffice?

I do not think it suffices.

We could extend the supertyping relationship some more to make it
suitable, but I think we will end up with the exact same proposal but
with different terminology :)

        John

-- 
John Meacham - ⑆repetae.net⑆john⑈ 


More information about the Haskell mailing list