Wheat Wizard
Wheat Wizard

Reputation: 4219

How can I do a type-level flip of `(,)`?

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

Answers (1)

Daniel Wagner
Daniel Wagner

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

Related Questions