Search code examples
haskelliotypechecking

Haskell: how do you check runtime types on IO?


I'm making my way through some introductory material on Haskell and trying to complete this silly Rock, Paper, Scissors implementation for the command line.

I would think that a type guard on the input would be good enough to convince the compiler that the input is of the type RPS, but alas, it is not.

How does one go about telling the compiler that input data is of one type or another?


data RPS = Rock | Paper | Scissors

_shoot :: RPS -> RPS -> String
_shoot Rock Paper = "Paper beats rock, you win!"
_shoot Paper Rock = "Paper beats rock, you loose."
_shoot Rock Scissors = "Rock beats scissors, you loose."
_shoot Scissors Rock = "Rock beats scissors, you win!"
_shoot Paper Scissors = "Scissors beats paper, you win!"
_shoot Scissors Paper = "Scissors beats paper, you loose!"
_shoot Rock Rock = "Tie!"
_shoot Scissors Scissors = "Tie!"
_shoot Paper Paper = "Tie!"

isRPS :: String -> Bool
isRPS s = elem s ["Rock", "Paper", "Scissors"]

main :: IO ()
main = do
  putStrLn "Rock, Paper, or Scissors?"
  choice <- getLine
  if isRPS choice -- this was my idea but is apparently not good enough
    then putStrLn (_shoot choice Rock) 
--                        ^^^^^^
-- Couldn't match type ‘[Char]’ with ‘RPS’ Expected type: RPS Actual type: String
    else putStrLn "Invalid choice."

Solution

  • You did not transform the choice (which is a String) to an RPS, or even better a Maybe RPS:

    readRPS :: String -> Maybe RPS
    readRPS "rock" = Just Rock
    readRPS "paper" = Just Paper
    readRPS "scissors" = Just Scissors
    readRPS _ = Nothing

    Here we thus return a Just x given the input is valid (with x the corresponding RPS item), or Nothing if the string is not a valid option.

    We can then implement this as:

    import Data.Char(toLower)
    
    main :: IO ()
    main = do
        putStrLn "Rock, Paper, or Scissors?"
        choice <- getLine
        case readRPS (map toLower choice) of
            Just rps -> putStrLn (_shoot rps Rock) 
            Nothing -> putStrLn "Invalid choice."
        main