[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