如何处理 Applicative 的副作用? [英] How to handle side effect with Applicative?

查看:18
本文介绍了如何处理 Applicative 的副作用?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我到处都看到 Applicative 可以处理副作用,但我看到的所有简单示例都只是将以下内容组合在一起:

I see everywhere that Applicative can handle side effects, but all the simple examples I've seen are just combining stuff together like:

> (,,) <$> [1,2] <*> ["a", "b", "c"] <*> ["foo", "bar"]
[(1,"a","foo"),(1,"a","bar"),(1,"b","foo"),(1,"b","bar"),
 (1,"c","foo"),(1,"c","bar"),(2,"a","foo"),(2,"a","bar"),
 (2,"b","foo"),(2,"b","bar"),(2,"c","foo"),(2,"c","bar")]

这很酷,但我看不出这与副作用有何联系.我的理解是 Applicative 是一个弱 monad,因此您可以处理副作用(就像处理 State monad 一样),但您不能重用前一个副作用的结果.

Which is cool but I can't see how that links to side effects. My understanding is that Applicative is a weak monad and so you can handle side effects (as you would do with a State monad) but you can't reuse the result of the previous side effect.

这是否意味着 >> 可以为 Applicative 之类的东西编写

Does that mean that >> could be written for an Applicative and things like

do
  print' "hello"
  print' "world"

会有意义(使用 print' :: a -> Applicative something)(使用适当的 do-applicative 扩展名).

would make sense (with print' :: a -> Applicative something) (with the appropriate do-applicative extension).

在其他世界中,MonadApplicative 的区别在于 Monad 允许 x <- ...Applicative 没有.

In other world, is the difference between Monad and Applicative is that Monad allows x <- ... but Applicative doesn't.

那么,Writer monad 只是一个应用程序吗?

Then, is the Writer monad, just an applicative?

推荐答案

Applicative 和 Monad 都提供了将多个副作用1 值组合"成单个副作用值的方法.

Applicative and Monad both provide ways of "combining" multiple side-effectful1 values into a single side-effectful value.

用于组合的 Applicative 接口只是让您组合有效值,从而产生的有效值根据某些固定"配方组合所有效果.

The Applicative interface for combining just lets you combine effectful values such that the resulting effectful value combines all their effects according to some "fixed" recipe.

用于组合的 Monad 接口允许您以这样的方式组合有效值,即组合值的效果取决于原始有效值在实际解析时的作用.

The Monad interface for combining lets you combine effectful values in such a way that the effects of the combined value depends on what the original effectful values do when they're actually resolved.

例如,State Integer monad/applicative 的值取决于(并影响)某些 Integer 状态.State Integer t 值只有在存在该状态时才具有具体值.

For example, the State Integer monad/applicative is of values that depend upon (and affect) some Integer state. State Integer t values only have a concrete value in the presence of that state.

一个函数,它接受两个 State Integer Char 值(称它们为 ab)并返回一个 State IntegerChar 值并且只使用 State Integer 的 Applicative 接口必须产生一个状态"始终相同的值,无论 Integer 状态值是什么并且无论 Char 值如何,输入都会产生.例如,它可以通过 ab 将状态线程化,以某种方式组合它们的 Char 值.或者它可以通过b然后a威胁国家.或者它可以只选择a 或只选择b.或者它可以完全忽略这两者,不对当前 Integer 状态产生任何影响,而只是 pure 一些字符值.或者它可以以任何固定的顺序运行它们中的任何一个或两个任何固定次数,并且它可以合并它知道的任何其他 State Integer t 值.但无论它做什么,它总是这样做,不管当前的 Integer 状态,或者任何 State Integer t 值产生的任何值它设法得到了它的帮助.

A function that takes two State Integer Char values (call them a and b) and gives us back a State Integer Char value and only uses the Applicative interface of State Integer must produce a value whose "statefulness" is always the same, regardless of what the Integer state value is and regardless of what Char values the inputs yield. For example, it could thread the state through a and then b, combining their Char values somehow. Or it could threat the state through b and then a. Or it could pick only a or only b. Or it could ignore both entirely, not taking either of their effects on the current Integer state, and just pure some char value. Or it could run either or both of them any fixed number of times in any fixed order, and it could incorporate any other State Integer t values it knows about. But whatever it does, it always does that, regardless of the current Integer state, or any values produced by any of the State Integer t values it manages to get its hands on.

接受相同输入但能够使用 State Integer 的 monad 接口的函数可以做的远不止这些.它可以运行 ab 取决于当前 Integer 状态是正还是负.它可以运行a,然后如果结果Char是一个ascii数字字符,它可以把数字变成一个数字并多次运行b.等等.

A function that took the same inputs but was able to use the monad interface for State Integer can do much more than that. It can run a or b depending on whether the current Integer state is positive or negative. It can run a, then if the resulting Char is an ascii digit character it can turn the digit into a number and run b that many times. And so on.

所以是的,计算如下:

do
  print' "hello"
  print' "world"

是一种可以仅使用 Applicative 接口来实现的,无论 print' 返回什么.您几乎可以纠正 Monad 和 Applicative 之间的区别,如果两者都有 do-notation,则 monadic do 将允许 x <- ...,而 applicative do 则不允许.不过,它比这更微妙;也适用于 Applicative:

Is one that could be implemented using only the Applicative interface to whatever print' returns. You are close to correct that the difference between Monad and Applicative if both had a do-notation would be that monadic do would allow x <- ..., while applicative do would not. It's a bit more subtle than that though; this would work with Applicative too:

do  x <- ...
    y <- ...
    pure $ f x y

Applicative 不能做的是检查 xy 来决定调用什么 f(或者对 fxy 的结果做任何事情,而不仅仅是 pure 它.

What Applicative can't do is inspect x and y to decide what f to call on them (or do anything with the result of f x y other than just pure it.

然而,Writer w 作为 monad 和作为应用程序之间没有区别,这并不完全正确.Writer w 的 monadic 接口确实不允许 value 依赖于效果(日志"),因此必须始终可以重写任何使用 monadic 特性定义的 Writer w 到只使用应用特性并且总是产生相同值的 2.但是 monadic 接口允许 effects 依赖于 values,而 applicative 接口则没有,所以你不能总是忠实地重现 的效果编写器 w 仅使用应用程序接口.

You are not quite correct that there's no difference between Writer w as a monad and as an applicative, however. It's true that the monadic interface of Writer w doesn't allow the value yielded to depend on the effects (the "log"), so it must always be possible to rewrite any Writer w defined using monadic features to one that only uses applicative features and always yields the same value2. But the monadic interface allows the effects to depend on the values, which the applicative interface doesn't, so you can't always faithfully reproduce the effects of a Writer w using only the applicative interface.

看这个(有点傻)示例程序:

See this (somewhat silly) example program:

import Control.Applicative
import Control.Monad.Writer

divM :: Writer [String] Int -> Writer [String] Int -> Writer [String] Int
divM numer denom
  = do  d <- denom
        if d == 0
          then  do  tell ["divide by zero"]
                    return 0
          else  do  n <- numer
                    return $ n `div` d


divA :: Writer [String] Int -> Writer [String] Int -> Writer [String] Int
divA numer denom = divIfNotZero <$> numer <*> denom
  where
    divIfNotZero n d = if d == 0 then 0 else n `div` d


noisy :: Show a => a -> Writer [String] a
noisy x = tell [(show x)] >> return x

然后在 GHCi 中加载:

Then with that loaded in GHCi:

*Main> runWriter $ noisy 6 `divM` noisy 3
(2,["3","6"])
*Main> runWriter $ noisy 6 `divM` noisy 0
(0,["0","divide by zero"])
*Main> runWriter $ undefined `divM` noisy 0
(0,["0","divide by zero"])

*Main> runWriter $ noisy 6 `divA` noisy 3
(2,["6","3"])
*Main> runWriter $ noisy 6 `divA` noisy 0
(0,["6","0"])
*Main> runWriter $ undefined `divA` noisy 0
(0,*** Exception: Prelude.undefined
*Main> runWriter $ (tell ["undefined"] *> pure undefined) `divA` noisy 0
(0,["undefined","0"])

注意如何使用divMnumer的效果是否包含在numer `divM` denom中取决于的值denom(与tell ["divide by zero"] 的效果是否相同).在应用接口可以做的最好的情况下,numer 的效果总是包含在 numerdivAdenom 中,即使惰性求值意味着由 numer 产生的 value 永远不会被检查.当分母为零时,不可能在日志中添加除以 0".

Note how with divM, whether numer's effects are included in numer `divM` denom depends on the value of denom (as does whether the effect of tell ["divide by zero"]). With the best the applicative interface can do, the effects of numer are always included in numerdivAdenom, even when lazy evaluation should mean that the value yielded by numer is never inspected. And it's not possible to helpfully add "divide by 0" to the log when the denominator is zero.

1 我不喜欢把结合有效的价值"视为 monad 和 applicative 的定义,但它是一个例子em> 你可以用它们做什么.

1 I don't like to think of "combining effectful values" as the definition of that monads and applicatives do, but it's an example of what you can do with them.

2 无论如何,当不涉及底部时;您应该能够从我的示例中看出为什么底部会弄乱等效性.

2 When bottoms aren't involved, anyway; you should be able to see from my example why bottom can mess up the equivalence.

这篇关于如何处理 Applicative 的副作用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆