【问题标题】:Haskell: Would "do" notation be useful for contexts other than monads?Haskell:“do”表示法对 monad 以外的上下文有用吗?
【发布时间】:2010-07-26 15:17:35
【问题描述】:

我们都喜欢do,我很好奇这种替代语法在理论上是否在 monad 世界之外有用。如果是这样,它将简化哪些其他类型的计算?例如,为 Applicative 提供等效的东西是否有意义?

【问题讨论】:

    标签: syntax haskell


    【解决方案1】:

    我的感觉是many Haskell programmers don't love do,当您不需要Monad 的全部功能时,支持使用Applicative 的常见论据之一是组合子<$>、<*>等允许非常清晰简洁的编码风格。

    即使对于一元代码,许多人更喜欢显式使用=<< 而不是do 表示法。 camccann 的 answer to your previous question about <*> 为这种偏好提供了一个绝妙的论据。

    我倾向于使用do 编写我的初稿,然后在我修改时用组合器替换。这只是我自己(内)的经验和品味的问题:对我来说,以一种更命令式的方式(使用do 更方便)通常最容易画出草图,但我认为非do 代码是通常更漂亮。

    另一方面,对于箭头,我无法想象不使用proc 和command do。元组很快就变得如此丑陋。

    【讨论】:

    • 伙计,我感觉有点糟糕——你在这里获得了超过两倍的赞成票,甚至与我之前的一个答案相关联,但我的答案却被接受了……对于它的价值,我全心全意赞同这个答案!
    【解决方案2】:

    考虑到do 符号本身,它实际上有什么好处可能会有所帮助。正如 Travis Brown 所指出的,我之前提倡使用带有 Monads 和相关类型的“函数应用程序”样式,但也有另一面:有些表达式根本无法编写干净利落的直接函数应用风格。例如,以下内容会很快使 applicative 样式变得笨拙:

    • 在不同嵌套深度的多个子表达式中使用的中间结果
    • 在子表达式中深度嵌套使用的最外层函数的参数
    • 尴尬或不一致的参数顺序,即需要将函数部分应用于除第一个参数以外的其他内容
    • 基于中间结果的深度嵌入流控制,分支之间共享子表达式
    • 中间结果的模式匹配,特别是在提取部分结果的情况下,将其用于进一步计算,然后重建修改后的版本作为下一个结果

    将这样的函数编写为单个表达式通常需要多个嵌套的 lambda,或者是那种荒谬的混淆废话,使无点样式名声不好。另一方面,do 块提供语法糖,以便通过嵌入式控制流轻松嵌套中间结果范围。

    通常你可能会提取这样的子表达式并将它们放在where 子句或其他东西中,但由于普通值形成一个函数应用程序为(>>=) 的单子——即Identity 单子——你可以想象而是在 do 块中编写这样的函数,尽管人们可能会觉得你很有趣。


    除了范围/绑定的东西,do 块为您做的另一件事是省略将子表达式链接在一起的运算符。不难想象在其他情况下,如果有一个“在这个块中使用这个函数组合这些表达式”的符号会很好,然后让编译器填补空白。

    在简单的情况下,所有表达式都具有相同的类型,将它们放在一个列表中然后折叠它可以很好地工作——例如,使用unwords 和unlines 以这种方式构建字符串。 do 的好处是它结合了具有通用结构和兼容(但不完全相同)类型的表达式。

    事实上,Applicative 论文中的“惯用括号”表示法也有同样的一般原则:do 块使用换行符来省略一元构造,惯用括号使用并置来省略提升的函数应用程序。 Arrow 的 proc 符号也类似,其他概念也可以用这种方式清晰地表达,例如:

    • 组合数据结构,例如合并某种结果集,省略合并功能
    • 其他函数应用习惯用法,例如参数优先的“正向管道”样式,省略了应用运算符
    • 并行计算,省略结果聚合函数

    虽然将其中的许多变成单一类型或完整的Monad 实例并不难,但为一般概念提供统一的、可扩展的语法糖位可能会很好。肯定有一个共同的线索将所有这些以及更多内容联系在一起,但这是一个与语法无关的更大主题......

    【讨论】:

      【解决方案3】:

      do 符号基本上是一种表示“根据需要转换为 lambda 并 在两行之间分配>>=”。

      当很明显使用什么运算符来汇总所有内容时,最好省略并 利用“换行符”运算符。

      可编程换行将是处理材料列表、应用链等的好方法。要制作列表,您还需要一个“可编程凹痕”。真的,你可以拿三个 有意义的位并使它们都可重载:

      • do 的开头。
      • 介于dos 之间。
      • do 结束。

      那么你可能不应该再叫它do了。也许它应该只是一个括号。

      【讨论】:

        【解决方案4】:

        成语方括号是考虑应用程序的一种很好的方式,但它们并不是唯一可能的此类语法扩展。

        Philippa Cowderoy 不久前向 haskell-cafe 发布了一个 "Applicative do" 表示法的提案,并观察到任何看起来有点像的函数:

        foo = do
            x <- bar
            y <- baz
            quux y 1234 x
        

        &lt;- 绑定的变量只出现在最后一行可以用 Applicative 实现——我实际上在方案中为此实现了一个基于语法规则的宏,我称之为“ado”。

        这在应用效果的顺序与“自然顺序”不同的情况下很有用,并且假设 Haskell 中存在 'ado' 只会使以下内容脱糖:

        foo = (\x y -> quux y 1234 x) <*> bar <*> baz
        

        但是,词法作用域规则有点令人不安。

        【讨论】:

          【解决方案5】:

          Applicative 有(更有限,更紧凑)成语括号,请参阅Applicative Programming with Effects,第 4 页。我相信,Conor McBride 的 Strathclyde Haskell 环境已经实现了这些。

          我不知道如何概括这些特殊的语法,但也许我没有给予足够的思考。

          【讨论】:

            【解决方案6】:

            BlazeHtml 使用的是do-notation,而实际上它只是一个Monoid(尽管包装为Monad 以便能够使用do)。

            因此,Monoids 的类似符号在那里会很有用。

            如果你看一下我的游戏代码"Defend The King",那么我也做了很多mconcating,就像 BlazeHtml 一样,我会从漂亮的语法中受益。

            【讨论】:

              【解决方案7】:

              有一个符合 do 表示法的单子泛化 - 参数化单子。请参阅 sigfpe 的Beyond Monads。示例用法:

              test1' = do put 1
                          x <- get
                          put (show x)
                          y <- get
                          return (x,y)
              

              这是一个“状态单子”,首先存储一个数字,然后是一个字符串。

              【讨论】:

                【解决方案8】:

                GHC 中有一个预处理器可以为 Arrows 执行此操作:http://www.haskell.org/ghc/docs/6.12.2/html/users_guide/arrow-notation.html

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2015-05-11
                  • 1970-01-01
                  相关资源
                  最近更新 更多