【问题标题】:Why exactly would using applicative functors be inferior to monads for integer division?为什么使用 applicative functors 不如 monads 进行整数除法?
【发布时间】:2020-06-16 12:04:29
【问题描述】:

我正在阅读 Graham Hutton 的《Haskell 编程》,对下面概述的思路感到困惑。

他使用下面的示例来鼓励使用 monad,通过展示应用函子在返回类型为 Maybe 的除法运算中的缺点来处理指示可能除以零的错误情况 em> 场景。

给定:

data Expr = Val Int | Div Expr Expr

safediv :: Int -> Int -> Maybe Int
safediv _ 0 = Nothing
safediv n m = Just (n `div` m)

eval :: Expr -> Maybe Int
eval (Val n) = pure n                               --type: Just(n)?
eval (Div x y) = pure safediv <*> eval x <*> eval y --type: Maybe(Maybe Int)?

他继续解释:

但是,此定义类型不正确。特别是, 函数safediv 的类型为Int-&gt;Int-&gt;Maybe Int,而在 以上上下文需要Int-&gt;Int-&gt;Int 类型的函数。

用自定义函数替换 pure safediv 没有帮助 要么,因为这个函数需要有类型 Maybe(Int-&gt;Int-&gt;Int),它不提供任何指示方式 当第二个整数参数为零时失败。 (十)

结论是函数eval不符合 由应用函子捕获的有效编程。这 应用风格限制了我们将纯函数应用于 有效的论点:eval 不适合这种模式,因为 用于处理结果值的函数safediv 不是 一个纯函数,但它本身可能会失败。

我不是 Haskell 程序员,但从 eval (Div x y) 的类型来看,它似乎是 Maybe(Maybe Int) 的类型 - 可以简单地压扁,不是吗? (类似于 Scala 中的 flatten 或 Haskell 中的 join)。 真正的问题是什么?

无论x,y 是否为Just(s)/Nothing(s),似乎safediv 都会正确评估——这里唯一的问题是可以适当转换的返回类型。作者究竟是如何从他的论点得出这个结论的,这是我很难理解的。

...applicative style 限制了我们将纯函数应用于有效参数

另外,为什么上面标有(X) 的段落在问题似乎是或返回类型不对齐时会做出这样的声明。

我知道应用程序可用于更有效地链接计算,其中一个的结果不会影响另一个 - 但在这种情况下,我对失败的发生方式/位置以及是否只是简单的返回感到困惑类型修复将解决问题:

eval (Div x y) = join(pure safediv <*> eval x <*> eval y)

safediv 必须是纯粹的吗? AFAIK 它也可以是F[Maybe]F[Either] 类型,不是吗?我可能会错过什么?我可以看到他要去哪里,但不确定这是否是到达那里的正确示例,恕我直言。

【问题讨论】:

  • 应用程序没有join的概念。要获得join,您需要一个单子。这就是重点:applicatives 还不够,你需要一个 monad。
  • 换一种说法:当你的数据类型有join(在Applicative Functor的其他东西之上)时,它一个Monad。

标签: haskell functional-programming


【解决方案1】:

我不是 Haskell 程序员,但从 eval (Div x y) 的类型来看,它似乎是 Maybe(Maybe Int) 的类型 - 可以简单地压扁,不是吗? (类似于 Scala 中的 flatten 或 Haskell 中的 join)。真正这里的问题是什么? …这里唯一的问题是可以适当转换的返回类型

关键问题! ‘Squashing’ 本质上是一个单子操作——事实上,join 的类型签名是join :: Monad m =&gt; m (m a) -&gt; m a。如果您将自己限制在应用方法pure(&lt;*&gt;) 中,则无法实现这一点,但如果您也让自己使用(&gt;&gt;=),这将变得容易。当然,您可以在不使用 monad 的情况下轻松实现 flattenMaybe :: Maybe (Maybe a)) -&gt; Maybe a,但这违背了 ApplicativeMonad 等概念的目的,它们应该适用于广泛的类型,而不仅仅是 Maybe

不管x,y 是否是Just(s)/Nothing(s) 似乎 safediv 将正确评估 - 这里唯一的问题是返回 可以适当转换的类型。究竟是如何 作者从他的论点到这个结论是我所拥有的 很难理解。

...applicative style 限制了我们将纯函数应用于有效参数

另外,为什么上面标有(X) 的段落在 问题似乎是或返回类型不对齐。

这里的想法是这样的。假设您有两个函数和两个值:

nonEffectful :: a -> b -> c
effectful    :: a -> b -> m c

effectfulA :: m a
effectfulB :: m b

现在,如果要将nonEffectful 函数应用于两个有效参数,m 只需为Applicative:很容易做到nonEffectful &lt;$&gt; effectfulA &lt;*&gt; effectfulB :: m c。但是,如果您尝试使用effectful 函数来代替,您会遇到问题:您会得到m (m c) 的返回类型而不是m c。要将m (m c)“压缩”为m c,您需要一个Monad 实例。所以应用程序只能将纯(非有效)函数应用于有效参数,但 monad 让我们可以将有效函数应用于有效参数。这就是 Hutton 试图做的,但有一个特定的功能 safeDiv :: Int -&gt; Int -&gt; Maybe Int

(我在上面的讨论中没有提到的一件事是直觉:为什么,在直观而不是正式的层面上,特定计算需要单子吗?正如您已经注意到的那样,答案和依赖有关。对于nonEffectful &lt;$&gt; effectfulA &lt;*&gt; effectfulB来说,这两个有效值相互之间没有影响。但是,有了effectful &lt;$&gt; effectfulA &lt;*&gt; effectfulB,突然就有了依赖:effectful函数必须依赖于传递的有效计算的结果Monad 可以被认为代表了可以相互依赖的有效计算的概念,而 Applicative 代表不能相互依赖的有效计算的概念(尽管纯函数可能依赖于它们) . 同样,为了评估嵌套计算m (m a),您首先需要评估外部计算,然后评估生成的内部有效计算。同样,我们有一个有效计算依赖于另一个有效计算,所以这重新需要Monad。)

【讨论】:

  • 啊啊!我知道了。这完全有道理。谢谢?
猜你喜欢
  • 2016-05-10
  • 2023-03-04
  • 2022-01-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多