Expressing do block using only monadic bind syntax
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 expressions satisfy these identities, which may be used as a translation into the kernel, after eliminating empty 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; and ok is a fresh identifier.
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. 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:
=
readFile "foo.txt" >>= \f ->
(print $ "prefix " ++ f) >>
(print $ f ++ " postfix")
All do
blocks can be mechanically translated into code without do
in this way.