【问题标题】:Composing Applicatives编写应用程序
【发布时间】:2019-09-22 23:54:43
【问题描述】:

我正在阅读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 都包裹在两层应用结构中。不过,我似乎遇到的障碍是了解这个位发生了什么:(&lt;*&gt;) &lt;$&gt; f。我可能会有后续问题,但它们可能取决于如何评估该表达式。是说“fmap &lt;*&gt; over f”还是“将&lt;$&gt; 应用到 f​​”?

请帮助您直观地了解这里发生的事情。

谢谢! :)

【问题讨论】:

  • Compose fgh &lt;*&gt; Compose fgx = Compose ((&lt;*&gt;) &lt;$&gt; fgh &lt;*&gt; fgx) = Compose (liftA2 (&lt;*&gt;) fgh fgx)liftA2 (&lt;*&gt;) fgh fgx ::~ f {gh &lt;*&gt; gx} :: f (g {h x})(伪代码,将 { } 读作“类型”)。

标签: haskell applicative function-composition


【解决方案1】:

考虑表达式a &lt;$&gt; b &lt;*&gt; c。这意味着获取函数a,并将其映射到函子b,这将产生一个新函子,然后将新函子映射到函子c

首先,假设a(\x y -&gt; x + y)bJust 3cJust 5a &lt;$&gt; b 然后计算为Just (\y -&gt; 3 + y)a &lt;$&gt; b &lt;*&gt; c 然后计算为Just 8

(如果这里之前的内容没有意义,那么在尝试理解多层应用程序之前,您应该尝试进一步理解单层应用程序。)

同样,在您的情况下,a(&lt;*&gt;)bfcx。如果您要为fx 选择合适的值,您会发现它们也可以很容易地进行评估(尽管请确保您的图层不同;在您的情况下,(&lt;*&gt;) 属于内部 Applicative ,而&lt;$&gt;&lt;*&gt; 属于外部)。

【讨论】:

  • 这对我看到一个被评估的具体示例帮助很大: Compose [Just (+1)] Compose [Just 5] // Compose () [ Just (+1)] [Just 5] // 撰写 [() (Just (+1))] [Just 5] // 撰写 [Just (+1) Just 5] // Compose [Just 6] // ``` 我不知道为什么我把事情复杂化了 :shrug: 谢谢 Joseph!
【解决方案2】:

您可以定义liftA2,而不是&lt;*&gt;

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))

【讨论】:

  • (...和a &lt;*&gt; b === liftA2 id a b.)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-09-27
  • 1970-01-01
  • 1970-01-01
  • 2012-08-21
相关资源
最近更新 更多