I\'m learning Haskell in the hope that it will help me get closer to functional programming. Previously, I\'ve mostly used languages with C-like syntax, like C, Java, and D.
Haskell style is functional, not imperative! Rather than "do this then that," think about combining functions and describing what your program will do, not how.
In the game, your program asks the user for a guess. A correct guess is a winner. Otherwise, the user tries again. The game continues until the user guesses correctly, so we write that:
main = untilM (isCorrect 42) (read `liftM` getLine)
This uses a combinator that repeatedly runs an action (getLine
pulls a line of input and read
converts that string to an integer in this case) and checks its result:
untilM :: Monad m => (a -> m Bool) -> m a -> m ()
untilM p a = do
x <- a
done <- p x
if done
then return ()
else untilM p a
The predicate (partially applied in main
) checks the guess against the correct value and responds accordingly:
isCorrect :: Int -> Int -> IO Bool
isCorrect num guess =
case compare num guess of
EQ -> putStrLn "You Win!" >> return True
LT -> putStrLn "Too high!" >> return False
GT -> putStrLn "Too low!" >> return False
The action to be run until the player guesses correctly is
read `liftM` getLine
Why not keep it simple and just compose the two functions?
*Main> :type read . getLine:1:7: Couldn't match expected type `a -> String' against inferred type `IO String' In the second argument of `(.)', namely `getLine' In the expression: read . getLine
The type of getLine
is IO String
, but read
wants a pure String
.
The function liftM from Control.Monad takes a pure function and “lifts” it into a monad. The type of the expression tells us a great deal about what it does:
*Main> :type read `liftM` getLine read `liftM` getLine :: (Read a) => IO a
It's an I/O action that when run gives us back a value converted with read
, an Int
in our case. Recall that readLine
is an I/O action that yields String
values, so you can think of liftM
as allowing us to apply read
“inside” the IO
monad.
Sample game:
1 Too low! 100 Too high! 42 You Win!