Это одна из сложнейших мелочей, которые смутили меня, когда я впервые попробовал Haskell. Вы неправильно понимаете смысл конструкции <-
в do-notation. result <- f handle
не означает «присваивает значение f handle
- result
»; это означает «привязать result
к значению« извлечено »из монадического значения f handle
» (где «извлечение» происходит каким-то образом, которое определяется конкретным экземпляром Monad, который вы используете, в этом случае монада IO).
т.е. в течение некоторого Monad м класса типов, то <-
утверждение принимает выражение типа m a
в правой стороне и переменную типа a
на левой стороне, и связывает переменную в значение. Таким образом, в вашем конкретном примере с result <- f handle
мы имеем типы f result :: IO a
, result :: a
и return result :: IO a
.
PS do-notation также имеет специальную форму let
(без ключа in
в данном случае!), Который работает как чистый аналог <-
. Таким образом, вы могли бы переписать пример как:
withFile' :: FilePath -> IOMode -> (Handle -> IO a) -> IO a
withFile' path mode f = do
handle <- openFile path mode
let result = f handle
hClose handle
result
В этом случае, поскольку let
является прямым назначением, типа result
является IO a
.
О, да! Полностью пропустил оператор _sucking_ '<-'! – drozzy
Могло ли это быть также 'let result = f handle; hClose handle; результат "или я снова не понял монады? – delnan
@delnan, который будет 'do {handle <- путь к файлу openFile; hClose handle; f ручка; } ', поэтому' f handle', вероятно, будет жаловаться на закрытую ручку. –