【问题标题】:How do you use Control.Applicative to write cleaner Haskell?你如何使用 Control.Applicative 来编写更干净的 Haskell?
【发布时间】:2010-01-20 19:52:28
【问题描述】:

在最近的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 时需要记住哪些有用的经验法则?

【问题讨论】:

    标签: haskell coding-style


    【解决方案1】:

    回答你的问题有很多话要说,但是,既然你问了,我会提供这个“经验法则”。

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

    例如,让我们看下面的代码:

    do a <- e1
       b <- e2
       c <- e3
       return (f a b c)
    

    我们看到在&lt;- 右侧的所有表达式中都没有出现任何生成的值(abc)。因此,我们可以将其转换为使用 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-comprehension 并很快确定它是否可以纳入@987654337 @样式代码。

    [1] 出现在&lt;- 左侧的那些。

    [2] 出现在&lt;- 右侧的表达式。

    [3] 严格来说,它的一部分可以通过分解e2 a

    【讨论】:

    • 如果您喜欢应用风格,有时可以混合使用 Monad 和 Applicative 运算符。你的第二个 do 块可以写成f &lt;$&gt; (e1 &gt;&gt;= e2) &lt;*&gt; e3
    • +1 获取 Applicative 何时足够以及何时需要 Monad 的具体示例。
    【解决方案2】:

    基本上,monad 也是应用函子 [1]。因此,每当您发现自己使用liftMliftM2 等时,您都可以使用&lt;*&gt; 将计算链接在一起。在某种意义上,您可以将应用函子视为类似于函数。纯函数f 可以通过f &lt;$&gt; x &lt;*&gt; y &lt;*&gt; z 解除。

    与 monads 相比,applicative functors 不能有选择地运行它的参数。所有论点的副作用都会发生。

    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 依次输出TrueFalse

    [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。展示了如何以应用方式使用一元 Parsec 库。

    【讨论】:

    • 这是我看到的唯一一个解释MonadApplicative 之间区别的地方(就你可以用每个表达的内容而言)。做得好!我们可以使用应用函数调用iftez = ifte &lt;$&gt; ... 吗?
    • ifte 已经是一个单子函数,所以再提升一次也没有意义。
    • 你可以把 "ifte' (pure True)" 写成 "ifte' True"——"pure" 很少需要与 "" 和 "" (如果有的话)。
    • y = ifte' True putStrLn "True" putStrLn "False"
    • 当您离开 pure 时,它是 y = ifte' True &lt;$&gt; (putStrLn "True") &lt;*&gt; (putStrLn "False")
    【解决方案3】:

    【讨论】:

      猜你喜欢
      • 2015-11-16
      • 2016-04-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-01-16
      • 1970-01-01
      • 2014-01-11
      • 1970-01-01
      相关资源
      最近更新 更多