Alvaro Marques
Alvaro Marques

Reputation: 80

"Couldn't match type ‘[]’ with ‘IO’" error in Haskell

I am trying to make a list with Points (a datatype that I created), the idea is add an element in each iteration. Something is wrong.

I have tried to put p out of myLoop but it doesn't seems to work either.

main = myLoop 
myLoop  = do 
            let p = []
            done <- isEOF
            if done
              then putStrLn ""
              else do inp <- getLine
                      let (label:coord) = words inp
                      p ++  [Point label (map getFloat coord)]
                      -- print (pointerList)
                      myLoop 

I am getting this output

trabalho.hs:30:23: error:
    • Couldn't match type ‘[]’ with ‘IO’
      Expected type: IO Point
        Actual type: [Point]
    • In a stmt of a 'do' block:
        p ++ [Point label (map getFloat coord)]
      In the expression:
        do inp <- getLine
           let (label : coord) = words inp
           p ++ [Point label (map getFloat coord)]
           myLoop
      In a stmt of a 'do' block:
        if done then
            putStrLn ""
        else
            do inp <- getLine
               let (label : coord) = ...
               p ++ [Point label (map getFloat coord)]
               ....
   |
30 |                       p ++  [Point label (map getFloat coord)]
   |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Upvotes: 0

Views: 708

Answers (1)

Del
Del

Reputation: 1309

First of all, do-notation is syntactic sugar with the following rules:

do x <- mA
   mB

roughly* desugars to mA >>= \x -> do mB, where mA has type Monad m => m a and do mB has type Monad m => m b for some types m, a, and b.

do mA
   mB

desugars to mA >> do mB, where mA has type Monad m => m a and do mB has type Monad m => m b for some types m, a, and b.

do a

desugars to a.

main is a special name that represents the entrypoint of the program, and it has type IO a for some type a. Because you define main = myLoop, myLoop must also have type IO a.

Therefore, in your myLoop function:

myLoop  = do 
            let p = []
            done <- isEOF
            if done
              then putStrLn ""
              else do inp <- getLine
                      let (label:coord) = words inp
                      p ++  [Point label (map getFloat coord)]
                      -- print (pointerList)
                      myLoop 

the do-block is working with the type m = IO. Therefore, when you write p ++ [Point label (map getFloat coord)], the typechecker expects a value of type IO c, for some type c.

However, p ++ [Point label (map getFloat coord)] has type [Point], resulting in the type error Cannot match type '[]' with 'IO'.

As the type mismatch indicates, your code does not make sense. I assume that you want to append Point label (map getFloat coord) to p. ++ does not mutate p; it creates a new list! Idiomatic Haskell uses recursion to achieve what you want. The most direct way of fixing your code would be to do the following:

main = myLoop []
myLoop p = do
    done <- isEOF
    if done
    then putStrLn ""
    else do inp <- getLine 
            let (label:coord) = words inp
            let p' = p ++  [Point label (map getFloat coord)]
            myLoop  p'

Here, myLoop takes p as a parameter and recursively passes an updated p to itself after reading input. main calls myLoop with an argument of [], which is the initial value of p.

The Haskell Wikibook has a good explanation of do-notation. In general, I would recommend reading the Haskell Wikibook to better understand Haskell.

*I say "roughly" because these aren't the exact rules. The Wikibook article explains do-notation in depth.

Upvotes: 5

Related Questions