问题描述:

Following a haskell tutorial, the author provides the following implementation of the withFile method:

withFile' :: FilePath -> IOMode -> (Handle -> IO a) -> IO a

withFile' path mode f = do

handle <- openFile path mode

result <- f handle

hClose handle

return result

But why do we need to wrap the result in a return? Doesn't the supplied function f already return an IO as can be seen by it's type Handle -> IO a?

网友答案:

You're right: f already returns an IO, so if the function were written like this:

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

there would be no need for a return. The problem is hClose handle comes in between, so we have to store the result first:

result <- f handle

and doing <- gets rid of the IO. So return puts it back.

网友答案:

This is one of the tricky little things that confused me when I first tried Haskell. You're misunderstanding the meaning of the <- construct in do-notation. result <- f handle doesn't mean "assign the value of f handle to result"; it means "bind result to a value 'extracted' from the monadic value of f handle" (where the 'extraction' happens in some way that's defined by the particular Monad instance that you're using, in this case the IO monad).

I.e., for some Monad typeclass m, the <- statement takes an expression of type m a in the right hand side and a variable of type a on the left hand side, and binds the variable to a value. Thus in your particular example, with result <- f handle, we have the types f result :: IO a, result :: a and return result :: IO a.

PS do-notation has also a special form of let (without the in keyword in this case!) that works as a pure counterpart to <-. So you could rewrite your example as:

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

In this case, because the let is a straightforward assignment, the type of result is IO a.

相关阅读:
Top