据我所知,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
等一元表达式中多次使用计算结果的一般情况。
答案 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
的代码。