I'm reading through Chapter 25 (Composing Types) of the haskellbook, and wish to understand applicative composition more completely
The author provides a type to embody type composition:
newtype Compose f g a =
Compose { getCompose :: f (g a) }
deriving (Eq, Show)
and supplies the functor instance for this type:
instance (Functor f, Functor g) =>
Functor (Compose f g) where
fmap f (Compose fga) =
Compose $ (fmap . fmap) f fga
But the Applicative instance is left as an exercise to the reader:
instance (Applicative f, Applicative g) =>
Applicative (Compose f g) where
-- pure :: a -> Compose f g a
pure = Compose . pure . pure
-- (<*>) :: Compose f g (a -> b)
-- -> Compose f g a
-- -> Compose f g b
Compose fgf <*> Compose fgx = undefined
I can cheat and look the answer up online... The source for Data.Functor.Compose provides the applicative instance definition:
Compose f <*> Compose x = Compose ((<*>) <$> f <*> x)
but I'm having trouble understanding what's going on here. According to type signatures, both f
and x
are wrapped up in two layers of applicative structure. The road block I seem to be hitting though is understanding what's going on with this bit: (<*>) <$> f
. I will probably have follow up questions, but they probably depend on how that expression is evaluated. Is it saying "fmap <*>
over f" or "apply <$>
to f"?
Please help to arrive at an intuitive understanding of what's happening here.
Thanks! :)
Compose fgh <*> Compose fgx = Compose ((<*>) <$> fgh <*> fgx) = Compose (liftA2 (<*>) fgh fgx)
andliftA2 (<*>) fgh fgx ::~ f {gh <*> gx} :: f (g {h x})
(pseudocode, reading{ }
as "type of"). – Will Ness