[Haskell-cafe] Type classes question

Ryan Ingram ryani.spam at gmail.com
Tue Oct 7 09:21:53 EDT 2008


On Tue, Oct 7, 2008 at 1:13 PM, Roly Perera
<roly.perera at dynamicaspects.org> wrote:
> Hi,
>
> I'm reasonably well versed in Haskell but fairly new to defining type classes.
> In particular I don't really understand how to arrange for all instances of X
> to also be instances of Y.
>
> It's quite possibly that my question is ill-posed, so I'll make it as concrete
> as possible: in the following code, I define a Stream class, with two
> instances, Stream1 and Stream2.  How do I arrange for there to be one
> implementation of Functor's fmap for all Stream instances?  I currently rely on
> delegation, but in the general case this isn't nice.

With your current implementation, you can't.  You get lucky because
all of your instance declarations are of the form
> instance Stream (X a) a
for some type X.

But it's just as possible to say

> newtype Stream3 = S3 [Int]

> instance Stream Stream3 Int where
>   first (S3 xs) = head xs
>   next (S3 xs) = tail xs
>   fby x (S3 xs) = S3 (x:xs)

Now the only valid fmap_ is over functions of type (Int -> Int).

If you really want all your instances to be type constructors, you
should just say so:

> class Stream f where
>    first :: f a -> a
>    next :: f a -> f a
>    fby :: a -> f a -> f a

Now, with this implementation what you want is at least somewhat
possible, but there's a new problem: there's no good way in haskell to
define superclasses or default methods for existing classes.  There is
a standing "class aliases" proposal [1], but nobody has implemented
it.

The current recommended practice is to define a "default" and leave it
to your instances to use it.  It's kind of ugly, but thems the breaks:

> class Functor f => Stream f where -- you said you want all streams to be functors, so enforce it!
>    first :: f a -> a
>    next :: f a -> f a
>    fby :: a -> f a -> f a
>
> fmapStreamDefault f = uncurry fby . both (f . first) (fmap_ f . next)
>
> instance Functor Stream1 where fmap = fmapStreamDefault
> instance Stream Stream1 where
>    first (x :< _) = x
>    next (_ :< xs) = xs
>    fby = (:<)

Here's another possible solution:

> newtype AsFunctor s a = AF { fstream :: (s a) }
> instance (Stream f) => Functor (AsFunctor f) where
>     fmap f (AF s) = AF (fmapStreamDefault f s)

Now to use fmap you wrap in AF and unwrap with fstream.

None of the existing solutions are really satisfactory, unfortunately.

   -- ryan

[1] http://repetae.net/recent/out/classalias.html


More information about the Haskell-Cafe mailing list