I wrote a simple program in Haskell that plays the guessing game described in The Rust Programming Language book:
Here’s how it works: the program will generate a random integer between 1 and 100. It will then prompt the player to enter a guess. After entering a guess, it will indicate whether the guess is too low or too high. If the guess is correct, the game will print congratulations and exit.
Here is what I wrote:
import Control.Monad (when)
import System.Random (randomRIO)
-- | Check if the guess is correct, otherwise provide a hint
respond :: Int -> Int -> String
respond correct guess
| guess > correct = "Smaller than " ++ show guess
| guess < correct = "Larger than " ++ show guess
| guess == correct = "Correct! " ++ show correct
-- | Main game loop; prompt for input and repeat until guessed correctly
play :: Int -> IO ()
play x = do
putStr "Guess: "
guess <- read <$> getLine
putStrLn $ respond x guess
when (guess /= x) $ play x
-- | Start the game with a random number between 1 and 100
main :: IO ()
main = play =<< randomRIO (1, 100)
The code works, but GHC gives me a warning that "Pattern match(es) are non exhaustive. In an equation for 'respond': Patterns not matched: _ _"
I take those two underscores to represent the two Ints
I have as arguments to the respond
function. What I don't understand is which case I haven't covered. Those aren't Maybe Int
s or anything special — the function requires two valid Ints
, so I only need to deal with integers — and I don't think there is any number that cannot be deemed greater than, less than, or equal to another?
Is this just GHC assuming I have not covered all cases because I didn't add a final otherwise =
guard? Even though it logically covers all cases.
Also, if you have any tips on how to write more idiomatic Haskell, I'd appreciate them. I'm still learning the basics.
GHC simply doesn't know that one of a > b
, a < b
, or a == b
must evaluate to True
. (Indeed, it is possible to write an Ord
instance that violates this assumption -- though most programmers would not consider doing so, and certainly the one for Int
will behave nicely in this regard.)
You can make it patently obvious to GHC that you've covered all the cases by using a complete pattern match instead, e.g.
respond correct guess = case compare guess correct of
GT -> ...
LT -> ...
EQ -> ...
GHC also has a special case in its exhaustiveness checker for the guards otherwise
and True
, so you may add (or replace) one of the guards with this as an alternative solution.
respond correct guess
| guess > correct = ...
| guess < correct = ...
| otherwise = ...