4

Could someone please provide me an example of

invmap :: (a -> b) -> (b -> a) -> f a -> f b  

and for what is Invariant good for?

softshipper
  • 32,463
  • 51
  • 192
  • 400

1 Answers1

9

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.

dfeuer
  • 48,079
  • 5
  • 63
  • 167
  • “Each invariant functor can be expressed in terms of a `Profunctor`”\[Citation needed\] – leftaroundabout Feb 09 '18 at 17:17
  • @leftroundabout, does that need a citation? It's just generalizing the type of `invmap` to a two-argument data type and getting `dimap`. – Carl Feb 09 '18 at 19:19
  • @Carl, I suspect leftaroundabout means that I should support the claim that *every* `Invariant` instance can be split like that. I'm quite confident of that, but I don't have a citation or a proof. A related claim I also have no idea how to prove is that `Invariant` instances are unique. I will try to add some explanation of why it's true. – dfeuer Feb 09 '18 at 19:57