在最近的answer to a style question中,我写了
main = untilM (isCorrect 42) (read `liftM` getLine)
和
isCorrect num guess =
case compare num guess of
EQ -> putStrLn "You Win!" >> return True
...
Martijn有用地提出了替代方案:
main = untilM (isCorrect 42) (read <$> getLine)
EQ -> True <$ putStrLn "You Win!"
使用Control.Applicative的抽象可以使Haskell代码中的哪些常见模式更清晰?有效使用Control.Applicative时要记住哪些有用的经验法则?
答案 0 :(得分:48)
在回答你的问题时有很多话要说,但是,既然你问过,我会提供这个“经验法则”。
如果您正在使用do
- 表示法,并且您正在排序的表达式[2]中未使用生成的值[1],那么该代码可以转换为Applicative样式。同样,如果您在已排序的表达式中使用一个或多个生成的值,则必须使用Monad
并且Applicative
不足以实现相同的代码。
例如,让我们看看以下代码:
do a <- e1
b <- e2
c <- e3
return (f a b c)
我们发现,在<-
右侧的任何表达式中,都不会显示任何生成的值(a
,b
,c
)。因此,我们可以将其转换为使用Applicative代码。这是一个可能的转变:
f <$> e1 <*> e2 <*> e3
和另一个:
liftA3 f e1 e2 e3
另一方面,以这段代码为例:
do a <- e1
b <- e2 a
c <- e3
return (f b c)
此代码无法使用Applicative
[3],因为稍后在理解中的表达式中使用生成的值a
。这必须使用Monad
来获得结果 - 尝试将其计入Applicative
以了解原因。
关于这个主题还有一些有趣且有用的细节,但是,我只是想给你这个经验法则,你可以略过do
- 理解并快速确定它是否可以被考虑到Applicative
样式代码。
[1]显示在<-
左侧的那些。
[2]显示在<-
右侧的表达式。
[3]严格来说,部分内容可以通过考虑e2 a
。
答案 1 :(得分:44)
基本上,monad也是应用函子[1]。因此,只要您发现自己使用liftM
,liftM2
等,就可以使用<*>
将计算链接在一起。从某种意义上说,你可以认为applicative functor与函数类似。通过执行f
可以解除纯函数f <$> x <*> y <*> z
。
与monad相比,applicative functor无法有选择地运行其参数。所有论点都会产生副作用。
import Control.Applicative
ifte condition trueClause falseClause = do
c <- condition
if c then trueClause else falseClause
x = ifte (return True) (putStrLn "True") (putStrLn "False")
ifte' condition trueClause falseClause =
if condition then trueClause else falseClause
y = ifte' <$> (pure True) <*> (putStrLn "True") <*> (putStrLn "False")
x
仅输出True
,而y
依次输出True
和False
。
[1] The Typeclassopedia。强烈推荐。
[2] http://www.soi.city.ac.uk/~ross/papers/Applicative.html。虽然这是一篇学术论文,但并不难理解。
[3] http://learnyouahaskell.com/functors-applicative-functors-and-monoids#applicative-functors。很好地解释了这笔交易。
[4] http://book.realworldhaskell.org/read/using-parsec.html#id652399。显示monadic Parsec
库如何以应用方式使用。
答案 2 :(得分:10)
见Bryan O'Sullivan的The basics of applicative functors, put to practical work。