DeriveFunctor: Use `contramap' in contravariant position
This rule for cofmap in GHC.Tc.Deriv.Functor
can not deal with a type variable in contravariant position
$(cofmap 'a 'b x) = x -- when b does not contain a
$(cofmap 'a 'a x) = error "type variable in contravariant position"
$(cofmap 'a '(b1,b2) x) = case x of (x1,x2) -> ($(cofmap 'a 'b1) x1, $(cofmap 'a 'b2) x2)
$(cofmap 'a '(T b1 a) x) = error "type variable in contravariant position" -- when a only occurs directly as the last argument of T
$(cofmap 'a '(T b1 b2) x) = fmap (\y. $(cofmap 'a 'b2 y)) x -- when a only occurs in the last parameter, b2
$(cofmap 'a '(tb -> tc) x) = \(y:tb[b/a]) -> $(cofmap 'a' 'tc' (x $(fmap 'a 'tb y)))
but now that we have Data.Functor.Contravariant
in base why not generate an invocation to contramap
?
$(cofmap 'a '(T b1 a) x) = contramap f x
As an example, this works becuase of the special case for functions
> :set -XDerivingStrategies -XDeriveFunctor
> newtype F a = MkF ((a -> Int) -> Int) deriving stock Functor
but if we use Op Int a
(which is a newtype wrapping a -> Int
) ghc does not know how to derive it.
> newtype F a = MkF (Op Int a -> Int) deriving stock Functor
<interactive>:10:52: error:
• Can't make a derived instance of
‘Functor F’ with the stock strategy:
Constructor ‘MkF’ must not use the type variable in a function argument
• In the newtype declaration for ‘F’
I claim it should work by inserting a contramap
invocation there so both these definitions
newtype F a = MkF (Op Int a -> Int) deriving stock Functor
newtype F a = MkF (Equivalence a -> Int) deriving stock Functor
would generate the following (basically)
instance Functor F where
fmap :: (a -> a') -> (F a -> F a')
fmap f (MkF as) = MkF (fmap as (contramap f))