6

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

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两次计算。这个例子(如果可能的话)如何只使用绑定语法来表达?

我认为 usingControl.Monad不是解决方案,因为它在内部使用do块。

我认为可以使用箭头(使用&&&)来表达这一点——也许这是只有箭头可以用作monads 的概括的情况

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

4

2 回答 2

11

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

我希望你的例子的这个翻译能给你提示:

main = readFile "foo.txt" >>= \f ->
       (print $ "prefix " ++ f) >>
       (print $ f ++ " postfix")
于 2014-02-22T16:25:14.997 回答
8

报告给出了从 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}

省略号“...”代表编译器生成的错误消息,传递给失败,最好给出模式匹配失败位置的一些指示;函数 >>、>>= 和 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这种方式的情况下机械地翻译成代码。

于 2014-02-22T19:30:47.473 回答