2012-08-18 20 views
6

Sono nuovo ad Haskell e cerco di capire come eseguire correttamente l'IO.I/O Haskell: Impossibile corrispondere al tipo previsto `IO a0 'con il tipo effettivo

le seguenti opere: 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 che i seguenti risultati in errore di compilazione:

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 

L'errore è qui:

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

quello che sta succedendo qui? Perché la prima versione funziona, mentre la seconda non funziona?

So che in Stack Overflow ci sono alcune domande simili come questa, ma nessuna di queste sembrava spiegarmi questo problema.

risposta

8

username è un String, ma promptUsername è un IO String. Devi fare qualcosa del tipo:

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

Mi espanderò. Questa è do-notation che confonde uno. Il codice come 'do {a; b <- c; d b}' è in realtà una scorciatoia per 'a >> = \ _ -> c >> = \ b -> d b'. Quelli che vengono dal mondo imperativo pensano a '<-' qui come a un tipo di operatore di incarichi. Non è. Ogni riga nella notazione si traduce in una funzione anonima e '<-' contrassegna l'argomento di tale funzione. Si dovrebbe essere fortemente incoraggiati a leggere le monadi e la loro applicazione pratica per familiarizzare con loro. – permeakra

+0

In realtà ero in grado di capire il problema e la soluzione senza conoscenza delle monadi. –

0

Ecco un'altra 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 

Il problema è proprio questo promptUsername è un azione non una stringa. L'azione "restituisce una stringa", quindi ha il tipo IO String, ma non è di per sé una stringa. Poiché Entry x y richiede un String nella posizione x, qualcosa nella forma di un'azione non potrebbe essere più adatto di un numero o di un valore booleano. Quindi, nel definire la tua azione complessa, main, devi "estrarre" la stringa risultante dall'azione più semplice promptUsername in ogni caso di esecuzione e dare il String come primo argomento alla voce. Quindi esegui l'azione persist sul risultato Entry

+0

Grazie! Il tuo punto è anche spiegato in http://learnyouahaskell.com/input-and-output. In generale, trovo Learn You a Haskell per Great Good! più facile da capire rispetto a Real World Haskell (http://book.realworldhaskell.org/). –

Problemi correlati