Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use Data.Functor.Invariant?

Tags:

haskell

Could someone please provide me an example of

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

and for what is Invariant good for?

like image 742
softshipper Avatar asked Oct 16 '25 18:10

softshipper


2 Answers

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.

like image 89
dfeuer Avatar answered Oct 18 '25 07:10

dfeuer


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.

like image 38
dfeuer Avatar answered Oct 18 '25 09:10

dfeuer