简体   繁体   English

为什么在整数除法中使用应用函子不如 monad?

[英]Why exactly would using applicative functors be inferior to monads for integer division?

I'm reading Graham Hutton's Programming in Haskell and am confused with the flow of thought outlined below.我正在阅读 Graham Hutton 的 Haskell 编程,并对下面概述的思路感到困惑。

He uses the example below to motivate the use of monads by showing the shortcomings of applicative functors for a divide operation where the return type is a Maybe to handle the error case indicating a potential division by zero scenario.他使用下面的示例通过展示应用函子在除法运算中的缺点来激发 monad 的使用,其中返回类型是一个Maybe来处理指示潜在除以零场景的错误情况。

Given:鉴于:

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

He goes on to explain:他继续解释:

However, this definition is not type correct.但是,此定义类型不正确。 In particular, the function safediv has type Int->Int->Maybe Int , whereas in the above context a function of type Int->Int->Int is required.特别是,函数safediv类型为Int->Int->Maybe Int ,而在上述上下文中,需要类型为Int->Int->Int的函数。

Replacing pure safediv by a custom defined function wound not help either, because this function would need to have type Maybe(Int->Int->Int) , which does not provide any means to indicate failure when the second integer argument is zero.用自定义定义的函数代替pure safediv也无济于事,因为该函数需要具有类型Maybe(Int->Int->Int) ,当第二个整数参数为零时,它不提供任何指示失败的方法。 (X) (X)

The conclusion is that the function eval does not fit the pattern of effectful programming that is captured by applicative functors.结论是函数eval不符合应用函子捕获的有效编程模式。 The applicative style restricts restricts us to applying pure functions to effectful arguments: eval does not fit this pattern because the function safediv that is used to process the resulting values is not a pure function, but may itself fail. applicative 风格限制了我们将纯函数应用于有效参数: eval不适合这种模式,因为用于处理结果值的函数safediv不是纯函数,但它本身可能会失败。

I'm not a Haskell programmer but from the type of eval (Div xy) it seems be that of Maybe(Maybe Int) - which can simply be squashed , no?我不是 Haskell 程序员,但从eval (Div xy)的类型来看,它似乎是Maybe(Maybe Int) ——它可以简单地被压扁,不是吗? (Something like a flatten in Scala or join in Haskell). (类似于 Scala 中的flattenjoin Haskell)。 What really is the issue here?这里的真正问题什么?

Irrespective of whether x,y are Just(s)/Nothing(s) it seems safediv will correctly evaluate - the only issue here is the return type which can be transformed appropriately.不管x,yJust(s)/Nothing(s)似乎safediv都会正确评估 - 这里唯一的问题是可以适当转换的返回类型。 How exactly does the author go from his argument to this conclusion is what I'm having a hard time understanding.作者究竟是如何从他的论点得出这个结论的,这是我很难理解的。

...applicative style restricts restricts us to applying pure functions to effectful arguments ...应用风格限制了我们将纯函数应用于有效参数

Also, why does paragraph marked (X) above make that claim when the problem just seems to be or return type misalignment.此外,当问题似乎是或返回类型未对齐时,为什么上面标记为(X)段落会提出该声明。

I understand applicatives can be used for more efficiently chaining computations where the results of one don't impact the other - but in this case I'm rather confused as to how/where the failure would happen and if just a simple return type fix would solve the problem:我知道 applicatives 可用于更有效地链接计算,其中一个的结果不会影响另一个 - 但在这种情况下,我对失败将如何/在哪里发生以及是否只是一个简单的返回类型修复感到困惑解决这个问题:

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

And does safediv have to be pure ?并且safediv必须是纯的吗? AFAIK it could also be of type F[Maybe] or F[Either] , no? AFAIK 它也可以是F[Maybe]F[Either] ,不是吗? What may I be missing?我可能缺少什么? I can see where he's going but not sure if this is the right example to get there IMHO.我能看到他是怎么回事,但不知道这是去那里恕我直言,正确的榜样。

I'm not a Haskell programmer but from the type of eval (Div xy) it seems be that of Maybe(Maybe Int) - which can simply be squashed , no?我不是 Haskell 程序员,但从eval (Div xy)的类型来看,它似乎是Maybe(Maybe Int) ——它可以简单地被压扁,不是吗? (Something like a flatten in Scala or join in Haskell). (类似于 Scala 中的flattenjoin Haskell)。 What really is the issue here?这里的真正问题什么? … the only issue here is the return type which can be transformed appropriately …这里唯一的问题是可以适当转换的返回类型

This is the key issue!关键问题! 'Squashing' is a fundamentally monadic operation — in fact, the type signature of join is join :: Monad m => m (ma) -> ma . 'Squashing' 是一个基本的 monadic 操作——事实上, join的类型签名是join :: Monad m => m (ma) -> ma If you restrict yourself to the applicative methods pure and (<*>) , there is no way to implement this, but it becomes easy if you let yourself use (>>=) as well.如果您将自己限制在应用方法pure(<*>) ,则无法实现这一点,但如果您也让自己使用(>>=) ,这将变得容易。 Sure, you can easily implement flattenMaybe :: Maybe (Maybe a)) -> Maybe a without using monads, but that defeats the purpose of concepts like Applicative and Monad , which should be applicable to a wide range of types, not just Maybe .当然,您可以轻松实现flattenMaybe :: Maybe (Maybe a)) -> Maybe a而不使用 monad,但这违背了ApplicativeMonad等概念的目的,它们应该适用于广泛的类型,而不仅仅是Maybe

Irrespective of whether x,y are Just(s)/Nothing(s) it seems safediv will correctly evaluate - the only issue here is the return type which can be transformed appropriately.不管x,yJust(s)/Nothing(s)似乎safediv都会正确评估 - 这里唯一的问题是可以适当转换的返回类型。 How exactly does the author go from his argument to this conclusion is what I'm having a hard time understanding.作者究竟是如何从他的论点得出这个结论的,这是我很难理解的。

...applicative style restricts restricts us to applying pure functions to effectful arguments ...应用风格限制了我们将纯函数应用于有效参数

Also, why does paragraph marked (X) above make that claim when the problem just seems to be or return type misalignment.此外,当问题似乎是或返回类型未对齐时,为什么上面标记为(X)段落会提出该声明。

The idea here is this.这里的想法是这样的。 Let's say you have two functions, and two values:假设您有两个函数和两个值:

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

effectfulA :: m a
effectfulB :: m b

Now, if you want to apply the nonEffectful function to the two effectful arguments, m only needs to be Applicative : it's easy to do nonEffectful <$> effectfulA <*> effectfulB :: mc .现在,如果您想将nonEffectful函数应用于两个有效参数, m只需要是Applicative :很容易做到nonEffectful <$> effectfulA <*> effectfulB :: mc But if you try that with the effectful function instead, you run into a problem: you get a return type of m (mc) instead of mc .但是如果你用effectful函数来尝试,你会遇到一个问题:你得到的返回类型是m (mc)而不是mc To 'squash' m (mc) into mc , you need a Monad instance.要将m (mc) '挤压'成mc ,您需要一个Monad实例。 So applicatives can only apply pure (non-effectful) functions to effectful arguments, but monads let us apply effectful functions to effectful arguments.所以 applicative 只能将纯(非有效)函数应用于有效参数,但 monad 让我们将有效函数应用于有效参数。 This is what Hutton was attempting to do this, but with a specific function safeDiv :: Int -> Int -> Maybe Int .这就是 Hutton 试图这样做的,但是有一个特定的函数safeDiv :: Int -> Int -> Maybe Int

(One thing I didn't mention in the above discussion is intuition: why , on an intuitive rather than formal level, are monads required for specific computations? As you have already noticed, the answer has to do with dependency. For nonEffectful <$> effectfulA <*> effectfulB , the two effectful values have no impact on each other. However, with effectful <$> effectfulA <*> effectfulB , suddenly there is a dependency: the effectful function must depend on the results of the effectful computations passed to it. Monad can be thought of as representing the idea of effectful computations which can depend on each other, whereas Applicative represents the idea of effectful computations which cannot depend on each other (although a pure function may depend on them). Similarly, in order to evaluate a nested computation m (ma) , you first need to evaluate the outer computation, and then evaluate the resulting inner effectful computation. Again we have an effectful computation which depends on ano (我在上面的讨论中没有提到的一件事是直觉:为什么在直观而不是正式的层面上,特定计算需要 monad?正如您已经注意到的,答案与依赖有关。对于nonEffectful <$> effectfulA <*> effectfulB ,两个有效值nonEffectful <$> effectfulA <*> effectfulB ,但是,有了effectful <$> effectfulA <*> effectfulB ,突然有了一个依赖: effectful函数必须依赖于有效计算的结果Monad可以被认为代表了可以相互依赖的有效计算的思想,而Applicative代表了不能相互依赖的有效计算的思想(尽管纯函数可能依赖于它们)。类似地,在为了评估嵌套计算m (ma) ,您首先需要评估外部计算,然后评估产生的内部有效计算。同样,我们有一个依赖于 ano 的有效计算ther effectful computation, so this requires a Monad .)有效的计算,所以这需要一个Monad 。)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM