Instantly share code, notes, and snippets.

# tkshill/DelayedGenerator.elm

Last active Nov 9, 2020
Mimicking Choice with Delayed Generators

# Creating Delayed Random Generator Commands in Elm

In this brief article, we look at using Tasks, Generators and Msg constructors in Elm to similate the effect of a computer opponent in a game, "thinking" about a choice.

Although the implications and paradigms referred to are relatively language agnostic, the actual solution implementation is written in the Elm, and the article assumes some knowledge of the programming language and the basics of the Model-View-Update style of User Interface design.

Tl;dr - If you'd rather just go straight to the working solution, just check out this Ellie.

## Context

Sometimes in programming, we want to intentionally delay the effect, or response from a particular computation. One such case is when you want to simulate to the user than the computer/app device they're interacting with is thinking. This concept of a thinking or pondering machine has been especially important to me as I've been working on my implementation of Quarto, a sort've advanced tic tac toe variant.

In Quarto, players can challenge themselves against a computer opponent, who at the moment, plays randomly available pieces randomly on the board.

## Making simple generators

First, let's look at the stardard random generator.

To illustrate our example today, we're going to be using the concept of playing cards. Below I've added a snippet of code from the elm-lang Random cards example, trimming way the parts unnecessary to our exploration today.

Unlike in other less strict languages like python or javascript, Elm can't simply produce a random number on the fly. All Elm functions are pure, meaning the same input should produce the same output every time. By definiton, the concept of randomness is inherently impure, since a random when given the same inputs, produces many different outputs. So we need a slightly more sophisticated method to get our random numbers.

### The Model type

```-- MODEL
type Card
= Ace
| Two
| Three
| Four
| Five
| Six
| Seven
| Eight
| Nine
| Ten
| Jack
| Queen
| King

type alias Model =
{ card : Card
}```

Above we see that we define a union type called `Card`, with each possible value of the `Card` type representing the potential value of a real life playing card.

Our model, the state of our application, is a record with a single field, `card`, which stores any one possible value of our `Card` type.

### The Msg type

```-- UPDATE
type Msg
= Draw
| NewCard Card```

Next we define the messages that can be passed around our app with a type called `Msg`. In Elm, passing messages are the only way to trigger updates to the app.

• Our first Msg, `Draw` is what we'll use to trigger our card generator function.
• Our second Msg, `NewCard Card` is the message that our random card generator will return once it's found a card from the list. Note that this message has Card as an associated type.

Next let's look at our actual randomness function.

### The Random Generator type

```-- Our generator function
cardGenerator : Random.Generator Card
cardGenerator =
Random.uniform Ace
[ Two
, Three
, Four
, Five
, Six
, Seven
, Eight
, Nine
, Ten
, Jack
, Queen
, King
]```

Here we define a function cardGenerator that uses the elm/Random API to make a random generator. Now, we don't really need to get into how generators work behind the scenes (fancy maths), we just have to understand that the `Random.uniform` function is a function that accept some data type `a`, where a can be anything, and a `List of a` and will return a random `Generator a`. So in this scenario, `a` is our `Card` type.

Now so far, we have a `Model` that holds cards, a `Generator` that produces cards, and a `Msg` that passes cards, but now we need to put them together.

### The Update function

```update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
case msg of
Draw ->
( model
, Random.generate NewCard cardGenerator
)

NewCard newCard ->
( Model newCard
, Cmd.none
)```

So finally we have the `update` function. In Elm, the update function is the only way we can update the model. Note the type signature (always start with the type signature), `Msg -> Model -> (Model, Cmd Msg)`.

`Update` accepts a `Msg` and the existing `Model`, and returns an updated model and a `Cmd`, which is basically some external task for the app to perform.

So let's break down our update function

• If a `Draw` message is passed to the application, the model doesn't change but a command is sent using the `Random.generate` function.
• `Random.generate` accepts a function that takes some type `a` and returns a Msg, then a `Generator` of that `a`, runs the generator, and passes the returns value to the msg constructor.
• The reason why `Random.generate` is a cmd is because the Elm runtime has to reach outside the app state to get a number to `seed` the random function.
• Once the generator produces it's value, it turns it to the `NewCard` branch of the update function, where our model is updated with that newly generated value.

If you want to see a the full version of how this works with the rest of the bells and whistles, you can check it out here.

## The Delay

So now we have a working random generator, and with the speed of modern computers, the generation happens pretty quick. Too quick. Our computer is too smart! Or more honestly, it's really troubling for the human brain to process that a sequence has happened that it can't even see. So how do we pause an application?

We put it to sleep!

Let's make a delay function that can delay the progression of the application. What do we need to do this.

### Process.Sleep

Firstly, we need the Process.sleep function. `Sleep` accepts a `Float` and returns a `Task`. For now, let's treat a task as just a promise to do a thing. We can request that Elm perform tasks, and tasks could either succeed or fail. Typically when they succeed, they return a value to the app through our delightful `Msg` type again. `Sleep` however, returns the type `() unit`, or essentially nothing. It just y'know, sleeps.

So `sleep 1000.00` produces a task that "sleeps" for 1000 milliseconds.

### Time.Now

The next thing we'll need is the Time.now function, which like, sleep, produces a task, but also returns a `Posix time` value, representing the current time. Posix time isn't necessary useful for us, so we'll also use `Time.posixToMillis` to convert the time from `Time.now` to an integer.

### Random.step

The final unique function we're gonna look at is the Random.step.

#### Manual Generators

Earlier on, we used `Random.generate` function to run our generator. But now, we want to chain a generator into a sleep process. Unfortunately, because of this we can't use generate anymore, since Elm commands are run in parallel, and cant be chained the way we want.

Fortunately, there's a way to produce a random function without using a command, and that's by passing in the inital seed ourserlves. Given a generator and an initial seed, the `step` function can run our generator and produce a random value (as well as a new seed in case we need to keep generating random numbers).

Using all this knowledge, we can define first, a function `generateCard`

```generateCard : Int -> Msg
generateCard seedNum =
seedNum
|> Random.initialSeed
|> Random.step cardGenerator
|> (\( value, _ ) -> NewCard value)```
• This function accepts an `Int` and uses the step function to make a new value (no commands required) and then creates our `NewCard` msg with that value.
• Note we make use of another `Random` function, `initialSeed`, which when given an `Int`, returns a `Seed` type that can be consumed by the step function.
• Step returns a tuple of the generated value and a new seed. We keep the value, and pass it into the `NewCard` constructor.

And then we can finally define a `delay` function:

```delayGenerator : Cmd Msg
delayGenerator =
Process.sleep 1000.00
• Our `delayGenerator` function chains our `sleep` `Task` into the `Time.now` `Task` using a helper functon `andThen`, who's implementation is beyond the scope of this article.

We can essentially read this function as

• Run the sleep task and wait for three seconds
• Ignore the value sleep passes back, and run the `now` function to get the current time
• call `Time.perform` (similar to `Random.generate`, `perform` can resolve these promises to do tasks into actual values
• perform will pass the posix value from `now` to `posixToMillis` to get an `Int`.
• And finally that integer is passed to our `generateCard` function, and pops out at the end a random card, 1000.00 milliseconds after delay is called.

And the best part is, our new update function doesn't change much at all.

```update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
case msg of
Draw ->
( { model | status = "Choosing a card..." }
, delayGenerator
)

NewCard newCard ->
( Model newCard "Card chosen. Draw again?"
, Cmd.none```

Note that all we had to do was change our `Cmd Msg` in the `Draw` branch from `Random.generate` to our new `delayGenerator` `Task` maker.

## Wrap Up

And that's it. I recognize that this might not all make perfect sense from the beginning, and that's okay. But hopefully I at least brought some context to how you can build these types of chains of commands in a logical fashion, combining matching functions and types.

I've attached a full example here that shows the whole thing in action. It's also available in this gist

I also highly recommend reading the documentation on random functions and tasks to get more context.

Also, if you want to see this type of concept out in the wild, I'm going to shamelessly plug my small game app Quarto where you can look at this type of code in action, and maybe even contribute if you'd like. I've been offering pair programming sessions whenever I can to folks looking to learn elm and contribute to open source.