[英]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 typeInt->Int->Maybe Int
, whereas in the above context a function of typeInt->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 typeMaybe(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 functionsafediv
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 中的
flatten
或join
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,y
是Just(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 ofMaybe(Maybe Int)
- which can simply be squashed , no?我不是 Haskell 程序员,但从
eval (Div xy)
的类型来看,它似乎是Maybe(Maybe Int)
——它可以简单地被压扁,不是吗? (Something like aflatten
in Scala orjoin
in Haskell).(类似于 Scala 中的
flatten
或join
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,但这违背了Applicative
和Monad
等概念的目的,它们应该适用于广泛的类型,而不仅仅是Maybe
。
Irrespective of whether
x,y
areJust(s)/Nothing(s)
it seemssafediv
will correctly evaluate - the only issue here is the return type which can be transformed appropriately.不管
x,y
是Just(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.