# Happy Learn Haskell Tutorial Vol 1

Written and illustrated by GetContented.

Published on 2017-07-08.

## Contents

Previous chapter: 18. Times-Table Train of Terror
19. Skwak the Squirrel
... 19.1. More on the (\$) Function
... 19.2. Mid-Lesson Homework
... 19.3. Continuing On
... 19.4. Problems and Homework
Next chapter: 20. Basic Input

# 19. Skwak the Squirrel 🔗

Games! We saw the Fridge game. It took place in one single “room” which made it very limited. Then, the Train game didn’t really give you any freedom, but at least it had more rooms.

Next we’ll see a game that lets us imagine that we’re a squirrel and we live in a tree. Our new game will only have two areas, but it will provide more capability than before. And, in the process, we’ll get some more practice with all the things we’ve seen so far.

If we wanted to make a game that is a tiny bit more like a real text adventure game, it’d have to let the player move around between its game areas.

To keep it ultra-simple, let’s say we (our squirrel) could be able to go between only the inside and outside of the tree, perhaps. The program we’ll be discovering in this section is a lot more complicated than the Fridge game, but it’s about one of the most simple, basic text adventures possible.

Let’s look at the types first:

``````
data GameObject = Player
| Acorn
deriving (Eq, Show)
data Room =
Room Description [GameObject]
deriving (Show)
type Description = String
type Inventory = [GameObject]
type GameMap = [Room]
type GameState = (GameMap, Inventory)
``````

We have a `GameObject` type whose values can be either `Player` or `Acorn`. Fairly straightforward, this is just a sum type like we’ve seen before. What about `deriving (Eq, Show)`, though? Well, this is a way to make a type become an instance of these typeclasses without having to write the code for it manually ourselves. Being an instance of `Eq` means we can use `(==)` and other comparison functions on values of this type, and being an instance of `Show` means it can be converted to strings with the `show` function.

Now, the `Room` type is a product type and it has a type constructor called `Room` as well as a value constructor of the same name, which is used to make values of the `Room` type. It has fields of `Description`, and a list of type `GameObject` which is used to hold the contents of the `Room`. So, because of the way the `GameObject` type is defined, a `Room` can have one or more `Player` or `Acorn` values in it. Our game will only ever have one of each in the whole game.

Next are a bunch of type synonyms which should be pretty easy to understand by now, possibly with the exception of `GameState` which is a 2-Tuple (otherwise known as a pair) of `GameMap`, and `Inventory`, which is a list of `GameObject`. `GameMap` is a list of `Room`.

The `GameState` type will be what we use to store all the changing pieces of our game as the player plays it. The `GameMap` will be all the rooms in the game, and the `Inventory` is what the player is holding moment by moment.

Let’s see a definiton for the initial state of the game and the `main` function, that will start the game.

``````
initialState :: GameState
initialState =
( [ Room "You are inside a tree." [Player]
, Room "You are outside of a tree." [Acorn]]
, [] )

main :: IO ()
main = do
putStrLn "Welcome to Skwak the Squirrel."
putStrLn "You are a squirrel."
gameLoop initialState
``````

Now we see the `main` function, which as we know is the `IO` action that will be executed by Haskell when we compile and run our program.

All it does it announce the game, then runs a function called `gameLoop` using the `intialState` which is the state that the game should start with. The `gameLoop` contains the bulk of the program, and we’ll see it in a moment.

First, Let’s look at `initialState`. This is a `GameState`, which as we know from the types is a 2-Tuple that has a list of `Room` then a list of `GameObject`. We start our game with two rooms... we use the `Room` constructor to build the rooms. We have the outside and the inside of the tree. The `Player` is inside, and the `Acorn` is outside.

Let’s look at the `gameLoop` function now:

``````
gameLoop :: GameState -> IO ()
gameLoop (rooms, currentInv) = do
let currentRoom =
case findRoomWithPlayer rooms of
Just r -> r
Nothing -> error \$ "Somehow the player "
++ "ended up outside the map!"
possibleCmds =
validCommands currentRoom currentInv
if playerWon (rooms, currentInv)
then gameOverRestart
else do
describeWorld currentRoom currentInv possibleCmds
takeActionThenLoop
currentRoom currentInv possibleCmds rooms
``````

This function takes a `GameState` and returns an `IO` action. It’s the `main` functionality of the game. Each move the player makes goes through the game loop once, which is why it’s called a loop, because it’s like a circle.

We see that we can have a `let` expression in our `do` block which essentially sets up temporary variables in the `do` block from that point onwards.

We’re finding and grabbing the current room from the list of rooms, or throwing up our hands if it can’t find the `Player`.

Then we work out which commands are valid for the current room and the current inventory. Some player commands are only possible with some combination of `Acorn` being in the room with the player, or in the inventory.

Once that is done, we check if the player has won with an `if` expression, and if so, we tell the player they’ve won and offer to play again. If they didn’t win, then we describe the world to them at the room they’re in, and let them take an action by grabbing their command, then go back again to the start (using the `takeActionThenLoop` function).

Now we’ll go through the remaining functions. Note, though, that we can use any expressions (such as `if`) with no trouble in a `do` block for `IO`, even other nested `do` blocks, so long as those expressions result in an `IO` value of some kind. We’ll see in a moment that all of those functions we just discussed yield `IO` values of some kind.

``````
findRoomWithPlayer :: [Room] -> Maybe Room
findRoomWithPlayer rooms =
L.find (\(Room _ obs) ->
any (== Player) obs)
rooms
``````

We can tell by the name that `findRoomWithPlayer` should return the `Room` whose objects include the `Player`. We’re returning a `Maybe Room` because it’s technically possible that the `Player` might not be in any `Room`. If that’s the case, there’s a problem, because the game won’t work. That’s why there’s an application of the `error` function in `gameLoop` if `findRoomWithPlayer` can’t find a `Player`.

We’re using a lambda as our Room-testing function here. The way find works is it looks through the list passed into it, applies the predicate section function `(== Player)` to each item, and if it finds one that returns `True`, it returns that one, wrapped in the `Just` data constructor from the `Maybe` type. If it doesn’t, it returns the `Nothing` value (also from the `Maybe` type.

In depth, our lambda takes a `Room`, pulls it apart using pattern-matching to get at the objects (which is a list of `GameObject`), names that `obs`, and then passes that to `any`, which will check to see if any of them return `True` for the function `(== Player)` which checks to see if something is equal to the `Player` value.

Next we’ll see the function that crafts the valid commands for a particular room and inventory combination:

``````
validCommands :: Room -> Inventory -> [String]
validCommands (Room _ gameObjs) invItems =
["go"] ++ takeCommandList
++ dropCommandList ++ ["quit"]
where
takeCommandList =
if somethingToTake gameObjs
then ["take"]
else []
dropCommandList =
if length invItems > 0
then ["put"]
else []
``````

This function takes a `Room` and an `Inventory` and returns a list of `String` values that are all the commands that a player can type in depending on the current data: whether there is anything to take (in the room), or drop (from inventory).

It’s using the list concatenation operator `(++)` to join the `String` lists together into one list to return, and two definitions with `if` expressions to ensure we only put “take” or “put” on the command list if it’s appropriate.

``````
somethingToTake :: [GameObject] -> Bool
somethingToTake objs =
any (/= Player) objs
``````

Of course, we can eta-reduce this function as we know by now (that is, get rid of the repeated trailing arguments):

``````
somethingToTake :: [GameObject] -> Bool
somethingToTake = any (/= Player)
``````

Here we take a list of `GameObject` which is used by `validCommands` as the list of objects in the room that can be taken. We’re using it to work out if there is anything that is not a `Player` in the list. The `any` function takes a predicate function, evaluates it on each item of a list, and returns `True` if any of the evaluations return `True`, otherwise `False`. The function `(/=)` means not equal to, and comes from the `Eq` typeclass. The end effect answers the question “Are there any non-player objects in this room?”.

As we’ve seen before, the use of parentheses around `(/= Player)` makes it into what’s called a section: it creates a function of one argument, having applied one of the 2-arguments required by the operator. Here we’re creating a function that takes a `GameObject` and returns a `Bool` that indicates whether the `GameObject` was `Player`.

``````
playerWon :: GameState -> Bool
playerWon (rooms, currentInv) =
any hasAcornAndInside rooms
where hasAcornAndInside (Room desc objs) =
desc == "You are inside a tree."
&& any (==Acorn) objs
``````

This function answers the question “Has the player won yet?” The player has won if the acorn is in the tree (and not in the player’s inventory). Pay careful attention to the indentation (where the lines start). This matters!

We test for this by pulling the `GameState` tuple apart into a variable each for rooms and current inventory. We then go through all the rooms, and using the `any` function again, check if there’s one which has the description “You are inside a tree.” that also has the Acorn in its objects list.

Note the use of the `&&` operator, which takes two `Bool} expressions and returns \{True` if they’re both `True`. This is called the logical and operator. It’s one way we can connect up logic expressions into larger chunks of meaning. There is also an `(||)` operator that is logical or, which will return `True` if either of its inputs evaluate to `True`, and a `not` function that takes only one `Bool` expression and returns the logical inverse of it (that is, if it’s `True`, it returns `False` and vice-versa).

Our `playerWon` function is a not the best way we could write it, because we’re relying on the description of the room to check if it’s the inside room. This gives us a lot of room for errors to creep in by accident. We did this to keep the types a bit simpler as you’re learning. It’s bad because what if we changed the description in the actual `Room` data, but then forgot to change this check `String`. It would mean it’d be impossible to win the game.

A better way would be to pull this description into its own definition that is defined in only one place. The best way, though, would be to have an identifier in the `Room` data type representing the type of `Room` it is, and to check against this to see if this `Room` was the winning goal `Room`.

``````
gameOverRestart :: IO ()
gameOverRestart = do
putStrLn \$ "You won!"
++ "You have successfully stored the acorn"
++ " for winter. Well done!"
putStrLn "Do you want to play again? y = yes"
playAgain <- getLine
if playAgain == "y"
then gameLoop initialState
else putStrLn "Thanks for playing!"
``````

Our function to check if the game is now over uses a `do` block to print some messages congratulating the player, then asks if they want to play again, collects a line of input from them, and restarts the game from the beginning by using `initialState` if their input equals `"y"`. Very straight-forward!

``````
getCommand :: IO String
getCommand = do
putStrLn "What do you want to do?"
getLine
``````

`getCommand` is a simple little action that prints a query to the player, gets that response and returns it. The type is `IO String`, which is the same type as `getLine`. Notice that as `getLine` is the last line of the function, we don’t need to use anything to pull the value out here, because it has the return type already. We’ll see how it’s used now in `takeActionThenLoop`:

``````
takeActionThenLoop :: Room ->
Inventory ->
[String] ->
[Room] ->
IO ()
takeActionThenLoop currentRoom
currentInv
possibleCmds
rooms =
do
command <- getCommand
if any (==command) possibleCmds
then case command of
"go" ->
do
putStrLn "You go..."
gameLoop \$ movePlayer (rooms, currentInv)
"take" ->
do
putStrLn "You take the acorn..."
gameLoop \$
moveAcornToInventory (rooms, currentInv)
"put" ->
do
putStrLn "You put the acorn down..."
gameLoop \$
moveAcornFromInventory (rooms, currentInv)
"quit" ->
putStrLn \$ "You decide to give up."
++ " Thanks for playing."
_ ->
do
putStrLn "That is not a command."
gameLoop (rooms, currentInv)
else do
putStrLn \$ "Command not possible here,"
++ " or that is not a command."
gameLoop (rooms, currentInv)
``````

Even though this is one of the largest functions in the program, it’s reasonably simple. It’s structured by firstly taking a command from the user, then checking if the command is one of the valid ones for the player right now. If not it complains that it can’t understand and just goes back to the `gameLoop` with the current data. However, if it does actually match against a valid command, it runs through the case expression trying to find a match.

Each of the commands has a corresponding function which results in a modified `GameState`, which is then passed back into the `gameLoop` function for a further turn. We’ll see each of these functions momentarily.

Notice that we use the `(\$)` function all over the place, such as to apply `gameLoop` to the result of the evaluation of the command functions, such as `movePlayer`.

Also notice that there is a pattern match for “`_`”, which for completeness will match on any other values for the `command`. In our case there aren’t actually any other commands, but not having this is a potential source of problems in the future as things change, and Haskell will complain if we leave it off. Take a moment and think what would happen if you added a new command to the `validCommands` function, but didn’t add it to the `takeActionThenLoop` function. If we keep this underscore catch-all, then play the game, the game will tell us that our new command is not recognised as a command. If we leave it off, though, our program will crash. That’s why it’s better to have total functions.

Having said this, a better solution would be to represent our commands with an algebraic data type. For now and to keep things slightly more simple, we’ll stick to the `String` type.

These next three functions are to adjust the game state when the player does something.

``````
movePlayer :: GameState -> GameState
movePlayer (rooms, inv) =
(newRooms, inv)
where
newRooms =
if any (==Player) objs
then (Room d (filter (/=Player) objs))
else (Room d (Player : objs))

moveAcornToInventory :: GameState -> GameState
moveAcornToInventory (rooms, inv) =
(newRooms, newInv)
where
newInv =
Acorn : inv
newRooms =
Room d (filter (/=Acorn) objs)

moveAcornFromInventory :: GameState -> GameState
moveAcornFromInventory (rooms, inv) =
(newRooms, newInv)
where
newInv =
filter (/=Acorn) inv
newRooms =
if any (==Player) objs
then Room d (Acorn : objs)
else Room d objs
``````

Our game only has two rooms, so the `movePlayer` function just goes over “all” two rooms using the `map` function, and applies a function that takes the `Player` out if it’s there using `filter`, or puts it in if it’s not there, using the list-prepend function `(:)`. It does this by pattern-matching the `Room` into its pieces, then reconstructing it with a new `GameObject` list.

The `moveAcornToInventory` function returns a new 2-tuple (a `GameState`) that is comprised of an adjusted set of rooms by mapping a deconstructing-reconstructing function that uses `filter` to remove all acorns from the rooms’ `GameObject` list (there should only be one anyway), and an adjusted inventory by prepending an `Acorn` to it using `(:)`. This “moves” the `Acorn` into inventory.

The `moveAcornFromInventory` does the reverse of this. The mapping function that puts the `Acorn` into the room where the player is is quite interesting because it has an `if` expression to detect if a particular room’s game objects contains the `Player`, and if so, it prepends an `Acorn`, otherwise it just re-builds the passed in room as it was before.

``````
describeWorld :: Room ->
Inventory ->
[String] ->
IO ()
describeWorld currentRoom
currentInv
possibleCmds =
do
putStrLn \$ describeRoom currentRoom
putStrLn \$ describeInventory currentInv
putStrLn \$ describeCommands possibleCmds
``````

This is pretty straightforward, it just uses other functions to describe to the player which room they’re in, what they’re carrying and also which commands they can use here.

Notice, again, that we’re using another `do` block to make a single `IO` action out of a bunch of `putStrLn` function applications.

## 19.1. More on the (\$) Function 🔗

Also we see the function `(\$)` again at work, applying the function on the left of it to the result of evaluating everything on the right of it. The function application operator is often used as above, to avoid having to type brackets around expressions, as you probably know by now. It has the lowest possible precedence, which means it’s evaluated last, after all the other parts of an expression.

The type signature of this is `(\$) :: (a -> b) -> a -> b` which means it takes a function from `a` to `b` (on the left of it) and an `a` (on the right of it), and applies the function to the “a” which gives a `b`. Obviously this is an extremely general function because `(\$)` can apply to any function, and any value as long as they fit together.

Again, it’s important to realise that even though the type signature of `(\$)` says it takes a function from `a` types to `b` types, it doesn’t have to be different types, but they can be if you like. So, `(+3)` fits the description even though its type is `Num a => a -> a` and this is because `(a -> b)` goes from one “any type” to another “any type”, so not necessarily the same type. `(a -> b)` is actually the type that fits any type of function at all!

There is no difference in result between writing `(+3) \$ 7` and writing `(+3) 7`, but when you start adding more arguments it starts to matter, like in `(+3) \$ 7 * 5` which gives `38`, a different answer than `(+3) 7 * 5`, which is 50. From this we can see that `(\$)` changes the order that things are evaluated in, just like putting parentheses around some things (so, `(+3) \$ 7 * 5` is equivalent to `(+3) (7 * 5)`).

## 19.2. Mid-Lesson Homework 🔗

Your homework (yes, mid-lesson homework) is to experiment with the `(\$)` function, and explicit bracketing, and explore the differences and similarities if you can.

## 19.3. Continuing On 🔗

Next, the three functions that `describeWorld` uses:

``````
describeRoom :: Room -> String
describeRoom (Room desc objs) =
desc ++ if any (==Acorn) objs
then " There is an acorn here"
else ""

describeInventory :: Inventory -> String
describeInventory [] =
"You are holding nothing"
describeInventory inv =
"You are holding: " ++
(concat \$ map show inv)

describeCommands :: [String] -> String
describeCommands commands =
"Commands: "
++ (L.intercalate ", " commands)
``````

The `describeRoom` function takes a single `Room` and pattern-matches it into the `desc` and `objs` variables, which are the description of the `Room`, and the objects that the `Room` contains respectively. Notice that we’re using `(++)` to append the result of an `if` expression to the `Room`’s description here. The `if` expression uses a section. Remember that a section is effectively a partially applied operator. In this case, `(==Acorn) :: GameObject -> Bool` will check if something is an `Acorn`, and return a `Boolean` value for that (`True`/`False`).

Let’s think some more about the function `any :: Foldable t => (a -> Bool) -> t a -> Bool` for a moment. This is a function that takes a predicate from types `a` to `Bool`, a `Foldable t a` and then returns a `Bool`. This function will tell us if any item in the list “satisfies” the predicate. That is, if any item in the list returns `True` for our predicate function.

Or, partially applied to the `(==Acorn)` section as in `any (==Acorn)`, it will tell us if there’s an `Acorn` in a supplied list! If there is an `Acorn`, it’ll say so. If not, just an empty `String`.

Next we’ll look at the `describeInventory` function. There are two definitions on this function. The first is for when there is nothing in the passed-in `Inventory`, so we’re matching on an empty list. This is simple.

The second definition is where the meat is. If the match with the empty list failed, that means there must be something in the `Inventory`, which we now pattern-match to the “inv” variable.

We then use the list concatenation operator (++) to append the result of an expression to the end of the beginning of a description of what they player is holding. Of course, this works because `String` is `[Char]`, so therefore a list.

The expression we’re appending is `(concat \$ map show inv)`. We can break this apart into its pieces. Firstly, we now know about the `(\$)` function, and can think about it like it’s bracketing like this: `(concat (map show inv))`.

Thinking about the expression `map show inv`, we can see that this takes the `inv` list and returns a new list that it builds by applying the `show` function to each `GameObject` in it, turning it into a list of strings. We can see this clearly if we check its type: `map show :: Show a => [a] -> [String]`.

The function `concat :: Foldable t -> t [a] -> [a]` takes a `Foldable`-wrapped list of type `a`, and returns a list of type `a`. So in this case, because we’re passing it a `[String]`, which as you know is the same as a `[[Char]]`. So that means the `Foldable t` that `concat` needs will be the list type, so a list of list of `Char`. The `concat` function will concatenate (join) that `[String]` to be a single `String` (or turn the `[[Char]]` into a `[Char]`, same thing), by joining all the strings into one `String`.

The end result is that `describeInventory` will either tell the player they’re holding nothing, or that they’re holding a list of what they’re holding. The fact that there will only ever be one item in the list means we don’t need to comma-separate it or anything.

Next is the `describeCommands` function. It takes a list of commands, which are just `String` values, and calls the `intercalate` function from `Data.List` on that it. This is a pretty neat function. Its type is `[a] -> [[a]] -> [a]`. The first argument is a separator, and the second is a list of items of the same type. It then joins them all together into one big list after putting the separator between each of the items. So, here we’re using it to separate all the commands by comma-space so that it reads nicely.

## 19.4. Problems and Homework 🔗

While this game is designed to be small and simple for teaching purposes, it’s not hard to see that it would be pretty easy to adjust it to have a larger map, or more items. There are some problems, though.

Your homework is to go back through the chapter and think about what the problems of this program are as it’s built. For example, what would happen to the program if you added an additional room? Why not try it out and see.

It can be very useful to think about all the possible ways that programs could be changed, and how important it is to design programs in ways that make extending them easier, and also to make them resilient to change in as many ways as possible. This game is very easy to extend in some ways, but it’s not written to be extended at all in other ways.

Ideally when we write programs, we make it very easy to adjust them. We can do this by building them up from smaller pieces that closely match the type of data and functionality we want. For example, perhaps a `List` isn't a very good data type to use for our game map, and perhaps when we wrote our map movement functions, we could have written them in a way that meant they would work on any size `List`, not just a two-element one.

We can see from the language Haskell itself that its designers have followed this pattern because this language is very easy to extend, and encourages a style of programming where we build the language up to the problem we’re trying to solve.