2011-12-19 8 views
5

Tras una haskell tutorial, el autor proporciona la siguiente implementación del métodowithFile:¿Cómo se implementa withFile en 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 

Pero, ¿por qué necesitamos para envolver el result en un return? ¿La función suministrada f ya no devuelve un IO como se puede ver por su tipo Handle -> IO a?

Respuesta

7

Tienes razón: f ya se devuelve un IO, por lo que si la función se escribe así:

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

no habría necesidad de un cambio. El problema es hClose handle viene en el medio, así que tenemos que almacenar el resultado en primer lugar:

result <- f handle 

y haciendo <- se deshace de la IO. Entonces return lo devuelve.

+0

Oh d'oh! Completamente perdido el operador _sucking_ '<-'! – drozzy

+1

También podría ser 'let result = f handle; hCerrar el mango; ¿result? o no logré comprender la mónada nuevamente? – delnan

+3

@delnan que sería 'do {handle <- openFile mode path; hCerrar el mango; f manejar; } ', entonces' f handle' probablemente se quejaría de un identificador cerrado. –

3

Esta es una de las pequeñas cosas engañosas que me confundieron cuando probé Haskell por primera vez. Está malinterpretando el significado de la construcción <- en notación de Do. result <- f handle no significa "asignar el valor de f handle a result"; significa "enlazar result a un valor 'extraído' del valor monádico de f handle" (donde la 'extracción' ocurre de algún modo definido por la instancia particular de Monad que está utilizando, en este caso la mónima IO).

es decir, por alguna clase de tipos Monad m, la declaración <- toma una expresión de tipo m a en el lado derecho y una variable de tipo a en el lado de la mano izquierda, y se une la variable a un valor. Por lo tanto, en su ejemplo particular, con result <- f handle, tenemos los tipos f result :: IO a, result :: a y return result :: IO a.

PD-notación tiene también una forma especial de let (sin la palabra clave in en este caso!) Que funciona como contraparte a pura <-. Por lo que podría volver a escribir tu ejemplo como:

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

En este caso, debido a que el let es una tarea sencilla, el tipo de result es IO a.

+1

¡Genial! Llamo al '<-' el operador de suck, ya que absorbe el valor de los rhs :-) – drozzy