Banana
Banana

Reputation: 824

Type error evaluating user input: IO () with actual type [[Char]]. Haskell

I'm trying to implement the game on which i can move different blocks on the board. I've set the conditions for moving and an actual moving of the blocks, now what i tried to add was a recursive loop on user input, which would ideally solve the game.

That's what i've written:

userInput tableArr = do
  str <- getLine
  if str == "help"
    then printHelp tableArr
    else if str == "gg"
      then putStrLn "Game over, you lose"
      else if length str == 3
         then isCorrectInput str tableArr
         else if str == "table"
           then let printer = printTable tableArr in userInput tableArr
           else displayErrorMsg tableArr

isCorrectInput :: String -> [[Char]] -> [[Char]]
isCorrectInput str tableArr = do
  let val = filter (not . isSpace) str
  let move = val !! 1
  let number = digitToInt(val !! 0)
  let posArr = findBlock (myElem number) tableArr
  if length val == 2
    then (if move == 'R'
            then let newArr = moveRIGHT tableArr number posArr in userInput newArr
            else if move == 'L'
              then let newArr = moveLEFT tableArr number posArr in userInput newArr
              else if move == 'U'
                then let newArr = moveUP tableArr number posArr in userInput newArr
                else if move == 'D'
                  then let newArr = moveDOWN tableArr number posArr in userInput newArr
                  else displayErrorMsg tableArr)
    else displayErrorMsg tableArr

displayErrorMsg tableArr = do
  putStr "Wrong input, type again: "
  userInput tableArr

Now a-bit clarification on what's happening here.

Function userInput takes in a [[Char]] (i have not set the signature cause i got confused on the return type of this thing). Now there's several conditions for the user input, but the error is thrown in isCorrectInput. (Also is let printer = printTable tableArr in userInput tableArr written correctly? I didn't want to add recursive end to function printTable which basically contains only putStrLns to print out my game board).

Now let's take a quick look at isCorrectInput. This evaluates the user input abit more accurately and takes an action based on the user input (Usually the user input is expected in manner "4 R" which means basically block with number 4 to right. Anyways if one of the moving conditions are matched then the move function is called, added to the new variable and then the function userInput is called out recursively once again.

The function displayErrorMsg is just to display message and call-out recursion again.

Now the error that i'm getting is:

Couldn't match expected type `IO ()' with actual type `[[Char]]'
In the expression: isCorrectInput str tableArr
In the expression:
  if length str == 3 then
      isCorrectInput str tableArr
  else
      if str == "table" then
          let printer = ... in userInput tableArr
      else
          displayErrorMsg tableArr
In the expression:
  if str == "gg" then
      putStrLn "Game over, you lose"
  else
      if length str == 3 then
          isCorrectInput str tableArr
      else
          if str == "table" then
              let ... in userInput tableArr
          else
              displayErrorMsg tableArr

I hope that my explanation was clear enough as i'm not a native English speaker and one of you could show what's wrong with my code.

Thank you :)

Upvotes: 0

Views: 257

Answers (2)

epsilonhalbe
epsilonhalbe

Reputation: 15959

Your code is messy - on more than one level, so bear with me while I try to tidy it up and help you understand this error and most importantly avoid it in the future.

TL,DR for you claim that isCorrectInput :: String -> [[Char]] -> [[Char]] in the type signature but all the functions at the end of your abundant ifs are of type IO () so the signature should be isCorrectInput :: String -> [[Char]] -> IO () probably (for there are some functions without implementation here).

But first of all try to work with the following principles

  • try to avoid IO () where possible and stick to "pure" functions
  • use guards/case statements instead of nested ifs
  • unless you have good reasons for it - code "walking" to the right side of the screen - is a bad sign
  • write small and understandable functions
  • if you cannot figure out the signature of a code piece - use ghci

    Prelude> :type functionnamewhosesignatureidontknow
    

    will help you

  • read "learn you a haskell for great good" or some other literature linked in the info section of this stackoverflow tag
  • if you have a haskell/functional programming user group near you join them

Now to your code - I'll update this section as I get more on with it

I try to start with a skeleton of functions just with a type signature and no implementation

Main.hs

type GameBoard = [String]
data Direction = L|R|U|D deriving (Eq,Show)
type Move = (Int,Direction)

mainLoop :: IO ()
-- checks for abort condition and gives helpful information,
-- while playing the game
mainLoop = undefined

parseMove :: GameBoard -> String -> Maybe Move
-- checks if a given (pure) input string is a valid move for
-- a given game state, for now I use Maybe to indicate all versions of invalid
-- input
parseMove = undefined

help :: IO ()
-- prints the rules of the game
help = undefined

printErr :: String -> IO ()
printErr = undefined

now this should compile - let's fill up the mainLoop - oh I noticed we want to use the current GameBoard in that one so add it as a parameter - note that there are techniques to make this a bit more convenient (notably the so calledState` monad)

mainLoop :: GameBoard -> IO ()
mainLoop gb = do
   str <- getLine
  case str of "help" -> do help
                           mainLoop gb

              "gg" -> putStrLn "Game Over"

          -- "table" I omit for sake of shortness
              _  -> do let nextMove = parseMove gb str
                       mainLoop (nextBoard gb nextMove)

nextBoard :: GameBoard -> Move -> GameBoard
nextBoard = undefined -- try to implement this one on your own

Ooops there's an error in my code - but hey the compiler told us nextMove is Maybe Move where nextBoard expects a Move so tidy that up quickly

              _  -> case parseMove gb str
                      of Just nextMove -> mainLoop (nextBoard gb nextMove)
                         _ -> do putStrLn "incorrect move - please repeat"
                                 mainLoop gb

next challenge is to tidy up is parsing the input, but first I redefine my GameBoard, knowing that haskell is a statically typed language I want to make use of that and actually use this to my advantage

type Tile = Empty | Movable | Fixed | Player deriving (Eq)
instance Show Tile where
  -- I usally use my favourite unicode box characters for games like this one
  show Empty   = " "
  show Movable = "0"
  show Fixed   = "#"
  show Player  = "@"
type GameBoard = [[Tile]]

first I will split the String at the ' ' but how if I don't know the name of that function?

=> use HOOGLE https://www.haskell.org/hoogle/?hoogle=%28a+-%3E+Bool%29+-%3E+%5Ba%5D+-%3E+%28%5Ba%5D%2C%5Ba%5D%29

and using ghci for the two candidates in Prelude we get break as the desired one

parseMove :: GameBoard -> String -> Maybe Move
-- note despite us changing what Gameboard is this signature has not changed!
-- first of all I assume that number and dir are separated by a space
-- watch out there is still a space at _dir
-- and both tuple entries are still Strings
parseMove gb = do let (_n, _dir) = break (== ' ') str
                  n <- parseInt n
                  dir <- parseDir _dir
                  if canPlayerMove gb (n,dir) then return (n,dir)
                                              else Nothing

  where parseInt :: String -> Maybe Int
        parseInt s = if all isDigit s -- you might import or define `isDigit` yourself
                       then Just (read s)
                       else Nothing
        parseDir :: String -> Maybe Direction
        parseDir "L" = Just L
        parseDir "R" = Just R
        -- ..
        parseDir _ = Nothing

canPlayerMove :: GameBoard -> Move -> Bool
canPlayerMove = undefined -- here you take care that the Game logic is respected

I hope you get a bit of a picture how to tidy up code. Also note that this code is by far the best, I hope I wrote it understandably

Btw. that do .. <- .. return in parseMove is a neat trick when working with Maybe the right arrow takes a Maybe value on the right hand side and pulls out a value if it is Just x, otherwise the whole computation below that point is discarded and Nothing is returned.

Upvotes: 3

Rodrigo Ribeiro
Rodrigo Ribeiro

Reputation: 3218

Your problem is that the function isCorrectInput isn't in IO monad. Look at its type:

isCorrectInput :: String -> [[Char]] -> [[Char]]

and the error message

Couldn't match expected type `IO ()' with actual type `[[Char]]'
In the expression: isCorrectInput str tableArr

Since your is monadic (because of the do used) it is assumed that it should return a monadic result. Since it appears to be using the IO monad, you should correct its type signature to (note that String = [Char]):

isCorrectInput :: String -> [String] -> IO [String]

and each expression that represents a result in your function should be a monadic expression or use a return in order to put the result in a given monad. So to fix this error, just add return:

if length str == 3 then
    return (isCorrectInput str tableArr)
else ...

My suggestion is to first understand Haskell IO and Monads. The Chapters 9 and 12 of Learn you a Haskell provide the necessary background.

Upvotes: 2

Related Questions