
On Tue, Oct 7, 2008 at 1:13 PM, Roly Perera
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