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!
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With