2012-08-18 23 views
6

Soy nuevo en Haskell, y trato de entender cómo hacer IO correctamente.Haskell IO: No se pudo encontrar el tipo esperado `IO a0 'con el tipo real

las siguientes obras: ok

main = do 
    action <- cmdParser 
    putStrLn "Username to add to the password manager:" 
    username <- getLine 
    case action of 
    Add -> persist entry 
     where 
     entry = Entry username "somepassword" 

Considerando que los siguientes resultados en error de compilación:

main = do 
    action <- cmdParser 
    case action of 
    Add -> persist entry 
     where 
     entry = Entry promptUsername "somepassword" 

promptUsername = do 
    putStrLn "Username to add to the password manager:" 
    username <- getLine 

el error está aquí:

Couldn't match expected type `IO b0' with actual type `[Char]' 
Expected type: IO b0 
    Actual type: String 
In the expression: username 
[...] 

lo que está pasando aquí? ¿Por qué funciona la primera versión, mientras que la segunda no?

Sé que en Stack Overflow hay algunas preguntas similares a esta, pero ninguna de ellas me explicó este problema.

Respuesta

8

username es un String, pero promptUsername es un IO String. Necesita hacer algo como:

username <- promptUsername 
let entry = Entry username "somepassword" 
persist entry 
+5

Voy a expandir. Esto es notación de do confunde uno. El código como 'do {a; b <- c; d b}' es en realidad una abreviatura de 'a >> = \ _ -> c >> = \ b -> d b'. Los que vienen del mundo imperativo piensan en '<-' aquí como una especie de operador de asignación. No lo es. Cada línea en notación do se traduce en función anónima, y ​​'<-' marca el argumento de dicha función. Se debe animar a alguien a leer sobre las mónadas y su aplicación práctica para familiarizarse con ellas. – permeakra

+0

Pude entender el problema y la solución sin conocimiento de mónadas. –

0

Aquí hay otra variante.

main = do 
    action <- cmdParser 
    case action of 
    Add -> do username <- promptUsername 
       let entry = Entry username "somepassword" 
       persist entry 

promptUsername :: IO String 
promptUsername = do 
    putStrLn "Username to add to the password manager:" 
    getLine 

-- fake definitions to make things compile 

persist :: Entry -> IO() 
persist = print 

cmdParser :: IO Add 
cmdParser = fmap (const Add) getLine 

data Add = Add deriving Show 
data Entry = Entry String String deriving Show 

El problema es simplemente que es un promptUsername acción no es una cadena. La acción 'devuelve una cadena', por lo que tiene el tipo IO String, pero en sí misma no es como una cadena. Como Entry x y requiere un String en la posición x, algo en forma de acción no podría encajar allí más que un número o un booleano. Por lo tanto, al definir su acción compleja, main, debe 'extraer' la cadena que resultará de la acción más simple promptUsername en cualquier caso de ejecución, y proporcionar el String como primer argumento de la entrada. A continuación, realice la acción persist en el resultado Entry

+0

Gracias! Su punto también se explica en http://learnyouahaskell.com/input-and-output. ¡En general, encuentro Learn You a Haskell para Great Good! más fácil de entender que Real World Haskell (http://book.realworldhaskell.org/). –

Cuestiones relacionadas