Этот код (взят из Learn You A Haskell ):
main = do putStr "Hey, "
putStr "I'm "
putStrLn "Andy!"
очевидно, десугары до
main = putStr "Hey, " >>=
(\_ -> putStr "I'm " >>=
(\_ -> putStrLn "Andy!"))
Который, как я понимаю, может быть истолкован как "Энди! Для того, чтобы положить StrLn"! Мне сначала нужно положить "Я", а для этого мне сначала нужно положить "Эй";
Я не согласен с этой интерпретацией, которая раздражает, потому что компилятор, очевидно, не делает и оставляет меня в замешательстве. Проблема, с которой я столкнулся, заключается в том, что лямбды игнорируют свои аргументы, во время ленивых вычислений разве такого рода вещи не должны распознаваться и замыкаться?
Также, конечно, привязка возвращает действие IO, и когда это действие IO попадает в main, оно выполняется. Но что мешает ему напечатать "Эй, Энди! Я"? Я подозреваю, что это то, что делает привязка.
Кроме того, как действие ввода-вывода типа "IO ()" несет достаточно информации, чтобы система выполнения могла вывести "Эй, я, Энди!"? Чем этот IO () отличается от IO (), чем печатает «Hello World!» или пишет в файл?
Рассмотрим другой, со страницы Википедии для монады:
Версия с сахаром:
do
putStrLn "What is your name?"
name <- getLine
putStrLn ("Nice to meet you, " ++ name ++ "!")
Версия Desugared:
putStrLn "What is your name?" >>=
(\_ ->
getLine >>=
(\name ->
putStrLn ("Nice to meet you, " ++ name ++ "!")))
Подобная история здесь.
Я думаю, мне просто нужно увидеть определение bind для IO, и тогда все будет ясно. Еще кое-что, что могло бы помочь, - это если бы кто-то мог помочь мне пройти через то, как программа действительно оценивается, и определить точные моменты, когда возникают побочные эффекты.