Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Trying to make a wrapped type family into a something Functor-like

Tags:

haskell

Here is a simple example where the identity Functor works well:

newtype R a = R a

instance Functor R where
    fmap f (R a) = R $ f a

but if I add an intermediate type family, things get wonky:

data IntT
data a :-> b

type family   Sem a         :: *
type instance Sem IntT      = Int
type instance Sem (a :-> b) = Sem a -> Sem b

newtype S a = S (Sem a)

and now I can't make S into a Functor. I can easily define a new class of Functor-like things, but then I will also need a class of Applicative-like and Monad-like, and that seems like an unhappy road. Especially as

smap f (S a) = S $ f a

actually has the type I want, namely smap :: (Sem a -> Sem b) -> S a -> S b. But, of course, this is not the type of a Functor. (Don't you just hate it when the "same" code has 2 different, incompatible types?)

I have explored Data.Category.Functor as well as Generics.Pointless.Functors, but neither seemed to quite solve my problem either. PointlessTypeFamilies seemed to have further good ideas, and yet I am still unsure how to get something sufficiently Functor-like out of this.

It has dawned onto me that even though the code for smap is identical to that of fmap for R, what is going on is slightly different. In a way, if I had a natural transformation from Sem to S, then somehow I ought to be able to lift that to obtain smap. At that point, I figured I might as well ask here, that might save me quite a bit of trouble!

like image 859
Jacques Carette Avatar asked Nov 14 '12 02:11

Jacques Carette


1 Answers

When I encounter a situation like this I usually switch to something like:

data S b = forall a. S (a -> b) (Sem a)

which can easily be made into a law-abiding Functor

instance Functor S where
  fmap f (S g s) = S (f . g) s

or I turn to Coyoneda to package up that behavior for me.

like image 78
Edward Kmett Avatar answered Nov 14 '22 10:11

Edward Kmett