Search code examples
loopshaskellio

couldn´t match type 'IO´ with ´[]` inside a do


Haskell newbie here :) . I wan´t to read iteratively an element from IO and, in case is present, delete the element from the list . In other case, repeat the reading till the element entered is in the list. I have the following code :

import Data.List (delete)

main = do
let initialDieRoll = [23,45,98,34]
strength <- askStrength
let dieRollWithoutStrength = removeStrengthFromDieRollLoop strength initialDieRoll
print dieRollWithoutStrength

removeStrengthFromDieRollLoop :: Int -> [Int] -> [Int]
removeStrengthFromDieRollLoop = removeStrengthFromDieRoll

removeStrengthFromDieRoll :: Int -> [Int] -> [Int]
removeStrengthFromDieRoll strength dieRoll = do
if strength `elem` dieRoll then
  delete strength dieRoll
else do
  "Please, choose an element from the initial die roll"
  newStrength <- askStrength
  removeStrengthFromDieRollLoop askStrength dieRoll


askStrength :: IO Int
askStrength = do
putStr "Strength : "
readLn::IO Int

But , when I request the new strength, I'm getting the following error :

Couldn't match type ‘IO’ with ‘[]’
  Expected: [Int]
  Actual: IO Int
In a stmt of a 'do' block: newStrength <- askStrength
In the expression:
do "Please, choose an element from the initial die roll"
   newStrength <- askStrength
   removeStrengthFromDieRollLoop askStrength dieRoll
In a stmt of a 'do' block:
  if strength `elem` dieRoll then
    delete st

What is the best way to proceed ? How can I fix the error, and which is the best way to request for the value looping each time if the value is not inside the initial list ? . Thanks .


Solution

  • Here's some code that compiles. (I have not run it, though)

    removeStrengthFromDieRoll :: Int -> [Int] -> IO [Int]
    removeStrengthFromDieRoll strength dieRoll = do
      if strength `elem` dieRoll then
        return (delete strength dieRoll)
      else do
        putStrLn "Please, choose an element from the initial die roll"
        newStrength <- askStrength
        removeStrengthFromDieRoll newStrength dieRoll
    
    
    askStrength :: IO Int
    askStrength = do
       putStr "Strength : "
       -- You might need hFlush stdout here, I'm not sure.
       readLn :: IO Int
    

    Some key aspects:

    • All functions that do IO, either directly or indirectly, must have a type of the form ... -> IO something. So, askStrength must have an IO type since it calls putStr. Hence, removeStrengthFromDieRoll must also have an IO type. Hence, if you use removeStrengthFromDieRoll in another function, that must still have an IO type, etc. "IO is viral" is a common way to state this.

    • If you need to print a string, you need something like putStrLn. You can't simply use a string "Please, ....".

    • When you need to return inside IO, but you only have a non-IO value, like the list delete strength dieRoll above, you need to bring that inside IO using the return function (or, equivalently, pure). Note that return is not the same as in C,C++,Java,... but in Haskell is only used to convert someType into IO someType, so that "pure" values can be returned ina function that has an IO result type.

    Finally, IO can be tricky at first to grasp from beginners. We all have been there. I'd suggest to following an IO monad tutorial, if you have not yet done so: there should be plenty of them around the 'net.