Haskell:除了monad之外,"do"符号对于上下文有用吗?

J C*_*per 10 syntax haskell

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

Tra*_*own 14

我的感觉是,很多Haskell新手不爱do可言,而赞成使用共同的论据之一Applicative,当你不需要的全部力量Monad在于组合子<$>,<*>等允许一个非常清晰和简洁的代码风格.

即使对于monadic代码,许多人更喜欢=<<明确使用而不是使用do符号.camccann 对你之前的问题<*>回答为这种偏好提供了一个很好的论据.

我倾向于使用我的第一个草稿do,然后在修改时用组合器替换.这只是我自己(经验)和品味的问题:我通常最容易以更迫切的方式绘制事物(这更方便do),但我认为非do代码通常更漂亮.

另一方面,对于箭头,我无法想象不使用proc命令do.元组刚刚变得如此丑陋.


C. *_*ann 9

关于do符号本身,它可能有助于考虑它实际上有什么用处.正如特拉维斯·布朗指出的那样,我之前曾主张使用带有Monads和相关类型的"函数应用程序"样式,但也有另一面:有些表达式根本无法直接编写为直接函数应用程序样式.例如,以下内容可以迅速使应用风格笨拙:

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

将这样的函数编写为单个表达式通常需要多个嵌套的lambdas,或者是一种荒谬的混淆废话,它使无点样式成为坏名称.阿do块,在另一方面,提供了一种用于与嵌入式控制流的中间结果容易嵌套作用域语法糖.

通常你可能会提取这样的子表达式并把它们放在一个where子句或者其他东西中,但是由于普通的值形成了一个带有函数应用程序(>>=)Identitymonad - 就像monad一样 - 你可以想象在一个do块中编写这样一个函数,尽管人们可能会看着你好笑.


除了作用域/绑定之外,do块为你做的另一件事就是将子表达式链接在一起的运算符.想象一下"在这个块中使用这个函数组合这些表达式",然后让编译器填入空白的其他情况并不难.

在简单的情况下,如果表达式都具有相同的类型,把它们放在一个列表,然后折叠它工作得很好-建筑的字符串使用这种方式unwords,并unlines为实例.它的好处do是它将表达式与通用结构和兼容(但不完全相同)类型相结合.

事实上,同样的一般原则也适用于论文中的"成语括号"符号Applicative:凡do块使用换行符来消除monadic构造,成语括号使用并置来消除提升函数的应用.该proc音乐符号Arrow也是类似的,和其他概念可以在这种方式来表达干净为好,如:

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

虽然将这些中的许多变成单个类型或完整Monad实例并不太难,但对于一般概念来说,拥有一个统一的,可扩展的语法糖可能会很好.肯定有一个共同的线程将所有这些和更多的东西捆绑在一起,但这是一个与语法无关的更大的话题......


sol*_*ack 7

do符号基本上是说:"根据需要转化为lambda表达式和分发的方式>>=字里行间".

当显而易见的是什么运算符用于推动所有事情时,省略并利用"换行符"运算符是很好的.

可编程换行将是处理材料列表,应用链等的好方法.要制作列表,您还需要一个"可编程的外部".实际上,您可以只取三个有意义的位并使它们全部可重载:

  • 开始do.
  • 介于两者之间do.
  • 结束了do.

然后你可能不应该再打电话do了.也许它应该只是一个支架.


Edw*_*ETT 5

成语括号构成了考虑Applicatives的一种不错的方式,但它们并不是唯一可能的语法扩展.

菲利普·考德罗伊(Philippa Cowderoy)在一段时间后向haskell-cafe 发布了一个"Applicative do"符号的提议,观察到任何类似的函数:

foo = do
    x <- bar
    y <- baz
    quux y 1234 x
Run Code Online (Sandbox Code Playgroud)

其中<-仅在最后一行中出现的变量可以使用Applicative实现 - 我实际上在方案中为此实现了一个基于语法规则的宏,我称之为"ado".

这对于应用效果的顺序与"自然顺序"不同并且假设Haskell中存在'ado'只会出于以下情况很有用:

foo = (\x y -> quux y 1234 x) <*> bar <*> baz
Run Code Online (Sandbox Code Playgroud)

但是,词汇范围规则有点令人不安.