组成应用语

时间:2019-09-22 23:54:43

标签: haskell applicative function-composition

我正在阅读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)

但是我在理解这里发生的事情时遇到了麻烦。根据类型签名,fx都包裹在两层应用结构中。我似乎遇到的障碍是了解(<*>) <$> f这是怎么回事。我可能会有后续问题,但它们可能取决于对表达式的评估方式。是说“在f上映射f <*>”还是“将<$>应用于f”?

请帮助您直观了解此处发生的情况。

谢谢! :)

2 个答案:

答案 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)bJust 3,而cJust 5a <$> b然后求值为Just (\y -> 3 + y),然后a <$> b <*> c然后求值为Just 8

(如果之前没有意义,那么在尝试了解多层应用程序之前,您应该尝试进一步了解它们的单层应用。)

类似地,在您的情况下,a(<*>)bf,而cx。如果您要为fx选择合适的值,则会发现它们也可以很容易地进行评估(尽管请确保各层保持不同; (<*>)在您的情况属于内部Applicative,而<$><*>属于外部Applicative。)