I'm trying to generate random UUID's v4 within a loop:
randomUuid =
-- TODO: find a way to generate random uuid for variableId
updatedVariables =
group.variables |> List.map (\variable -> { variable | id = randomUuid })
I read the doc of elm/random and elm/uuid but could not find how to generate an UUID without using a seed.
The only thing I could do is:
newUuid : Random.Seed -> ( String, Random.Seed )
newUuid seed =
seed
|> Random.step UUID.generator
|> Tuple.mapFirst UUID.toString
I see that elm/random as an independentSeed
function but I cannot get it to generate a seed.
The node equivalent of what I'm trying to achieve with randomUuid
is:
const { uuid } = require('uuidv4');
const randomUuid = uuid();
I feel like I might be missing some important concept in Elm here but cannot figure that one on my own. Any help or pointer would be greatly appreciated.
Generating random values is an effect and as such a pure language cannot just perform it.
However, there is a pure version of randomness, which is using random seeds. These have the property that every time you generate a value using the same seed, you get the same value - hence this is just a pure computation and is completely ok in a pure context.
Elm allows you to execute effects as Cmd
, the thing you return from your init
and update
functions. So one option you have is to always return Random.generate GotANewUUID UUID.generator
before you need it, then perform your computation when you handle the GotANewUUID
msg.
The other option is to keep track of the random seed. You either start with a deterministic one with Random.initialSeed
(probably not what you want with UUIDs, as they would be exactly the same on every run of your program), or in your init
function you return Random.generate GotNewSeed Random.independentSeed
. Then you store the seed in your model.
Every time you need to generate a new UUID, you use your newUuid
function above, making sure to store the new seed.
Here's an example:
import Random
import UUID
type alias Thing =
{ id : String
-- , some other stuff
}
type alias Model =
{ seed : Random.Seed
, stuff : List Thing
}
type Msg
= GotNewSeed Random.Seed
| AddAThing Thing
| AddABunchOfThings (List Thing)
init : () -> (Model, Cmd Msg)
init flags =
({ seed = Random.initialSeed 567876567
-- Let's start with a deterministic seed
-- so you don't need to deal with Maybe Seed later
, stuff = []
}, Random.generate GotNewSeed Random.independentSeed
)
update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
case msg of
GotNewSeed seed ->
({model | seed = seed}, Cmd.none)
AddAThing thing ->
let
(newId, newSeed) =
newUuid model.seed
in
({ model | stuff = { thing | id = newId } :: model.stuff
, seed = newSeed }
, Cmd.none
)
AddABunchOfThings things ->
let
(newStuff, newSeed) =
List.foldl (\thing (stuff, seed) ->
newUuid seed
|> Tuple.mapFirst (\id ->
{ thing | id = id } :: stuff
)
) (model.stuff, model.seed) things
in
({model | stuff = newStuff, seed = newSeed}, Cmd.none)