你如何使用Control.Applicative编写更干净的Haskell?

Gre*_*con 60 haskell coding-style

在最近的一个风格问题的答案中,我写道

main = untilM (isCorrect 42) (read `liftM` getLine)
Run Code Online (Sandbox Code Playgroud)

isCorrect num guess =
  case compare num guess of
    EQ -> putStrLn "You Win!" >> return True
    ...
Run Code Online (Sandbox Code Playgroud)

Martijn帮助提出了替代方案:

main = untilM (isCorrect 42) (read <$> getLine)

EQ -> True <$ putStrLn "You Win!"
Run Code Online (Sandbox Code Playgroud)

使用Control.Applicative中的抽象可以使Haskell代码中的哪些常见模式更清晰?有效使用Control.Applicative时要记住哪些有用的经验法则?

Ton*_*ris 48

回答你的问题有很多话要说,但是,既然你问过,我会提供这个"经验法则".

如果您正在使用do-notation,并且您正在排序的表达式[2]中未使用生成的值[1],那么该代码可以转换为Applicative样式.同样,如果在排序的表达式中使用一个或多个生成的值,则必须使用MonadApplicative不足以实现相同的代码.

例如,让我们看看以下代码:

do a <- e1
   b <- e2
   c <- e3
   return (f a b c)
Run Code Online (Sandbox Code Playgroud)

我们看到,在没有表达式的权利<-做任何生成的值(a,b,c)出现.因此,我们可以将其转换为使用Applicative代码.这是一个可能的转变:

f <$> e1 <*> e2 <*> e3
Run Code Online (Sandbox Code Playgroud)

另一个:

liftA3 f e1 e2 e3
Run Code Online (Sandbox Code Playgroud)

另一方面,以这段代码为例:

do a <- e1
   b <- e2 a
   c <- e3
   return (f b c)
Run Code Online (Sandbox Code Playgroud)

此代码不能使用Applicative[3],因为a稍后在理解中的表达式中使用生成的值.这必须用于Monad获得结果 - 尝试将其Applicative考虑在内以了解原因.

关于这个主题有一些更有趣和有用的细节,但是,我只是想给你这个经验法则,你可以do略过一个理解并快速确定它是否可以被考虑到Applicative样式代码中.

[1]出现在左边的那些<-.

[2]表达在右边的表达<-.

[3]严格来说,部分内容可以通过分解来实现e2 a.

  • 如果您喜欢应用风格,有时可以混合使用Monad和Applicative运算符.你的第二个块可以写成`f <$>(e1 >> = e2)<*> e3` (13认同)
  • +1表示具体的例子,当Applicative足够时,以及需要Monad时. (3认同)

Wei*_* Hu 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")
Run Code Online (Sandbox Code Playgroud)

x只有输出True,而y输出TrueFalse顺序.

[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库如何以应用方式使用.

  • 这是我见过的唯一一个"Monad"和"Applicative"之间区别的地方(根据你们每个人的表达方式).很好!我们可以使用应用函数调用`ifte`:`z = ifte <$> ...`? (3认同)
  • 您可以将"ifte'<$>(纯真)"写为"ifte'True" - 很少需要"pure"与"<$>"和"<*>"(如果有的话)相结合. (3认同)