简体   繁体   English

仅使用monadic绑定语法表示阻止

[英]Expressing do block using only monadic bind syntax

As far as I know, do blocks in Haskell are just some kind of syntactic sugar for monadic bind operators. 据我所知, do在Haskell块都只是某种对一元的绑定运营商的语法糖。 For example, one could convert 例如,一个人可以转换

main = do f <- readFile "foo.txt"
          print f
          print "Finished"

to

main = readFile "foo.txt" >>= print >> print "Finished"

Can all do blocks be converted to bind syntax? 都可以do块转换的绑定语法? What about, for example, this block where f is used multiple times: 例如,这个块多次使用f

main = do f <- readFile "foo.txt"
          print $ "prefix " ++ f
          print $ f ++ " postfix"

Assuming we are in the IO monad, it is not possible to simply execute the readFile computation twice. 假设我们在IO monad中,则不可能简单地执行readFile计算两次。 How can this example (if possible at all) expressed using only bind syntax? 如何使用绑定语法表示此示例(如果可能的话)?

I think using Control.Monad is no solution, because it internally uses do blocks. 我认为使用Control.Monad不是解决方案,因为它在内部使用do块。

I think it it's possible to express this using arrows (using &&& ) -- maybe this is a case where only arrows can be used as a generalization of monads ? 我认为可以使用箭头表示这一点(使用&&& ) - 也许这种情况下只有箭头可以用作monads的推广

Note that this question is not about the special examples above but about the general case of the result of a computation being used multiple times in monadic expressions like print . 请注意,这个问题不是关于上面的特殊示例,而是关于计算结果在print等monadic表达式中多次使用的一般情况。

Yes, all of them can be converted to bind syntax; 是的,所有这些都可以转换为绑定语法; in fact, they are converted internally by the compiler. 实际上,它们是由编译器在内部转换的。

I hope this translation of your example gives you the hint: 我希望你的例子的这个翻译给你提示:

main = readFile "foo.txt" >>= \f ->
       (print $ "prefix " ++ f) >>
       (print $ f ++ " postfix")

The Report gives a full translation from do syntax into kernel Haskell: 报告提供了从do语法到内核Haskell的完整翻译:

Do expressions satisfy these identities, which may be used as a translation into the kernel, after eliminating empty stmts: 在消除空stmts之后,表达式是否满足这些标识,可以用作内核的转换:

 do {e} = e do {e;stmts} = e >> do {stmts} do {p <- e; stmts} = let ok p = do {stmts} ok _ = fail "..." in e >>= ok do {let decls; stmts} = let decls in do {stmts} 

The ellipsis "..." stands for a compiler-generated error message, passed to fail, preferably giving some indication of the location of the pattern-match failure; 省略号“...”代表编译器生成的错误消息,传递给失败,最好给出一些模式匹配失败位置的指示; the functions >>, >>=, and fail are operations in the class Monad, as defined in the Prelude; 函数>>,>> =和fail是Monad类中的操作,如Prelude中所定义; and ok is a fresh identifier. 而ok是一个新的标识符。

So your example translates this way: 所以你的例子就是这样翻译的:

do f <- readFile "foo.txt"
   print $ "prefix " ++ f
   print $ f ++ " postfix"
=
let ok f = do print $ "prefix " ++ f
              print $ f ++ " postfix"
    ok _ = fail "..."
in readFile "foo.txt" >>= ok
=
let ok f = (print $ "prefix " ++ f) >> do print $ f ++ " postfix"
    ok _ = fail "..."
in readFile "foo.txt" >>= ok
=
let ok f = (print $ "prefix " ++ f) >> (print $ f ++ " postfix")
    ok _ = fail "..."
in readFile "foo.txt" >>= ok

This version has no do blocks, but doesn't look very natural. 这个版本没有do块,但看起来不自然。 But we can apply equational reasoning and whatever optimizations we know. 但我们可以应用等式推理和我们所知道的任何优化。 So, for example, observing that the ok _ = fail "..." clause is dead code, we could inlike ok like so: 因此,例如,观察了ok _ = fail "..."子句死码,我们可以inlike ok ,像这样:

 =
 readFile "foo.txt" >>= \f ->
 (print $ "prefix " ++ f) >>
 (print $ f ++ " postfix")

All do blocks can be mechanically translated into code without do in this way. 所有do块可以机械地翻译成代码,而无需do这样。

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

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