2011-12-19 2 views
5

После haskell tutorial, автор предлагает следующую реализацию метода withFile:Как withFile реализуется в Haskell

withFile' :: FilePath -> IOMode -> (Handle -> IO a) -> IO a 
withFile' path mode f = do 
    handle <- openFile path mode 
    result <- f handle 
    hClose handle 
    return result 

Но зачем нам нужно обернуть result в return? Не возвращает ли функция fIO, что видно по типу Handle -> IO a?

ответ

7

Вы правы: f уже возвращает IO, так что если функция была написана так:

withFile' path mode f = do 
    handle <- openFile path mode 
    f handle 

не было бы никакой необходимости в возвращении. Проблема заключается в том hClose handle идет между ними, так что мы должны сохранить результат первого:

result <- f handle 

и делать <- избавляется от IO. Итак, return возвращает его.

+0

О, да! Полностью пропустил оператор _sucking_ '<-'! – drozzy

+1

Могло ли это быть также 'let result = f handle; hClose handle; результат "или я снова не понял монады? – delnan

+3

@delnan, который будет 'do {handle <- путь к файлу openFile; hClose handle; f ручка; } ', поэтому' f handle', вероятно, будет жаловаться на закрытую ручку. –

3

Это одна из сложнейших мелочей, которые смутили меня, когда я впервые попробовал 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.

+1

Прохладный! Я называю '<-' оператором suck, так как он сосет значение из rhs :-) – drozzy

Смежные вопросы