То, что вы описали - частный случай.
Вот несколько частных случаев (хаскель vs псевдокод).
Обратите внимание, что на Хаскеле код линейный
То, что сделано монадами, в других языках нередко встроено. Удобство невстроенного решения в том, что есть немало функций, которые работают с любыми монадами (mapM, filterM, ...), применить которые к встроенным в язык вещам может быть проблематично.
1. nullable
do
x <- foo
y <- bar x
return $ baz y
вместо
x = foo();
if (x) {
y = bar();
if (y) {
return baz(y); } }
2. списки
do
x <- lines str
y <- words x
return $ length y
вместо
for (x in lines(str))
for (y in words(x))
yield y.length();
3. continuation
do
h1 <- ContT $ withFile "1.txt" ReadMode
h2 <- ContT $ withFile "2.txt" WriteMode
liftIO $ ...use handles...
вместо
using (h1 = File.Open("1.txt", File.Mode.Read)) {
using (h2 = File.Open("2.txt", File.Mode.Write)) {
...use files... }}
Причем можно, например, и так:
do
-- сразу список файлов открываем
hs <- mapM (\name -> ContT (withFile name ReadMode))
["1.txt", "2.txt", "3.txt", "4.txt"]
А как сделать так с using'ами?