Reputation: 4219
I wanted to extend the idea of fst
and snd
to type classes. So I wrote the typeclasses
class Fstable f where
fst' :: f a -> a
class Sndable f where
snd' :: f a -> a
It was easy enough to write the instance for Sndable
, just the following
instance Sndable ((,) a) where
snd' (a, b) = b
However the instance for Fstable
is not as easy. Now I would very much like to use a flip
on (,)
to create the * -> *
I want. But there is no flip at the type level so I would have to make my own or make the * -> *
I want on my own.
I haven't done anything type level in a couple months but I remember that one way to make type level functions is using FunctionalDependencies
. I can make the flip of (,)
easily enough:
{-# Language MultiParamTypeClases, FunctionalDependcies, FlexibleInstances #-}
class BackTuple a b c | a b -> c
instance BackTuple a b (b, a)
But BackTuple
has kind * -> * -> * -> Constraint
rather than the * -> * -> *
I want.
So then I tried using a type synonym
{-# Language TypeSynonymInstances #-}
type W a b = (b, a)
instance Fstable (W a) where
fst' (a, b) = a
But I can't curry so W a
complains about the fact that it is not W
is being supplied the wrong number of arguments. I feel like maybe a type family could fix this but I don't know where to start.
I tried to build a flip, but I didn't really know how to start there either.
How can I make the flip of (,)
?
Upvotes: 4
Views: 278
Reputation: 152707
One choice would be to use a plain old simply-kinded class rather than a higher-kinded one. For example:
class Fstable tuple fst | tuple -> fst where fst' :: tuple -> fst
class Sndable tuple snd | tuple -> snd where snd' :: tuple -> snd
instance Fstable (a,b) a where fst' = fst
instance Sndable (a,b) b where snd' = snd
Analogous definitions can be done with type families if you prefer, of course:
class Fstable tuple where type Fst tuple; fst' :: tuple -> Fst tuple
class Sndable tuple where type Snd tuple; snd' :: tuple -> Snd tuple
instance Fstable (a,b) where type Fst (a,b) = a; fst' = fst
instance Sndable (a,b) where type Snd (a,b) = b; snd' = snd
The other choice, which is compatible with your original class, is to use a newtype to flip the arguments. Unlike a type alias, newtypes are suitable for partial application and so can be used with higher-kinded classes. The price you pay for this feature is the syntactic noise of introducing a newtype wrapper at each usage of the class.
newtype Flip f b a = Flip (f a b)
instance Fstable (Flip (,) b) where
fst' (Flip (a,b)) = a
Upvotes: 5