2012-08-18 3 views
6

Я новичок в Haskell, и я стараюсь понять, как правильно выполнить IO.Haskell IO: Не удалось совместить ожидаемый тип `IO a0 'с фактическим типом

Следующие работает нормально:

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" 

Принимая во внимание следующие результаты в ошибке компиляции:

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 

Ошибка находится здесь:

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

Что здесь происходит? Почему работает первая версия, а вторая - нет?

Я знаю, что в переполнении стека есть несколько подобных вопросов, подобных этому, но ни один из них, казалось, не объяснил мне эту проблему.

ответ

8

username является String, но promptUsername является IO String. Вам необходимо сделать что-то вроде:

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

Я расширю. Это недоразумение. Код типа 'do {a; b <- c; d b}' на самом деле является сокращением для 'a >> = \ _ -> c >> = \ b -> d b'. Те, кто пришел из императивного мира, думают о '<-' здесь как о своем назначении. Это не. Каждая строка в do-notation преобразуется в анонимную функцию, а '<-' отмечает аргумент такой функции. Нужно настоятельно рекомендовать читать о монадах и их практическом применении, чтобы ознакомиться с ними. – permeakra

+0

Я действительно смог понять проблему и решение без знания монадов. –

0

Вот еще один вариант.

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 

Проблема заключается в том, что только promptUsername является действие не строка. Действие «возвращает строку», поэтому он имеет тип IO String, но сам по себе ничего не напоминает String. Так как Entry x y требует String в положении x, то что-то в форме действие больше не может быть подходящим, чем число или логическое значение. Поэтому при определении вашего сложного действия, main, вы должны «извлечь» строку, которая будет результатом более простого действия promptUsername в любом случае исполнения, и дать String в качестве первого аргумента записи. Затем вы делаете действие persist на результат Entry

+0

Спасибо! Ваша точка также объясняется в http://learnyouahaskell.com/input-and-output. В общем, я нахожу Learn You a Haskell для Великого Добра! легче понять, чем Real World Haskell (http://book.realworldhaskell.org/). –

Смежные вопросы