Generating random number in ELM
is tricky. We can’t call a function Math.random()
like in JavaScript.
ELM by design support pure functions only. If you need to do anything impure you have to
use Cmd
, Msg
and Update
means you have to use ELM runtime to the impure job.
To make our life easier ELM 0.19 comes with elm/random package.
elm/random
gives a Generator
which ask ELM runtime to generate a random number and pass back as Update
Msg
.
Random.generate
will accept the Msg
with 1 argument and a function with describe the type and range of the random.
type Msg = RandomNumber int
Random.generate RandomNumber (Random.int 0 10)
The above example is to generate random integer between 0 & 10. Since instead of returning value, Random.generate
will send the message RandomNumber
with random integer generated. To receive and handle this generated random integer we should have update
function as below
update msg model ->
case msg of
RandomNumber rn ->
-- use the random number rn
Random Item from list
Lets consider we have a list of items and we need to pick item randomly from this list.
type alias Char =
{ text: String }
type alias Flags =
{ }
type alias Model =
{ selected : Maybe Char, chars: List Char }
initialModel : Model
initialModel =
{ selected = Nothing
, chars = [
{text= "A"}
,{text= "B"}
,{text = "C"}
,{text= "D"}
,{text= "E"}
]
}
selecting random item can be trigger in multiple ways like a button click
or on app mount
.
For this post let pick the random item on a button click
.
renderText : Maybe Char -> Html Msg
renderText selected =
case selected of
Just char ->
text char.text
Nothing ->
text "Please click on Random Button"
view : Model -> Html Msg
view model =
div []
[ button [ onClick FindRandom ] [ text "Random" ]
, div [] [ (renderText model.selected) ]
]
Now we have a button with Random
text which will send Cmd FindRandom
.
Next lets handle the FindRandom
in the update.
type Msg
= FindRandom
update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
case msg of
FindRandom ->
(model, Random.generate RandomNumber (Random.int 0 (List.length model.chars - 1)))
Once the update method receive the Msg
FindRandom
it will trigger the Random.generate
with boundary 0 & no.of item in the list.
ELM runtime generate random number in the given boundary and will send the Msg RandomNumber
with the generated number.
Next lets handle that. The above code will become,
type Msg
= FindRandom
| RandomNumber Int
update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
case msg of
FindRandom ->
(model, Random.generate RandomNumber (Random.int 0 (List.length model.chars - 1)))
RandomNumber rn ->
let
selected = Array.fromList model.chars
|> Array.get rn
in
({ model | selected = selected }, Cmd.none)
Once the update receives RandomNumber
with the generated number, we have to pick Item from list. Since we don’t have index for list we need to convert th list into array and use the generated number as index to get the List Item. Once we get the List Item we update the model.selected
value and that will get rendered in the view.
The whole code will look like,
module Main exposing (main)
import Browser
import Html exposing (Html, button, div, text)
import Html.Events exposing (onClick)
import Random
import Array
type alias Char =
{ text: String }
type alias Flags =
{ }
type alias Model =
{ selected : Maybe Char, chars: List Char }
initialModel : Model
initialModel =
{ selected = Nothing
, chars = [
{text= "A"}
,{text= "B"}
,{text = "C"}
,{text= "D"}
,{text= "E"}
]
}
type Msg
= FindRandom
| RandomNumber Int
update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
case msg of
FindRandom ->
(model, Random.generate RandomNumber (Random.int 0 (List.length model.chars - 1)))
RandomNumber rn ->
let
selected = Array.fromList model.chars
|> Array.get rn
in
({ model | selected = selected }, Cmd.none)
renderText : Maybe Char -> Html Msg
renderText selected =
case selected of
Just char ->
text char.text
Nothing ->
text "Please click on Random Button"
view : Model -> Html Msg
view model =
div []
[ button [ onClick FindRandom ] [ text "Random" ]
, div [] [ (renderText model.selected) ]
]
init : Flags -> (Model, Cmd Msg)
init flags =
(initialModel, FindRandom)
main : Program Flags Model Msg
main =
Browser.element
{ init = init
, view = view
, update = update
, subscriptions = always Sub.none
}
This is an extracted version which used in random gstin app and its source is available on github.com/revathskumar/random-gstin
The Running version is available on ellie-app.
Versions of Language/packages used in this post.
| Library/Language | Version |
| ---------------- |---------|
| ELM | 0.19.0 |
| elm/random | 1.0.0 |