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

时间:2014-02-22 16:14:03

标签: haskell scope monads do-notation

据我所知,Haskell中的do块只是monadic绑定运算符的某种语法糖。例如,可以转换

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

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

是否可以将所有do块转换为绑定语法?例如,多次使用f的块:

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

假设我们在IO monad中,则不可能简单地执行两次readFile计算。该示例(如果可能的话)如何仅使用绑定语法表达?

我认为使用Control.Monad无法解决问题,因为它内部使用do块。

我认为可以使用箭头表示这一点(使用&&&) - 也许这种情况下只有箭头可以用作 monads的推广

请注意,这个问题不是关于上面的特殊示例,而是关于在print等一元表达式中多次使用计算结果的一般情况。

2 个答案:

答案 0 :(得分:11)

是的,所有这些都可以转换为绑定语法;实际上,它们是由编译器在内部转换的。

我希望您的示例的翻译为您提供提示:

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

答案 1 :(得分:7)

Report提供了从do语法到内核Haskell的完整翻译:

  

在消除空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}
     

省略号“...”代表编译器生成的错误消息,传递给失败,最好给出一些模式匹配失败位置的指示;函数&gt;&gt;,&gt;&gt; =和fail是Monad类中的操作,如Prelude中所定义;而ok是一个新的标识符。

所以你的例子就是这样翻译的:

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

此版本没有do块,但看起来不太自然。但我们可以应用等式推理和我们所知道的任何优化。因此,例如,观察ok _ = fail "..."子句是死代码,我们可以像ok那样:

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

所有do块都可以通过这种方式机械转换为不含do的代码。