5.6

Commands

We use commands to execute operations that have side effects. Like messages, commands are also represented as data. Think of them as data structures that store what needs to happen along with what data will be needed. We then hand the command over to the Elm runtime. The runtime will execute it and notify our application with a result. That sounds really vague, doesn’t it? Let’s look at an example for generating random numbers to make things a bit more concrete.

Generating Random Numbers

There are two main approaches to generating random numbers: True Random Number Generators (TRNGs) and Pseudo-Random Number Generators (PRNGs). TRNGs generate numbers from truly random physical phenomena, for example the little variations in someone’s mouse movements or the point in time at which a radioactive material decays or the atmospheric noise picked up by a radio.

Due to their reliance on a physical phenomena, TRNGs take considerably longer time to generate random numbers. Therefore, most computer programs that need random numbers quickly tend to rely on PRNGs which are much more efficient than TRNGs. Despite their efficiency, PRNGs are not suitable for applications that need truly unpredictable random numbers, such as encryption key generators.

PRNGs take an initial value (called seed) and apply an algorithm to generate a seemingly random number. The modern algorithms used by PRNGs are so good that the numbers generated by them look quite random.

However, if we use the same initial seed multiple times we will get the exact same number each time.

Generating Random Numbers without Side Effects

Let’s explore this behavior of PRNGs further by writing some Elm code in elm-repl. We’ll start by generating an initial seed.

> import Random

> seed0 = Random.initialSeed 31415
Seed { state = State 31416 1, next = <function>, split = <function>, range = <function> }
    : Random.Seed

The initialSeed function from the Random module takes an integer and returns a seed. We randomly picked the number 31415 as an input. Here’s what the initialSeed function’s type signature looks like:

initialSeed : Int -> Seed

The Seed type is defined in the Random module like this:

type Seed
    = Seed
        { state : State
        , next : State -> ( Int, State )
        , split : State -> ( State, State )
        , range : State -> ( Int, Int )
        }


type State
    = State Int Int

We haven’t seen a complicated union type like this before. The Seed type uses the same name for type and data constructors, which is perfectly valid in Elm. If we want to create a seed directly without using the initialSeed function we need to pass a record that contains values for state, next, split, and range properties. The process of creating a new seed like this is fairly complex. That’s why the Random module provides the initialSeed function which hides all of that complexity from us.

The good news is, we won’t need to create a seed directly without using the initialSeed function or understand how the Seed type works to be able to generate random numbers in Elm. Nonetheless, I wanted to cover it briefly here so that you can make sense of the output generated by the initialSeed function we saw earlier.

Seed
    { state = State 31416 1
    , next = <function>
    , split = <function>
    , range = <function>
    } : Random.Seed

Now that we have an initial seed, we can use the Random.step function to generate a random value.

> Random.step (Random.int 0 100) seed0

The output from that expression is hard to read. Here’s how it looks after some formatting:

( 48,
  Seed
     { state = State 1257079824 40692
     , next = <function>
     , split = <function>
     , range = <function>
     }
) : ( Int, Random.Seed )

The above output is a tuple. The first element is a random number and the second element is the seed we can use to generate the next random number. As mentioned earlier, we need to provide a different seed to the step function each time we want to generate a new random number. Otherwise, we keep getting the same number over and over again. That’s why the step function returns a new seed in addition to a random number.

The step function takes a random number generator and a seed as inputs. Here’s what its type signature looks like:

step : Generator a -> Seed -> (a, Seed)

The Random.int 0 100 expression we typed into the repl earlier creates a generator that produces random 32-bit integers between 0 and 100.

> Random.int 0 100
Generator <function> : Random.Generator Int

It’s important to understand that the Random.int function itself doesn’t generate a random number. It returns a generator which can be passed to other functions such as step that will in turn ask that generator to produce random numbers. Think of a generator as a recipe for generating certain types of random values. So Random.int 0 100 describes how to create a recipe for generating integers between 0 and 100. Similarly, Random.float 0 1 describes how to create a recipe for generating floating-point numbers between 0 and 1.

Next, we’ll capture the new seed returned by the step function so that we can use it to generate a different random number.

> result = Random.step (Random.int 0 100) seed0
(48,Seed { state = State 1257079824 40692, next = <function>, split = <function>, range = <function> })
    : ( Int, Random.Seed )

> randomNumber = Tuple.first result
48 : Int

> newSeed = Tuple.second result
Seed { state = State 1257079824 40692, next = <function>, split = <function>, range = <function> }
    : Random.Seed

Now, we’re ready to generate a new random number.

> Random.step (Random.int 0 100) newSeed

Here’s the output after some formatting:

( 26,
  Seed
     { state = State 284581387 1655838864
     , next = <function>
     , split = <function>
     , range = <function>
     }
) : ( Int, Random.Seed )

This time we got 26 as the new number. Last time it was 48. The numbers inside the state property also changed. If those numbers didn’t change we would get the same number again and again. What this means is that the Random.step function is pure and doesn’t generate any side effects. It returns the exact same output given the same input.

Generating Random Numbers with Side Effects

This process of manually providing seed values, as we’ve done so far, has some drawbacks. First, it is cumbersome. Second, it makes the random number sequence predictable. Elm provides a way to get rid of these hurdles by picking an unpredictable initial seed value by default. It does so by using the current time generated by a real-time clock embedded in our computers to produce seed values.

As soon as we give Elm the responsibility of picking the initial seed value, the random number generation process causes side effects. To get the initial seed value, it has to now interact with the real-time clock, which is part of the outside world. This is where commands come in.

To generate random numbers with side effects we need to follow the Elm Architecture. It’s hard to write a full fledged program in repl, so we’ll create a new file called RandomNumber.elm in the beginning-elm/elm-examples directory.

Model

The first thing we need to define is our model. Add the following code to RandomNumber.elm.

module RandomNumber exposing (..)


type alias Model =
    Int

All we need to keep track of here is a number. The above model looks exactly like the one we defined for the counter app in Model View Update - Part 1 section. Next, we need to create an initial model. Add the following code to the bottom of RandomNumber.elm.

init : ( Model, Cmd msg )
init =
    ( 0, Cmd.none )

When commands are involved, the code for initializing our model becomes a little more complex. In the counter app, all we had to do was return an integer as shown below.

initialModel : Model
initialModel =
    0

Let’s go through each change one by one.

Change #1: The function name changed from initialModel to init since we’re doing more than just initializing a model.

Change #2: The init function now returns a tuple. The first element represents the model and the second element represents commands. In addition to providing an initial value for our model, we need to tell the Elm runtime what commands to run when it loads our app. Since we don’t want to run any commands in the beginning, we simply return Cmd.none. The Cmd type is defined in the Platform.Cmd module and it represents commands in Elm. none is a constant also defined in the Platform.Cmd module. It returns an empty list of commands.

Let’s say we need to restore the state of our app when it’s launched. The state from the previous run could have been stored in a local storage or a remote server. Both of those lie outside the boundary of our app. Therefore, we need to rely on the Elm runtime to retrieve the state by returning a command from the init function.

Change #3: The type annotation has also changed to init : ( Model, Cmd msg ). We already know what Model is. Cmd msg means a command that will notify our app with results by sending messages of type msg. msg is just a type variable, so it doesn’t represent a concrete type. Later in this section, we’ll define a type called Msg to make it clear which messages can be sent to our app.

View

Next, we need to present our initial model to the user. Add the following code to the bottom of RandomNumber.elm.

view : Model -> Html msg
view model =
    div []
        [ button [] [ text "Generate Random Number" ]
        , text (toString model)
        ]

Our view is very simple. All we’re displaying is a button and the string representation of a random number. We also need to import the Html module in RandomNumber.elm.

module RandomNumber exposing (..)

import Html exposing (..)
.
.

Application Entry Point

To display the view, we need to define an entry point to our app. Add the following code to the bottom of RandomNumber.elm.

main : Program Never Model msg
main =
    program
        { init = init
        , view = view
        , update = update
        , subscriptions = (\_ -> Sub.none)
        }

With the introduction of commands, the main function also became slightly more complex. Here’s how the main function looked in the counter app:

main : Program Never Model msg
main =
    beginnerProgram
        { model = initialModel
        , view = view
        , update = update
        }

We’re now using the program function defined in the Html module instead of beginnerProgram to wire everything up. program takes a record with four properties. We’re already familiar with view and update. The init property represents the initial model and a list of commands to run when the app is launched.

subscriptions represents things we want to listen to, for example web socket messages and location changes. By assigning (\_ -> Sub.none), we let the Elm runtime know that we aren’t interested in listening to anything right now. We’ll cover subscriptions in detail later in this chapter.

The only thing remaining to define is the update function. Add the following code right above the main function in RandomNumber.elm.

update : msg -> Model -> ( Model, Cmd msg )
update msg model =
    init


main =
    ...

The update function simply returns the output from the init function. We’ll expand it to be more meaningful in a bit. Finally, we’re ready to display our view. Run elm-reactor from the beginning-elm directory in terminal if it’s not running already, and go to this URL in your browser: http://localhost:8000/elm-examples/RandomNumber.elm. You should see a view that looks like this:

Update

Right now, the Generate Random Number button doesn’t do anything. Let’s define a message that will get triggered when that button is clicked. Add the following type definition right above the update function in RandomNumber.elm.

type Msg
    = GenerateRandomNumber


update =
    ...

Next, we need to modify the update function so that it can respond to the GenerateRandomNumber message.

update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        GenerateRandomNumber ->
            ( model, Random.generate NewRandomNumber (Random.int 0 100) )

The type annotation for the update function now uses the Msg type instead of msg, so don’t forget to make that change. If you don’t remember the difference between the msg type variable and the Msg union type, you might want to refresh your memory by reading the Model View Update - Part 1 section again.

The update function now returns a tuple containing a model and commands instead of just the model. Notice how the update and init (or initialModel in the previous sections) functions tend to have the same return types. Here’s the update function from the counter app for comparison:

update : Msg -> Model -> Model
update msg model =
    case msg of
        Increment ->
            model + 1

        Decrement ->
            model - 1

Let’s understand what’s going on in the body of the new update function. When the GenerateRandomNumber message is received, we return an unmodified model and a command for generating a random number. The command is generated by using the generate function defined in the Random module.

In Elm, we don’t tend to create commands directly through the use of some constructor function. Instead, we rely on functions like generate. Here’s another example: let’s say we want to create a command that sends an HTTP request to a remote server. We can use the Http.send function to generate that command. We just look for an appropriate function in a module and use it to create a command.

generate tries to achieve the same goal as the step function — generate a random number. The way it goes about achieving that goal, however, is very different. step uses a generator and a seed to return a random number instantaneously.

> Random.step (Random.int 0 100) seed0

generate on the other hand, takes a generator and tells the Elm runtime to run that generator. It also gives the runtime the name of the message the runtime should send when the number is ready. We haven’t defined the NewRandomNumber message yet; we’ll do that soon.

Why do we have to give the generate function a message? Why can’t it just return a random number immediately like the step function did? It’s due to the fact that we aren’t manually providing an initial seed value anymore. We need to generate the seed by using our computer’s clock, which produces side effects.

To run things that generate side effects, Elm requires us to create a command along with any pertinent information that command needs. We then hand that command over to the runtime. The runtime executes the command and notifies our application with the result by sending the message included in the command.

Here’s what the generate function’s type signature looks like:

generate : (a -> msg) -> Generator a -> Cmd msg

The first argument is a function that takes a value and wraps it in a message. In our case, that’s NewRandomNumber. Remember, messages that take an argument are essentially functions. If you check the type of NewRandomNumber in repl, you will see that it takes an integer and returns a message of type Msg.

> type Msg = NewRandomNumber Int

> NewRandomNumber
<function> : Int -> Repl.Msg

The second argument to the generate function is a random number generator. In our case, it’s Random.int, which takes the range as inputs (e.g., 0 100). Lastly, the generate function returns a command that encapsulates the message and the generator. The next step is to add NewRandomNumber to the Msg type and also handle it in the update function as shown below.

type Msg
    = GenerateRandomNumber
    | NewRandomNumber Int


update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        GenerateRandomNumber ->
            ( model, Random.generate NewRandomNumber (Random.int 0 100) )

        NewRandomNumber number ->
            ( number, Cmd.none )

When the NewRandomNumber message is received, we simply return the number in payload as our model and an empty list of commands. We also replaced msg with the Msg type in the update function’s type annotation. Let’s import the Html.Events and Random modules next and add an onClick attribute to our button so that the GenerateRandomNumber message gets triggered when the button is clicked.

module RandomNumber exposing (..)

import Html exposing (..)
import Html.Events exposing (onClick)
import Random
.
.
view : Model -> Html Msg
view model =
    div []
        [ button [ onClick GenerateRandomNumber ]
            [ text "Generate Random Number" ]
        , text (toString model)
        ]

Don’t forget to replace msg with the Msg type in the view function’s type annotation. We actually need to do that replacement in the init and main functions as well.

init : ( Model, Cmd Msg )
init =
    ...
.
.
main : Program Never Model Msg
main =
    ...

Now we’re ready to test our program. Refresh the page at http://localhost:8000/elm-examples/RandomNumber.elm and you should be able to generate random numbers now.

Summary

In this section, we learned how to use commands to execute operations that cause side effects. Here is how the Elm Architecture looks with the introduction of commands:

The interaction between the Elm runtime and our code has also become a bit more complex now that we are using commands. The sequence diagram below shows that interaction.

Commands are an important concept in Elm. We need to understand them well in order to be able to build complex applications in Elm. In the next section, we’ll continue to explore how commands can help us execute one of the most common operations performed in web apps — sending and receiving data from a remote HTTP server — without sacrificing our application code’s purity.

Back to top

New chapters are coming soon!

Sign up for the Elm Programming newsletter to get notified!

* indicates required
Close