Haskell: how do you check runtime types on IO?
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
You're nearly there, you just need the read function to convert the user's string to your RPS
data type.
The first thing you need to do is to make RPS
an instance of the Read
typeclass. This can be done easily by amending your data
declaration to:
data RPS = Rock | Paper | Scissors deriving Read
what deriving Read
does is give RPS
a default instance of the Read
typeclass, which works in the obvious way: read "Rock"
will become Rock
and so on, provided the compiler knows you're using read
in a context where a value of type RPS
is expected.
Then all you need to do, in your main
function, is change this:
putStrLn (_shoot choice Rock)
to
putStrLn (_shoot (read choice) Rock)
Since _shoot
has a type signature telling GHC that its first argument must be an RPS
value, it will know to use the instance of read
defined for your RPS
type, and all should be well, since you've already restricted the valid user choices to those 3 specific strings.
(Note that for larger programs there are safer and better ways of handling things like this - see Willem's answer for one simple approach - but this is fine for a basic learning exercise.)