PhD*_*PhD 6 haskell functional-programming
我正在阅读 Graham Hutton 的 Haskell 编程,并对下面概述的思路感到困惑。
他使用下面的示例通过展示应用函子在除法运算中的缺点来激发 monad 的使用,其中返回类型是 aMaybe来处理指示潜在除以零场景的错误情况。
鉴于:
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)?
Run Code Online (Sandbox Code Playgroud)
他继续解释:
但是,此定义类型不正确。特别是,该函数
safediv具有 typeInt->Int->Maybe Int,而在上述上下文中,需要一个 type 函数Int->Int->Int。替换
pure safediv为自定义定义的函数 wind 也无济于事,因为该函数需要具有 typeMaybe(Int->Int->Int),当第二个整数参数为零时,它不提供任何指示失败的方法。(X)结论是该函数
eval不符合应用函子捕获的有效编程模式。applicative 风格限制了我们将纯函数应用于有效参数:eval不适合这种模式,因为safediv用于处理结果值的函数不是纯函数,但它本身可能会失败。
我不是 Haskell 程序员,但从eval (Div x y)它的类型来看似乎是Maybe(Maybe Int)- 可以简单地被压扁,不是吗?(类似于flattenScala 或joinHaskell 中的 a)。这里的真正问题是什么?
不论x,y是Just(s)/Nothing(s)它似乎safediv会正确评价-在这里唯一的问题是可以适当地改变了返回类型。作者究竟是如何从他的论点得出这个结论的,这是我很难理解的。
...应用风格限制了我们将纯函数应用于有效参数
另外,为什么(X)上面标记的段落在问题似乎只是或返回类型未对齐时做出这种声明。
我知道 applicatives 可用于更有效地链接计算,其中一个的结果不会影响另一个 - 但在这种情况下,我很困惑失败将如何/在哪里发生,如果只是一个简单的返回类型修复会解决这个问题:
eval (Div x y) = join(pure safediv <*> eval x <*> eval y)
Run Code Online (Sandbox Code Playgroud)
而且safediv 一定要纯吗?AFAIK它也可以是类型F[Maybe]或F[Either],不是吗?我可能缺少什么?我可以看到在那里他是怎么回事,但不知道这是去那里恕我直言,正确的榜样。
bra*_*drn 10
我不是 Haskell 程序员,但从
eval (Div x y)它的类型来看似乎是Maybe(Maybe Int)- 可以简单地被压扁,不是吗?(类似于flattenScala 或joinHaskell 中的 a)。这里的真正问题是什么?…这里唯一的问题是可以适当转换的返回类型
这是关键问题!'Squashing' 是一个基本的 monadic 操作——事实上,它的类型签名join是join :: Monad m => m (m a) -> m a。如果您将自己限制在可应用的方法pure和 中(<*>),则无法实现这一点,但如果您也让自己使用它,就会变得容易(>>=)。当然,你可以很容易地实现flattenMaybe :: Maybe (Maybe a)) -> Maybe a,而无需使用单子,但像概念失败的目的Applicative和Monad,这应该是适用于多种类型,而不仅仅是Maybe。
不论
x,y是Just(s)/Nothing(s)它似乎safediv会正确评价-在这里唯一的问题是可以适当地改变了返回类型。作者究竟是如何从他的论点得出这个结论的,这是我很难理解的。...应用风格限制了我们将纯函数应用于有效参数
另外,为什么
(X)上面标记的段落在问题似乎只是或返回类型未对齐时做出这种声明。
这里的想法是这样的。假设您有两个函数和两个值:
nonEffectful :: a -> b -> c
effectful :: a -> b -> m c
effectfulA :: m a
effectfulB :: m b
Run Code Online (Sandbox Code Playgroud)
现在,如果你想把nonEffectful函数应用到两个有效的参数上,m只需要Applicative:很容易做到nonEffectful <$> effectfulA <*> effectfulB :: m c。但是如果你用这个effectful函数来尝试,你会遇到一个问题:你得到的返回类型是m (m c)而不是m c。要“挤压”m (m c)到m c,您需要一个Monad实例。所以 applicative 只能将纯(非有效)函数应用于有效参数,但 monad 让我们将有效函数应用于有效参数。这就是 Hutton 试图这样做的,但具有特定的功能safeDiv :: Int -> Int -> Maybe Int。
(我在上面的讨论中没有提到的一件事是直觉:为什么在直观而不是正式的层面上,特定计算需要 monad?正如您已经注意到的,答案与依赖有关。对于nonEffectful <$> effectfulA <*> effectfulB,这两个有效值彼此之间没有影响。然而,effectful <$> effectfulA <*> effectfulB突然之间有了一个依赖:effectful函数必须依赖于传递给它的有效计算的结果。Monad可以被认为是代表有效计算的思想,它可以依赖于每个其他,而Applicative代表有效计算的思想,它们不能相互依赖(尽管纯函数可能依赖它们)。类似地,为了评估嵌套计算m (m a),您首先需要评估外部计算,然后评估由此产生的内部有效计算。同样,我们有一个有效的计算,它依赖于另一个有效的计算,所以这需要一个Monad.)