简体   繁体   English

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

[英]Haskell: Would “do” notation be useful for contexts other than monads?

We all love do , and I was curious if perhaps this sort of alternate syntax would theoretically be useful outside of the monad world. 我们都喜欢do ,我很好奇,如果这种替代语法在理论上可能在monad世界之外有用。 If so, what other sorts of computations would it simplify? 如果是这样,它会简化哪些其他类型的计算? Would it make sense to have something equivalent for Applicative, for example? 例如,为Applicative提供相同的东西是否有意义?

My sense is that many Haskell programmers don't love do at all, and one of the common arguments in favor of using Applicative when you don't need the full power of Monad is that the combinators <$> , <*> , etc. allow a very clear and concise coding style. 我的感觉是许多Haskell程序员根本不喜欢do ,并且当你不需要Monad的全部功能时,支持使用Applicative一个常见论点是组合器<$><*>等允许非常清晰简洁的编码风格。

Even for monadic code, many people prefer using =<< explicitly instead of do notation. 即使是一元的代码,很多人喜欢使用=<<明确,而不是do记号。 camccann 's answer to your previous question about <*> gives a fantastic argument for this preference. camccann 对你之前关于<*>问题回答给出了这个偏好的奇妙论据。

I tend to write my first drafts using do and then replace with combinators as I revise. 我倾向于使用do编写我的第一个草稿,然后在我修改时用组合器替换。 This is just a matter of my own (in)experience and taste: it's often easiest for me to sketch things out in a more imperative fashion (which is more convenient with do ), but I think non- do code is usually prettier. 这只是我自己的(中)的经验和品味的问题:通常最容易为我勾画的事情了更迫切的方式(这是更方便do ),但我觉得非do代码通常是漂亮。

For arrows, on the other hand, I can't imagine not using proc and command do . 对于箭头,在另一方面,我不能没有使用想象proc命令do The tuples just get so ugly so quickly. 元组刚刚变得如此丑陋。

It might help to consider, regarding do notation itself, what it's actually good for. 它可能有助于考虑,就do记号本身,就是它的功能确实不错。 As Travis Brown points out, I've previously advocated the use of a "function application" style with Monad s and related types, but there's a flip side to that as well: Some expressions simply can't be written cleanly in direct function application style . 正如特拉维斯·布朗指出的那样,我之前曾提倡使用Monad和相关类型的“函数应用程序”样式,但也有另一面: 有些表达式根本无法在直接函数应用程序中写得干净利落风格 For instance, the following can quickly make applicative style clumsy: 例如,以下内容可以迅速使应用风格笨拙:

  • Intermediate results used in multiple subexpressions, at different depths of nesting 中间结果用于多个子表达式,在不同的嵌套深度
  • Arguments to the outermost function used deeply-nested in subexpressions 最外层函数的参数深度嵌套在子表达式中
  • Awkward or inconsistent argument order, ie needing to partially apply a function to something other than its first argument 尴尬或不一致的参数顺序,即需要将函数部分地应用于除第一个参数之外的其他函数
  • Deeply embedded flow control based on intermediate results, with shared subexpressions between branches 基于中间结果的深度嵌入式流控制,分支之间共享子表达式
  • Pattern matching on intermediate results, particularly in the case of extracting part of a result, using that for further computation, then reconstructing a modified version as the next result 中间结果上的模式匹配,特别是在提取结果的一部分的情况下,使用它进行进一步计算,然后将修改后的版本重建为下一个结果

Writing such a function as a single expression generally requires either multiple nested lambdas, or the kind of absurd obfuscating nonsense that gives point-free style a bad name. 将这样的函数编写为单个表达式通常需要多个嵌套的lambdas,或者是一种荒谬的混淆废话,它使无点样式成为坏名称。 A do block, on the other hand, provides syntactic sugar for easy nested scoping of intermediate results with embedded control flow. 另一方面, do块提供语法糖,以便通过嵌入式控制流轻松嵌套中间结果的范围。

Normally you'd probably extract such subexpressions and put them in a where clause or something, but since ordinary values form a monad with function application as (>>=) --namely the Identity monad--you could conceivably write such a function in a do block instead, though people might look at you funny. 通常你可能会提取这样的子表达式并将它们放在where子句或其他东西中,但是由于普通值与函数应用程序形成一个monad (>>=) - 也就是Identity monad - 你可以想象写下这样一个函数一个do阻止相反,尽管人们可能会看着你笑。


Besides the scoping/binding stuff, the other thing a do block does for you is elide the operator that chains subexpressions together. 除了作用域/绑定之外, do块为你do的另一件事就是将子表达式链接在一起的运算符。 It's not too hard to imagine other cases where it would be nice to have a notation for "combine these expressions using this function within this block", and then let the compiler fill in the blanks. 想象一下“在这个块中使用这个函数组合这些表达式”,然后让编译器填入空白的其他情况并不难。

In the easy case, where the expressions all have the same type, putting them in a list and then folding it works well--building strings in this manner using unwords and unlines , for instance. 在简单的情况下,表达式都具有相同的类型,将它们放在一个列表然后折叠它也很有效 - 例如,使用unwordsunlines以这种方式构建字符串。 The benefit of do is that it combines expressions with common structure and compatible--but not identical--types. do的好处是它将表达式与通用结构和兼容(但不完全相同)类型相结合。

In fact, the same general principle is true of the "idiom bracket" notation from the Applicative paper: Where do blocks use newlines to elide monadic construction, idiom brackets use juxtaposition to elide lifted function application. 事实上,同样的总的原则是从的“成语支架”符号的真正Applicative纸:在哪里do块使用新行的Elid到一元结构,成语支架利用并列提起的Elid功能应用。 The proc notation for Arrow is also similar, and other concepts could be cleanly expressed in such fashion as well, such as: Arrowproc符号也类似,其他概念也可以用这种方式干净地表达,例如:

  • Composing data structures, eg merging result sets of some sort, eliding the merge function 组合数据结构,例如合并某种类型的结果集,省略合并功能
  • Other function application idioms, such as argument-first "forward pipe" style, eliding the application operator 其他函数应用程序习惯用法,例如参数优先“前向管道”样式,忽略了应用程序运算符
  • Parallel computations, eliding the result aggregation function 并行计算,省略结果聚合函数

Although it's not too hard to make many of these into either a single type or a full Monad instance, it might be nice to have a unified, extensible bit of syntactic sugar for the general concept. 虽然将这些中的许多变成单个类型或完整的Monad实例并不太难,但对于一般概念来说,拥有一个统一的,可扩展的语法糖可能会很好。 There's certainly a common thread tying together all these and more, but that's a much larger topic not really related to syntax... 肯定有一个共同的线程将所有这些和更多的东西捆绑在一起,但这是一个与语法无关的更大的话题......

The do notation is basically a way of saying "transform to lambdas as needed and distribute >>= between the lines". do记号基本上是说:“根据需要转化为lambda表达式和分发的方式>>=字里行间”。

When it's obvious what operator is being used to roll everything up, it's nice to omit and leverage the "newline" operator. 当显而易见的是什么运算符用于推动所有事情时,省略并利用“换行符”运算符是很好的。

Programmable newline would be nice way to approach makings list, applicative chains, &c. 可编程换行将是处理材料列表,应用链等的好方法。 To make lists, you'd also need a "programmable outdent". 要制作列表,您还需要一个“可编程的外部”。 Really, you could just take the three meaningful bits and make them all overloadable: 实际上,您可以只取三个有意义的位并使它们全部可重载:

  • Begin of do . 开始do
  • In between do s. 在两者之间do
  • End of do . 结束do

Then you probably shouldn't call it do anymore. 然后,你可能不应该把它do了。 Maybe it should just be a bracket. 也许它应该只是一个支架。

Idiom brackets form one nice way to think about Applicatives, but they aren't the only possible such syntax extension. 成语括号构成了考虑Applicatives的一种不错的方式,但它们并不是唯一可能的语法扩展。

Philippa Cowderoy posted a proposal for an "Applicative do" notation to the haskell-cafe a while back with the observation that any function that looks kind of like: 菲利普·考德罗伊(Philippa Cowderoy)在一段时间后向haskell-cafe发布了一个“Applicative do”符号的提议,观察到任何类似的函数:

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

where the variables bound by <- only occur in the last line could be implemented with Applicative -- I actually implemented a syntax-rule based macro for this in scheme, which I called 'ado'. 其中<-唯一出现的变量出现在最后一行可以使用Applicative实现 - 我实际上在方案中实现了一个基于语法规则的宏,我称之为'ado'。

This is useful in the cases where the order of applicative effects differs from the "natural order" and assuming the existence of 'ado' in Haskell would just desugar to: 这对于应用效果的顺序与“自然顺序”不同并且假设Haskell中存在'ado'只会出于以下情况很有用:

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

However, the lexical scoping rules are a bit disconcerting. 但是,词汇范围规则有点令人不安。

Applicative has (much more limited, and more compact) Idiom Brackets, see Applicative Programming with Effects , page 4. Conor McBride's Strathclyde Haskell Environment has implemented these, I believe. Applicative有(更有限,更紧凑)成语括号,请参阅应用程序编程与效果 ,第4页。我相信Conor McBride的Strathclyde Haskell环境实现了这些。

I don't see how to generalize these kinds of special syntaxes, but maybe I haven't given it enough thought. 我不知道如何推广这些特殊的语法,但也许我没有给它足够的思考。

BlazeHtml is using do -notation when actually it's just a Monoid (though wrapped as a Monad to be able to use do ). BlazeHtml使用do -notation实际上它只是一个Monoid (虽然包裹为Monad以便能够使用do )。

So a similar notation for Monoid s would be useful there. 因此Monoid s的类似符号在那里很有用。

If you look at the code of my game "Defend The King" , then I also do a lot of mconcat ing, and like BlazeHtml, I would benefit from a nice-looking syntax for that. 如果你看看我的游戏“保卫国王”的代码,那么我也做了很多mconcat ,就像BlazeHtml一样,我会受益于一个漂亮的语法。

There's a generalization of monads that fits with do notation - parametrized monads. 还有适合有单子的推广do记号-参数化的单子。 See Beyond Monads by sigfpe. 通过sigfpe查看Beyond Monads。 Example usage: 用法示例:

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

This is a "state monad" that stores first a number, and then a string. 这是一个“状态monad”,它首先存储一个数字,然后是一个字符串。

GHC中有一个预处理器可以为Arrows做到这一点: http//www.haskell.org/ghc/docs/6.12.2/html/users_guide/arrow-notation.html

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM