Happy Learn Haskell Tutorial Vol 1

Buy now at Leanpub

Please Buy now at Leanpub

Written and illustrated by GetContented.

Published on 2024-05-24.


Contents

Main Table of Contents
Previous chapter: 15. Basic Output
16. Fridge, The Game
... 16.1. Do Blocks with IO
... 16.2. Do Block Nesting
... 16.3. Whole-Program Recursion
... 16.4. Homework
Next chapter: 17. The People Book

16. Fridge, The Game 🔗

This is one of the simplest horror games ever. The original idea was by Peter Halasz of https://becauseofgames.com who created it in the programming language C when he was learning how to program.

We’re going to use this little game to learn how to get some input from the user, to sequence chunks of IO code together, and also to see a way to make a program continue until the user wants to stop.

Here is the listing. We explain it below.


main :: IO ()
main = do
  putStrLn "You are in a fridge. What do you want to do?"
  putStrLn "1. Try to get out."
  putStrLn "2. Eat."
  putStrLn "3. Die."
  command <- getLine
  case command of
    "1" ->
        putStrLn "You try to get out. You fail. You die."
    "2" ->
        do
          putStrLn "You eat. You eat some more."
          putStrLn "Damn, this food is tasty!"
          putStrLn "You eat so much you die."
    "3" ->
        putStrLn "You die."
    _   ->
        putStrLn "Did not understand."
  putStrLn "Play again? write y if you do."
  playAgain <- getLine
  if playAgain == "y"
  then main
  else putStrLn "Thanks for playing."

This program uses a do block, which is something we haven’t seen before. This allows you to join many actions together into one single action. There are two main things to know about do blocks, which we’ll cover below.

16.1. Do Blocks with IO 🔗

Firstly, all of the actions in an IO do block are executed in the order they’re written. That is, they’re sequenced together. Secondly, using “<-”, you can connect an inner value from an IO action on its right to a variable on its left. It’s worth noting that you can only use this <- syntax within a do block, though.

You’ll also notice that we have a case expression. We can use any Haskell expressions we like in a do block as long as they result in an action of the same type as the do block’s type.

Anyway, let’s see these things in action with two tiny example programs, one for do blocks combining and sequencing IO actions, and one for gathering input from the user.


main :: IO ()
main =
  do
    putStrLn "Hello"
    putStrLn "There"

Ok, so this program will first print Hello on the screen, then it will print There on the next line. The do block takes one or more actions, and packs them into a single action. The type of that do block above is IO (), just like main, and in IO, these actions will be sequenced one after the other as we’ve written them down the page.

And now, getting some input from the user:


main :: IO ()
main =
  do
    putStrLn "What is your name? "
    theName <- getLine
    putStrLn ("You said your name is " ++ theName)

Ok here we’re sequencing three actions together. We can get input from the user in Haskell with getLine. When this program is run, once the first line has been output, it will wait for the user to put some text in and press return. Once they do that, it will have bound that text into the getLine action, pulled that value out as theName, and printed out the last line which includes that text the user entered!

If we look back at our original program, and look at the line command <- getLine, you’ll see <- is there to pull the String from the getLine action and set the variable “command” to its value. The type of getLine is IO String, which means it’s an IO action that “contains” a String when it’s executed. When we use <-, we can think about it like it’s extracting the String value from the action (note that this applies only when we’re in an IO action, though).

16.2. Do Block Nesting 🔗

Notice, also, that you can put do blocks within do blocks. This is called nesting. We’re doing this by having another do block in the “2” branch of the case expression.

16.3. Whole-Program Recursion 🔗

This game has two sections. First it tells the user their options and asks for their input with getLine, and then depending on what they wrote, it tells them what happened. Next, it asks if they want to play again, and if they do, it runs the whole thing again by calling main. This is recursion of the whole program. The program is an IO action, and do blocks allow us to compose IO actions, so it’s perfectly fine to have the whole program at the end recursively.

One last thing to note about do blocks, though, is that they must always end with the same type as the whole do block. So, becuase ours ends with an if expression whose resultant type is IO (), which is the type of the main function itself, it will work just fine. We’ll see more examples of do blocks in later chapters.

16.4. Homework 🔗

Homework is to go for a walk with a pad and pen and write a program to add up a few of the numbers on number plates of cars in your street (assuming there are lots of cars around, if not, pick something else where there are lots of numbers). Add them up using the (+) function, and use either print or putStrLn with show. Do this as many times as you need to so that you know how to do it without looking it up. You will probably need to do this in a few different sessions to fully anchor it in your memory. Also, write a function that prints out a greeting.

You’ll notice that we’re giving you lots of repeated homework with putStrLn, print, show and the other basics. That’s because we want to make sure you can do it very well. Varied repetition, or practice, is the key to getting very good with a skill.


If you’ve enjoyed reading this, please consider purchasing a copy at Leanpub today

Please follow us, and check out our videos Follow @HappyLearnTutes

Also, Volume 2 is now in beta and being written! Show your support and register your interest at its Leanpub site.


Main Table of Contents
Previous chapter: 15. Basic Output
16. Fridge, The Game
... 16.1. Do Blocks with IO
... 16.2. Do Block Nesting
... 16.3. Whole-Program Recursion
... 16.4. Homework
Next chapter: 17. The People Book