Lauri Lehmijoki
Lauri Lehmijoki

Reputation: 337

Haskell IO: Couldn't match expected type `IO a0' with actual type

I am new to Haskell, and I try to understand how to do IO correctly.

The following works 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"

Whereas the following results in compilation error:

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

The error is here:

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

What is going on here? Why the first version works, while the second one does not?

I know that in Stack Overflow there are a few similar questions like this, but none of them seemed to explain this problem to me.

Upvotes: 6

Views: 21844

Answers (2)

applicative
applicative

Reputation: 8199

Here's another variant.

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

The problem is just that promptUsername is an action not a String. The action 'returns a String', so it has type IO String, but it is itself nothing like a String. Since Entry x y requires a String in the x position, something in the shape of an action could no more fit there than a number or boolean could. So in defining your complex action, main, you must 'extract' the string that will result from the simpler action promptUsername in any case of execution, and give the String as the first argument the entry. Then you do the persist action on the resulting Entry

Upvotes: 0

dan_waterworth
dan_waterworth

Reputation: 6441

username is a String, but promptUsername is an IO String. You need to do something like:

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

Upvotes: 10

Related Questions