我正在阅读haskellbook的第25章(撰写类型),并希望更完整地理解应用构成
作者提供了一种类型来体现类型构成:
newtype Compose f g a =
Compose { getCompose :: f (g a) }
deriving (Eq, Show)
并提供此类型的仿函数实例:
instance (Functor f, Functor g) =>
Functor (Compose f g) where
fmap f (Compose fga) =
Compose $ (fmap . fmap) f fga
但Applicative实例留给读者练习:
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
我可以作弊并在线查找答案... Data.Functor.Compose的源提供了适用的实例定义:
Compose f <*> Compose x = Compose ((<*>) <$> f <*> x)
但是我在理解这里发生的事情时遇到了麻烦。根据类型签名,f
和x
都包裹在两层应用结构中。我似乎遇到的障碍是了解(<*>) <$> f
这是怎么回事。我可能会有后续问题,但它们可能取决于对表达式的评估方式。是说“在f上映射f <*>
”还是“将<$>
应用于f”?
请帮助您直观了解此处发生的情况。
谢谢! :)
答案 0 :(得分:2)
您可以定义<*>
而不是liftA2
。
import Control.Applicative (Applicative (..))
newtype Compose f g a = Compose
{ getCompose :: f (g a) }
deriving Functor
instance (Applicative f, Applicative g) => Applicative (Compose f g) where
pure a = Compose (pure (pure a))
-- liftA2 :: (a -> b -> c) -> Compose f g a -> Compose f g b -> Compose f g c
liftA2 f (Compose fga) (Compose fgb) = Compose _1
我们有fga :: f (g a)
和fgb :: f (g b)
,我们需要_1 :: f (g c)
。由于f
是适用的,因此我们可以使用liftA2
组合这两个值:
liftA2 f (Compose fga) (Compose fgb) = Compose (liftA2 _2 fga fgb)
现在我们需要
_2 :: g a -> g b -> g c
由于g
也适用,因此我们也可以使用其liftA2
:
liftA2 f (Compose fga) (Compose fgb) = Compose (liftA2 (liftA2 f) fga fgb)
这种提起liftA2
应用程序的模式对其他事情也很有用。一般来说,
liftA2 . liftA2 :: (Applicative f, Applicative g) => (a -> b -> c) -> f (g a) -> f (g b) -> f (g c)
liftA2 . liftA2 . liftA2
:: (Applicative f, Applicative g, Applicative h)
=> (a -> b -> c) -> f (g (h a)) -> f (g (h b)) -> f (g (h c))
答案 1 :(得分:1)
考虑表达式a <$> b <*> c
。这意味着采用函数a
,并将其映射到函子b
上,这将产生一个新的函子,然后将该新函子映射到函子c
上。
首先,假设a
是(\x y -> x + y)
,b
是Just 3
,而c
是Just 5
。 a <$> b
然后求值为Just (\y -> 3 + y)
,然后a <$> b <*> c
然后求值为Just 8
。
(如果之前没有意义,那么在尝试了解多层应用程序之前,您应该尝试进一步了解它们的单层应用。)
类似地,在您的情况下,a
是(<*>)
,b
是f
,而c
是x
。如果您要为f
和x
选择合适的值,则会发现它们也可以很容易地进行评估(尽管请确保各层保持不同; (<*>)
在您的情况属于内部Applicative,而<$>
和<*>
属于外部Applicative。)