编写应用程序
Composing Applicatives
我正在阅读 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 实例作为练习留给 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
我可以作弊并在线查找答案... Data.Functor.Compose 的来源提供了应用实例定义:
Compose f <*> Compose x = Compose ((<*>) <$> f <*> x)
但我无法理解这里发生的事情。根据类型签名,f
和 x
都包含在两层应用结构中。我似乎遇到的障碍是了解这个位的情况:(<*>) <$> f
。我可能会有后续问题,但它们可能取决于该表达式的评估方式。是说 "fmap <*>
over f" 还是 "apply <$>
to f"?
请帮助对这里发生的事情有一个直观的了解。
谢谢! :)
考虑表达式 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
选择合适的值,您会发现它们也可以很容易地计算出来(尽管一定要保持您的图层不同;您的 (<*>)
case 属于内部 Applicative,而 <$>
和 <*>
属于外部 Applicative。
而不是 <*>
,您可以定义 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))
我正在阅读 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 实例作为练习留给 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
我可以作弊并在线查找答案... Data.Functor.Compose 的来源提供了应用实例定义:
Compose f <*> Compose x = Compose ((<*>) <$> f <*> x)
但我无法理解这里发生的事情。根据类型签名,f
和 x
都包含在两层应用结构中。我似乎遇到的障碍是了解这个位的情况:(<*>) <$> f
。我可能会有后续问题,但它们可能取决于该表达式的评估方式。是说 "fmap <*>
over f" 还是 "apply <$>
to f"?
请帮助对这里发生的事情有一个直观的了解。
谢谢! :)
考虑表达式 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
选择合适的值,您会发现它们也可以很容易地计算出来(尽管一定要保持您的图层不同;您的 (<*>)
case 属于内部 Applicative,而 <$>
和 <*>
属于外部 Applicative。
而不是 <*>
,您可以定义 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))