2012-08-18 22 views
6

Ich bin neu in Haskell, und ich versuche zu verstehen, wie IO richtig zu tun ist.Haskell IO: Konnte den erwarteten Typ `IO a0 'mit dem tatsächlichen Typ nicht übereinstimmen

Folgende Arbeiten 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" 

Während die folgenden Ergebnisse in Kompilierungsfehler:

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 

Der Fehler ist hier:

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

Was ist hier los? Warum funktioniert die erste Version, während die zweite nicht?

Ich weiß, dass in Stack Overflow gibt es ein paar ähnliche Fragen wie diese, aber keiner von ihnen schien mir dieses Problem zu erklären.

Antwort

8

username ist ein String, aber promptUsername ist ein IO String. Sie tun müssen, um so etwas wie:

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

Ich werde erweitern. Dies ist eine Do-Notation, die eines verwirrt. Code wie 'do {a; b <- c; d b}' ist eigentlich eine Abkürzung für 'a >> = \ _ -> c >> = \ b -> d b'. Diejenigen, die aus der imperativen Welt kommen, denken hier an "<" als eine Art Zuweisungsoperator. Es ist nicht. Jede Zeile in Do-Notation wird in anonyme Funktion übersetzt und '<-' markiert das Argument einer solchen Funktion. Man sollte dringend ermutigt werden, über Monaden und ihre praktische Anwendung zu lesen, um sich mit ihnen vertraut zu machen. – permeakra

+0

Ich war tatsächlich in der Lage, das Problem und die Lösung ohne Wissen über Monaden zu verstehen. –

0

Hier ist eine weitere 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 

Das Problem ist nur, dass promptUsername eine Aktion ist kein String. Die Aktion 'gibt einen String zurück', also hat sie den Typ IO String, aber sie ist selbst nichts wie ein String. Da Entry x y einen String in der x-Position benötigt, könnte etwas in der Form einer Aktion nicht mehr passen als eine Zahl oder Boolean könnte. Also bei der Definition Ihrer komplexen Aktion, main, müssen Sie die Zeichenfolge extrahieren, die aus der einfacheren Aktion promptUsername in jedem Fall der Ausführung resultiert, und geben Sie das String als das erste Argument den Eintrag. Dann machen Sie die persist Aktion auf die resultierende Entry

+0

Danke! Ihr Punkt wird auch in http://learnyouahaskell.com/input-and-output erklärt. Im Allgemeinen finde ich, dass du ein Haskell für großes gutes lernst! einfacher zu verstehen als Real World Haskell (http://book.realworldhaskell.org/). –

Verwandte Themen