Почему Haskell не может быть обманут в выполнении операций ввода-вывода, используя строгую оценку?

Я просто изучаю монады Haskell и IO. Мне интересно, почему бы это не заставить программу выводить "привет", а также "пока":

second a b = b
main = print ((second $! ((print "hi") >>= (\r -> return ()))) "bye")

Насколько я понимаю, оператор $! заставил бы первый аргумент second быть оценен, а оператору >>= нужно было бы запустить print "hi", чтобы получить от него значение и передайте его на \r -> return (), который напечатает "hi" на экране.

Что не так с моими рассуждениями?

А также, есть ли способ пропустить Haskell нельзя обмануть (кроме использования небезопасных функций) в выполнение операций ввода-вывода внутри "безопасного" кода?

Ответы

Ответ 1

Выставляемое выражение ((print "hi") >>= (\r -> return ())), которое имеет тип IO (). Таким образом, он представляет собой операцию ввода-вывода. Но оценка такой вещи совсем не похожа на ее работу!

Оценка значения означает выполнение достаточно простых шагов, чтобы превратить его в так называемую слабую головную нормальную форму. Поскольку IO является абстрактным, немного сложно понять, что это значит в этом случае, но можно думать о IO a как RealWorld -> (a, RealWorld), а затем нормальная форма слабой головки - это, ну, функция, ожидающая с учетом RealWorld.

Выполнение подразумевает оценку, но также передает RealWorld в качестве аргумента, тем самым вызывая эффект IO.

Все это не очень специфично для IO; ваше замешательство и концепции в равной степени относятся к a -> b. Если вы понимаете, что делает $!, когда второй аргумент является функцией, вы поймете, что происходит, когда это действие IO.

Ответ 2

Вы путаете оценку с исполнением. Когда вы вызываете выражение типа print "hi", это ничего не выводит. Он просто производит значение (в данном случае значение типа IO()), представляющее действие печати чего-либо. Вы можете придумать значение print "hi" в качестве рецепта для печати "привет".