Random

So far we have only seen commands to make HTTP requests, but we can command other things as well, like generating random values! So we are going to make an app that rolls dice, producing a random number between 1 and 6.

Click the blue “Edit” button to see this example in action. Generate a couple random numbers, and look through the code to try to figure out how it works. Click the blue button now!

Edit

  1. import Browser
  2. import Html exposing (..)
  3. import Html.Events exposing (..)
  4. import Random
  5. -- MAIN
  6. main =
  7. Browser.element
  8. { init = init
  9. , update = update
  10. , subscriptions = subscriptions
  11. , view = view
  12. }
  13. -- MODEL
  14. type alias Model =
  15. { dieFace : Int
  16. }
  17. init : () -> (Model, Cmd Msg)
  18. init _ =
  19. ( Model 1
  20. , Cmd.none
  21. )
  22. -- UPDATE
  23. type Msg
  24. = Roll
  25. | NewFace Int
  26. update : Msg -> Model -> (Model, Cmd Msg)
  27. update msg model =
  28. case msg of
  29. Roll ->
  30. ( model
  31. , Random.generate NewFace (Random.int 1 6)
  32. )
  33. NewFace newFace ->
  34. ( Model newFace
  35. , Cmd.none
  36. )
  37. -- SUBSCRIPTIONS
  38. subscriptions : Model -> Sub Msg
  39. subscriptions model =
  40. Sub.none
  41. -- VIEW
  42. view : Model -> Html Msg
  43. view model =
  44. div []
  45. [ h1 [] [ text (String.fromInt model.dieFace) ]
  46. , button [ onClick Roll ] [ text "Roll" ]
  47. ]

The new thing here is command issued in the update function:

  1. Random.generate NewFace (Random.int 1 6)

Generating random values works a bit different than in languages like JavaScript, Python, Java, etc. So let’s see how it works in Elm!

Random Generators

We are using the elm/random package for this. The Random module in particular.

The core idea is that we have random Generator that describes how to generate a random value. For example:

  1. import Random
  2. probability : Random.Generator Float
  3. probability =
  4. Random.float 0 1
  5. roll : Random.Generator Int
  6. roll =
  7. Random.int 1 6
  8. usuallyTrue : Random.Generator Bool
  9. usuallyTrue =
  10. Random.weighted (80, True) [ (20, False) ]

So here we have three random generators. The roll generator is saying it will produce an Int, and more specifically, it will produce an integer between 1 and 6 inclusive. Likewise, the usuallyTrue generator is saying it will produce a Bool, and more specifically, it will be true 80% of the time.

The point is that we are not actually generating the values yet. We are just describing how to generate them. From there you use the Random.generate to turn it into a command:

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

When the command is performed, the Generator produces some value, and then that gets turned into a message for your update function. So in our example, the Generator produces a value between 1 and 6, and then it gets turned into a message like NewFace 1 or NewFace 4. That is all we need to know to get our random dice rolls, but generators can do quite a bit more!

Combining Generators

Once we have some simple generators like probability and usuallyTrue, we can start snapping them together with functions like map3. Imagine we want to make a simple slot machine. We could create a generator like this:

  1. import Random
  2. type Symbol = Cherry | Seven | Bar | Grapes
  3. symbol : Random.Generator Symbol
  4. symbol =
  5. Random.uniform Cherry [ Seven, Bar, Grapes ]
  6. type alias Spin =
  7. { one : Symbol
  8. , two : Symbol
  9. , three : Symbol
  10. }
  11. spin : Random.Generator Spin
  12. spin =
  13. Random.map3 Spin symbol symbol symbol

We first create Symbol to describe the pictures that can appear on the slot machine. We then create a random generator that generates each symbol with equal probability.

From there we use map3 to combine them into a new spin generator. It says to generate three symbols and then put them together into a Spin.

The point here is that from small building blocks, we can create a Generator that describes pretty complex behavior. And then from our application, we just have to say something like Random.generate NewSpin spin to get the next random value.

Exercises: Here are a few ideas to make the example code on this page a bit more interesting!

  • Instead of showing a number, show the die face as an image.
  • Instead of showing an image of a die face, use elm/svg to draw it yourself.
  • Create a weighted die with Random.weighted.
  • Add a second die and have them both roll at the same time.
  • Have the dice flip around randomly before they settle on a final value.