"Transposition" of functors?
I recently had to write the following function:
mToL :: Maybe [a] -> [Maybe a] mToL Nothing = [] mToL (Just xs) = map Just xs This raised the question of whether the above can be generalized:
transposeF :: (Functor f, Functor g) => f (ga) -> g (fa) I assume that this only works if there is a way to βcollapseβ f (ga) to fa , or is there another way?
Actually there is a type class that supports this. It is not part of the standard type class, but "representable functors" have this feature.
The representative functor is an F functor with two things
- type
A - Isomorphism between
Fand(->) A
We could imagine it as
type family ReprObj (f :: * -> *):: * class Functor f => Repr f where toHom :: fa -> (ReprObj f -> a) fromHom :: (ReprObj f -> a) -> fa where toHom . fromHom = fromHom . toHom = id toHom . fromHom = fromHom . toHom = id toHom . fromHom = fromHom . toHom = id . An example of a representable functor,
newtype Pair a = Pair (a, a) deriving Functor type instance ReprObj Pair = Bool instance Repr Pair where toHom (Pair (a, b)) True = a toHom (Pair (a, b)) False = b fromHom f = Pair (f True, f False) Now, using this, we can get
swap :: (Functor f, Functor g, Repr f, Repr g) => f (ga) -> g (fa) swap g = fromHom $ \obj -> fmap ($ obj) hom where hom = fmap toHom g In fact, we can also get a free applicative instance and a monad instance from representable functors. I described in detail how you could do this in a post.
Traversable traversable typeclass provides sequence and sequenceA operations that provide the most common solutions to your problem, but they require various restrictions:
sequence :: (Traversable t, Monad m) => t (ma) -> m (ta) sequenceA :: (Traversable t, Applicative f) => t (fa) -> f (ta) This is generally impossible, no. Set f to Const b and g to Identity .
newtype Const ba = Const { getConst :: b } newtype Identity a = Identity { runIdentity :: a } These are both explicit functors with their normal instances. transposeF cannot work because Const b does not provide any a for wrapping with Identity. Therefore, you cannot record the transpose function.
On the other hand, this is good for whole Functor parses. The categorical concept refers to the conjugate functor , and as soon as you see them, you will begin to see them everywhere. They are a very powerful concept in their own right.
It is impossible to do this with functors simply because there is no general way to expand the value of a functor:
Prelude> :info Functor class Functor f where fmap :: (a -> b) -> fa -> fb (GHC.Base.<$) :: a -> fb -> fa -- Defined in `GHC.Base' instance Functor Maybe -- Defined in `Data.Maybe' instance Functor (Either a) -- Defined in `Data.Either' instance Functor [] -- Defined in `GHC.Base' instance Functor IO -- Defined in `GHC.Base' instance Functor ((->) r) -- Defined in `GHC.Base' instance Functor ((,) a) -- Defined in `GHC.Base' Instead, you can create your own typeclass to provide a generic transpose function as follows:
import Control.Applicative class Applicative f => Transposable t where unwrap :: ta -> a transpose :: s (ta) -> t (sa) transpose = fmap pure . unwrap The reason we make Applicative superclass of Transposable , so we can use its pure method. All Applicative instances are also Functor instances.