Could someone please provide me an example of
invmap :: (a -> b) -> (b -> a) -> f a -> f b
and for what is Invariant good for?
Could someone please provide me an example of
invmap :: (a -> b) -> (b -> a) -> f a -> f b
and for what is Invariant good for?
Mostly, people don't use Invariant. The reason you'd want to is if you're working with a type in which a variable appears in both covariant and contravariant positions.
newtype Endo a = Endo {appEndo :: a -> a}
newtype Foo a = Foo (Maybe a -> IO a)
data Bar a = Bar [a] (a -> Bool)
None of these are instances of Functor or Contravariant, but they can all be instances of Invariant.
The reason people rarely bother is that if you need to do a lot of mapping over such a type, you're typically better off factoring it out into covariant and contravariant parts. Each invariant functor can be expressed in terms of a Profunctor:
newtype FooP x y = FooP (Maybe x -> IO y)
data BarP x y = Bar [y] (x -> Bool)
Now
Endo a ~= (->) a a
Foo a ~= FooP a a
Bar a ~= BarP a a
-- So we'd likely write newtype Bar a = Bar (BarP a a)
It's generally easier to see what's going on if you unwrap the newtype, dimap over the underlying Profunctor, and then wrap it up again rather than messing around with invmap.
How can we transform an Invariant functor into a Profunctor? First, let's dispose of sums and products. If we can turn f and g into profunctors fp and gp, then we can surely turn f :+: g and f :*: g into equivalent profunctor sums and products.
What about compositions? It's slightly trickier, but not much. Suppose that we can turn f and g into profunctors fp and gp. Now define
-- Compose f g a ~= ComposeP fp gp a a
newtype ComposeP p q a b = ComposeP (p (q b a) (q a b))
instance (Profunctor p, Profunctor q) => Profunctor (ComposeP p q) where
dimap f g (ComposeP p) = ComposeP $ dimap (dimap g f) (dimap f g) p
Now suppose you have a function type; f a -> g a. This looks like fp b a -> gp a b.
I think that should cover most of the interesting cases.