你如何使用Control.Applicative来编写更清洁的Haskell?
我写了一个最近对风格问题的回答
main = untilM (isCorrect 42) (read `liftM` getLine)
和
isCorrect num guess = case compare num guess of EQ -> putStrLn "You Win!" >> return True ...
Martijn有用地build议了替代scheme:
main = untilM (isCorrect 42) (read <$> getLine) EQ -> True <$ putStrLn "You Win!"
通过使用Control.Applicative抽象,可以使Haskell代码中哪些常见模式变得更清晰? 记住有效使用Control.Applicative的有用的经验法则是什么?
回答你的问题还有很多,但是,既然你问了,我会提供这个“经验法则”。
如果您正在使用do
-notation,并且您所生成的值[1]未在您正在sorting的expression式[2]中使用,那么该代码可以转换为Applicative样式。 同样,如果在sorting的expression式中使用一个或多个生成的值,则必须使用Monad
而Applicative
的强度不足以实现相同的代码。
例如,让我们看看下面的代码:
do a <- e1 b <- e2 c <- e3 return (fabc)
我们看到,在<-
的右边的expression式中没有任何一个生成的值( a
, b
, c
)出现。 因此,我们可以将其转换为使用Applicative代码。 这是一个可能的转变:
f <$> e1 <*> e2 <*> e3
另一个:
liftA3 f e1 e2 e3
另一方面,以这段代码为例:
do a <- e1 b <- e2 a c <- e3 return (fbc)
该代码不能使用Applicative
[3],因为生成的值a
稍后在理解的expression式中使用。 这必须使用Monad
来得到它的结果 – 尝试将其分解到Applicative
以得到原因。
关于这个问题还有一些有趣的和有用的细节,但是我只是想给你一个这样的经验法则,你可以浏览一个do
comprehension,并且很快确定它是否可以被分解到Applicative
样式代码中。
[1]那些出现在<-
的左边。
[2]表示出现在<-
的右侧。
[3]严格来说,其中的一部分可以通过分解e2 a
。
基本上,单子也是应用函子[1]。 所以,只要你发现自己使用了liftM
, liftM2
等,就可以使用<*>
来链接计算。 从某种意义上讲,你可以把应用函数看作类似于函数的函数。 纯函数f
可以通过执行f <$> x <*> y <*> z
来提升。
与monad相比,applicative仿函数不能有select地运行它的参数。 所有争论的副作用将发生。
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] 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
库。
请参阅应用函数的基础知识,由Bryan O'Sullivan 提供实际工作 。