# Extending the guessing game with effects

In the previous chapter we implemented a simple guessing game. In this chapter we will extend the final program with to keep track of how many guesses the player has done and finally report it at the end.

First, this was the final program from the previous chapter.

``````let io @ { ? } = import! std.io
let random = import! std.random
let int = import! std.int
let string = import! std.string
let { Result } = import! std.result
let { Ordering, compare } = import! std.cmp

do _ = io.println "Guess a number between 1 and 100!"
do target_number = random.thread_rng.gen_int_range 1 101

let guess_number _ : () -> IO () =
do line = io.read_line
match int.parse (string.trim line) with
| Err _ ->
do _ = io.println "That is not a number!"
guess_number ()
| Ok guess ->
match compare guess target_number with
| EQ -> io.println "Correct!"
| LT ->
do _ = io.println "Wrong! Your guess is too low!"
guess_number ()
| GT ->
do _ = io.println "Wrong! Your guess is too high!"
guess_number ()

// Start the first guess
guess_number ()
``````

To keep track of how many guesses the player has done we need to keep some state as we go through each guess. There are a few ways of doing this but the one we will look at here is using gluon's extensible effect system. This can be found under the `std.effect` module.

Extensible effects are a way to represent side effects in a composable way. In other words, it is possible to use multiple different effects easily, something which can otherwise be fairly difficult with the monadic representation of side effects that gluon employs.

## Rewriting IO into Eff

Before we get into adding more effects though we first need to replace the `IO` monad with the `Eff` monad used to represent effects. Normally it is not possible to use a `Monad` directly when using effects and instead an entirely new "effect handler" needs to be written for the effect. However, `Eff` allows the use of ONE `Monad` instance among the various effects in an effect row with the `Lift` effect.

Lets see how we need to modify the code the guessing game to use `Eff` instead of `IO`, without adding any functionality.

``````let io @ { ? } = import! std.io
let random = import! std.random
let int = import! std.int
let string = import! std.string
let { Result } = import! std.result
let { Ordering, compare } = import! std.cmp

let { (<|) } = import! std.function
let effect @ { Eff, ? } = import! std.effect
let { Lift, run_lift, lift, ? } = import! std.effect.lift

let start _ : () -> Eff [| lift : Lift IO | r |] () =
do _ = lift <| io.println "Guess a number between 1 and 100!"
do target_number = lift <| random.thread_rng.gen_int_range 1 101

let guess_number _ : () -> Eff [| lift : Lift IO | r |] () =
do line = lift <| io.read_line
match int.parse (string.trim line) with
| Err _ ->
do _ = lift <| io.println "That is not a number!"
guess_number ()
| Ok guess ->
match compare guess target_number with
| EQ -> lift <| io.println "Correct!"
| LT ->
do _ = lift <| io.println "Wrong! Your guess is too low!"
guess_number ()
| GT ->
do _ = lift <| io.println "Wrong! Your guess is too high!"
guess_number ()

// Start the first guess
guess_number ()

run_lift <| start ()
``````

There is quite a bit going down here so lets break it down.

``````let { (<|) } = import! std.function
let effect @ { Eff, ? } = import! std.effect
let { Lift, run_lift, lift, ? } = import! std.effect.lift
``````

Here we import a few new things. The first just imports the useful `<|` operator, it doesn't really do any thing on its own and just serves as a way to let us avoid parentheses in a few places. Essentially `f <| a 123 "a"` is the same as `f (a 123 "a")`.

Next we import the `Eff` type and its implicit instances so we can use `do` with it.

Finally we import `Lift` and some functions to help us work with it. The `lift` function lets us "lift" a monadic action into the `Eff` monad. `run_lift` does the opposite, it takes the monadic action out of the `Eff` monad and lets us evaluate it.

``````let start _ : () -> Eff [| lift : Lift IO | r |] () =
``````

Here we define our entrypoint and we can see the first use of the `Eff` type and a new bit of syntax which describes an "effect row".

Effect rows are described in a similar manner to records, except they use `[|` and `|]` to delimit instead of `{` and `}`. Rather than describing what fields a record holds they instead describe the effects used in this function. In the same way as records, effect rows also has an optional `| r` part which is what makes this "extensible" (see polymorphic records in the reference). If we defined the row without the `| r` part then we would get an error when trying to use the effect in a place that allows more effects.

``````let eff_closed : Eff [| eff_1 : Eff1 |] () = ...

let eff_open : Eff [| eff_1 : Eff1 | r |] () = ...

let eff : Eff [| eff_1 : Eff1, eff_2 : Eff2 | r |] () =
// Error, type mismatch `[| eff_1 : Eff1 |]` does not contain `eff_2 : Eff2`
do _ = eff_closed
// Ok, because the action is polymorphic/extensible
do _ = eff_open
// etc
``````

Essentially the type variable says that the effect "may have more effects then the ones specified" which lets us only specify the effects that each individual function needs while still being able to use them transparently in a program that uses additional effects.

In this place we only use the `lift : Lift IO` effect but we will see how we can use this to specify multiple effects in a little bit.

``````    do _ = lift <| io.println "Guess a number between 1 and 100!"
``````

Here we actually start running an action. Since `io.println` returns an `IO ()` action and we need an `Eff [| lift : Lift IO | r |] ()` action we use `lift` to convert into the correct type. Otherwise the code is exactly the same as before!

(The need for `lift` is likely to be removed in the future.)

The lines that follow are all the same as before, only they may have received the `lift` treatment as well. Except for the last line.

``````run_lift <| start ()
``````

Here we use `run_lift` to go back from `Eff [| lift : Lift IO | r |] ()` into `IO ()` which the gluon interpreter can execute.

## Adding the State effect

Now that we have the program written with `Eff` we can use this to easily add some state to track how many guesses the player has done!

We do this using the `State` effect which lets us hold a value in the `Eff` monad which we can read and write to.

``````let io @ { ? } = import! std.io
let random = import! std.random
let int = import! std.int
let string = import! std.string
let { Result } = import! std.result
let { Ordering, compare } = import! std.cmp

let { (<|) } = import! std.function
let effect @ { Eff, ? } = import! std.effect
let { Lift, run_lift, lift, ? } = import! std.effect.lift
let { State, eval_state, ? } = import! std.effect.state

let start _ : () -> Eff [| lift : Lift IO, state : State Int | r |] () =
do _ = lift <| io.println "Guess a number between 1 and 100!"
do target_number = lift <| random.thread_rng.gen_int_range 1 101

let guess_number _ : () -> Eff [| lift : Lift IO, state : State Int | r |] () =
do line = lift <| io.read_line
match int.parse (string.trim line) with
| Err _ ->
do _ = lift <| io.println "That is not a number!"
guess_number ()
| Ok guess ->
match compare guess target_number with
| EQ -> lift <| io.println "Correct!"
| LT ->
do _ = lift <| io.println "Wrong! Your guess is too low!"
guess_number ()
| GT ->
do _ = lift <| io.println "Wrong! Your guess is too high!"
guess_number ()

// Start the first guess
guess_number ()

run_lift <| eval_state 0 <| start ()
``````

There are only two new things here, first we add `state : State Int` to the effect row to indicate that we want to use a state consisting of a single integer.

The second addition is to add `eval_state 0` before the `run_lift` call. This initializes the state with `0` and then evaluates the effect.

Notably we haven't actually used the state in any way except initializing it so lets actually use it. To work with the state we have the basic `get`, `put` and `modify` actions.'

``````let io @ { ? } = import! std.io
let random = import! std.random
let int = import! std.int
let string = import! std.string
let { Result } = import! std.result
let { Ordering, compare } = import! std.cmp

let { (<|) } = import! std.function
let effect @ { Eff, ? } = import! std.effect
let { Lift, run_lift, lift, ? } = import! std.effect.lift
let { State, eval_state, modify, get, ? } = import! std.effect.state

let start _ : () -> Eff [| lift : Lift IO, state : State Int | r |] () =
do _ = lift <| io.println "Guess a number between 1 and 100!"
do target_number = lift <| random.thread_rng.gen_int_range 1 101

let guess_number _ : () -> Eff [| lift : Lift IO, state : State Int | r |] () =
do line = lift <| io.read_line
match int.parse (string.trim line) with
| Err _ ->
do _ = lift <| io.println "That is not a number!"
guess_number ()
| Ok guess ->
// Increment the guess counter by one
do _ = modify ((+) 1)
match compare guess target_number with
| EQ ->
// Retrieve the guess so we can output it
do guesses = get
lift <| io.println ("Correct! You got it in " ++ show guesses ++ " guesses!")
| LT ->
do _ = lift <| io.println "Wrong! Your guess is too low!"
guess_number ()
| GT ->
do _ = lift <| io.println "Wrong! Your guess is too high!"
guess_number ()

// Start the first guess
guess_number ()

run_lift <| eval_state 0 <| start ()
``````

We only needed to add two things here, first we add one on every guess using `modify` then we use `get` to retrieve the final amount when the player succeeded.

And that is all we had to do to add the guess tracking. The strength of extensible effects is precisely that there is very little work needed to add additional side effects to the program, despite the program as a whole still being entirely pure!

Just to show the strength of effects one last time we add the `Error` effect to move the error handling outside of the main logic.

``````let io @ { ? } = import! std.io
let random = import! std.random
let int = import! std.int
let string = import! std.string
let { Result, map_err } = import! std.result
let { Ordering, compare } = import! std.cmp

let { (<|) } = import! std.function
let effect @ { Eff, ? } = import! std.effect
let { Lift, run_lift, lift, ? } = import! std.effect.lift
let { State, eval_state, modify, get, ? } = import! std.effect.state
let { Error, ok_or_throw, run_error, catch, ? } = import! std.effect.error

type GuessError =
| NotANumber

let start _ : () -> Eff [| lift : Lift IO, state : State Int, error : Error GuessError | r |] () =
do _ = lift <| io.println "Guess a number between 1 and 100!"
do target_number = lift <| random.thread_rng.gen_int_range 1 101

let guess_number _ : () -> Eff [| lift : Lift IO, state : State Int, error : Error GuessError | r |] () =
do line = lift <| io.read_line
do guess =
ok_or_throw
<| map_err (\_ -> NotANumber)
<| int.parse
<| string.trim line
do _ = modify ((+) 1)
match compare guess target_number with
| EQ ->
do guesses = get
lift <| io.println ("Correct! You got it in " ++ show guesses ++ " guesses!")
| LT ->
do _ = lift <| io.println "Wrong! Your guess is too low!"
guess_number ()
| GT ->
do _ = lift <| io.println "Wrong! Your guess is too high!"
guess_number ()

// Start the first guess
catch (guess_number ()) <| \err ->
match err with
| NotANumber ->
do _ = lift <| io.println "That is not a number!"
guess_number ()

run_lift <| run_error <| eval_state 0 <| start ()
``````