I am using the "compose" pattern in Elm.
In Main.elm, I am generating an initial seed for Random using the following:
type alias Model =
{ ...
, seed : Seed
}
initialModel : Model
initialModel =
{ ...
, seed = initialSeed 0
}
init : ( Model, Cmd Msg )
init =
( initialModel, generate InitializeSeed (int minInt maxInt) )
type Msg
= ...
| InitializeSeed Int
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
...
InitializeSeed value ->
( { model | seed = Random.initialSeed value }, Cmd.none )
which seems to work well for initializing the random number generator seed to a random starting value.
I have an "independent" widget that is used in the main application. I would like to pass the seed down to that widget on receipt of the InitializeSeed message and then retrieve the value as it changes in the child widget (using Random.step) so that I can then update other widgets' seeds as they need the generator.
How can I generate a message for the child widget from the update function in Main so that I can pass the seed down to the child? How can the child return the updated seed back to the parent?
UPDATE:
I figured out how to pass the seed down to the child widget, although I am not certain that this is best way.
The child code looks like this:
type alias Model =
{ ...
, seed : Seed
}
replaceSeed : Model -> Seed -> Model
replaceSeed model seed =
{ model | seed = seed }
and in the parent, I modified the update function like this:
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
...
InitializeSeed value ->
let
seed =
Random.initialSeed value
child_ =
Child.replaceSeed model.child seed
in
( { model
| seed = seed
, child = child_
}
, Cmd.none
)
I am still looking for a way to have the child return the updated seed.
While the top level update function in any Elm program must be
update : Msg -> Model -> (Model, Cmd Msg)
there are no limitations on the shape of any of its descendents. So nothing prevents you defining for your child
update : Seed -> Msg -> Model -> (Model, Cmd Msg, Seed)
or, if the child never returns any commands
update : Seed -> Msg -> Model -> (Model, Seed)
Maybe you have several things to pass down, including for example a parent message. Then you could go as far as
type alias Config msg =
{ seed : Seed
, someParentMessage : String -> msg
}
update : Config msg -> Msg -> Model -> (Model, Maybe (Cmd msg), Seed)
You need to wire these all up appropriately in Main of course, but the compiler will help with that.
Related
While refactoring my F# code, I found a record with a field of type bool ref:
type MyType =
{
Enabled : bool ref
// other, irrelevant fields here
}
I decided to try changing it to a mutable field instead
// Refactored version
type MyType =
{
mutable Enabled : bool
// other fields unchanged
}
Also, I applied all the changes required to make the code compile (i.e. changing := to <-, removing incr and decr functions, etc).
I noticed that after the changes some of the unit tests started to fail.
As the code is pretty large, I can't really see what exactly changed.
Is there a significant difference in implementation of the two that could change the behavior of my program?
Yes, there is a difference. Refs are first-class values, while mutable variables are a language construct.
Or, from a different perspective, you might say that ref cells are passed by reference, while mutable variables are passed by value.
Consider this:
type T = { mutable x : int }
type U = { y : int ref }
let t = { x = 5 }
let u = { y = ref 5 }
let mutable xx = t.x
xx <- 10
printfn "%d" t.x // Prints 5
let mutable yy = u.y
yy := 10
printfn "%d" !u.y // Prints 10
This happens because xx is a completely new mutable variable, unrelated to t.x, so that mutating xx has no effect on x.
But yy is a reference to the exact same ref cell as u.y, so that pushing a new value into that cell while referring to it via yy has the same effect as if referring to it via u.y.
If you "copy" a ref, the copy ends up pointing to the same ref, but if you copy a mutable variable, only its value gets copied.
You have the difference not because one is first-value, passed by reference/value or other things. It's because a ref is just a container (class) on its own.
The difference is more obvious when you implement a ref by yourself. You could do it like this:
type Reference<'a> = {
mutable Value: 'a
}
Now look at both definitions.
type MyTypeA = {
mutable Enabled: bool
}
type MyTypeB = {
Enabled: Reference<bool>
}
MyTypeA has a Enabled field that can be directly changed or with other word is mutable.
On the other-side you have MyTypeB that is theoretically immutable but has a Enabled that reference to a mutable class.
The Enabled from MyTypeB just reference to an object that is mutable like the millions of other classes in .NET. From the above type definitions, you can create objects like these.
let t = { MyTypeA.Enabled = true }
let u = { MyTypeB.Enabled = { Value = true }}
Creating the types makes it more obvious, that the first is a mutable field, and the second contains an object with a mutable field.
You find the implementation of ref in FSharp.Core/prim-types.fs it looks like this:
[<DebuggerDisplay("{contents}")>]
[<StructuralEquality; StructuralComparison>]
[<CompiledName("FSharpRef`1")>]
type Ref<'T> =
{
[<DebuggerBrowsable(DebuggerBrowsableState.Never)>]
mutable contents: 'T }
member x.Value
with get() = x.contents
and set v = x.contents <- v
and 'T ref = Ref<'T>
The ref keyword in F# is just the built-in way to create such a pre-defined mutable Reference object, instead that you create your own type for this. And it has some benefits that it works well whenever you need to pass byref, in or out values in .NET. So you should use ref. But you also can use a mutable for this. For example, both code examples do the same.
With a reference
let parsed =
let result = ref 0
match System.Int32.TryParse("1234", result) with
| true -> result.Value
| false -> result.Value
With a mutable
let parsed =
let mutable result = 0
match System.Int32.TryParse("1234", &result) with
| true -> result
| false -> result
In both examples you get a 1234 as an int parsed. But the first example will create a FSharpRef and pass it to Int32.TryParse while the second example creates a field or variable and passes it with out to Int32.TryParse
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)
Problem
The input data has 2 types of records, lets call them R and W.
I need to traverse this data in Sequence from top to bottom in such a way that if the current record is of type W, it has to be merged with a map(lets call it workMap). If the key of that W-type record is already present in the map, the value of this record is added to it, otherwise a new entry is made into workMap.
If the current record is of type R, the workMap calculated until this record, is attached to the current record.
For example, if this is the order of records -
W1- a -> 2
W2- b -> 3
W3- a -> 4
R1
W4- c -> 1
R2
W5- c -> 4
Where W1, W2, W3, W4 and W5 are of type W; And R1 and R2 are of type R
At the end of this function, I should have the following -
R1 - { a -> 6,
b -> 3 } //merged(W1, W2, W3)
R2 - { a -> 6,
b -> 3,
c -> 1 } //merged(W1, W2, W3, W4)
{ a -> 6,
b -> 3,
c -> 5 } //merged(W1, W2, W3, W4, W5)
I want all the R-type records attached to the intermediate workMaps calculated until that point; And the final workMap after the last record is processed.
Here is the code that I have written -
def calcPerPartition(itr: Iterator[(InputKey, InputVal)]):
Iterator[(ReportKey, ReportVal)] = {
val workMap = mutable.HashMap.empty[WorkKey, WorkVal]
val reportList = mutable.ArrayBuffer.empty[(ReportKey, Reportval)]
while (itr.hasNext) {
val temp = itr.next()
val (iKey, iVal) = (temp._1, temp._2)
if (iKey.recordType == reportType) {
//creates a new (ReportKey, Reportval)
reportList += getNewReportRecord(workMap, iKey, iVal)
}
else {
//if iKey is already present, merge the values
//other wise adds a new entry
updateWorkMap(workMap, iKey, iVal)
}
}
val workList: Seq[(ReportKey, ReportVal)] = workMap.toList.map(convertToReport)
reportList.iterator ++ workList.iterator
}
ReportKey class is like this -
case class ReportKey (
// the type of record - report or work
rType: Int,
date: String,
.....
)
There are two problems with this approach that I am asking help for -
I have to keep track of a reportList - a list of R type records attached with intermediate workMaps. As the data grows, the reportList also grows and I am running into OutOfMemoryExceptions.
I have to combine reportList and workMap records in the same data structure and then return them. If there is any other elegant way, I would definitely consider changing this design.
For the sake of completeness - I am using spark. The function calcPerPartition is passed as argument for mapPartitions on an RDD. I need the workMaps from each partition to do some additional calculations later.
I know that if I don't have to return workMaps from each partition, the problem becomes much easier, like this -
...
val workMap = mutable.HashMap.empty[WorkKey, WorkVal]
itr.scanLeft[Option[(ReportKey, Reportval)]](
None)((acc: Option[(ReportKey, Reportval)],
curr: (InputKey, InputVal)) => {
if (curr._1.recordType == reportType) {
val rec = getNewReportRecord(workMap, curr._1, curr._2)
Some(rec)
}
else {
updateWorkMap(workMap, curr._1, curr._2)
None
}
})
val reportList = scan.filter(_.isDefined).map(_.get)
//workMap is still empty after the scanLeft.
...
Sure, I can do a reduce operation on the input data to derive the final workMap but I would need to look at the data twice. Considering that the input data set is huge, I want to avoid that too.
But unfortunately I need the workMaps at a latter step.
So, is there a better way to solve the above problem? If I can't solve problem 2 at all(according to this), is there any other way I can avoid storing R records(reportList) in a list or scan the data more than once?
I don't yet have a better design for the second question - if you can avoid combining reportList and workMap into a single data structure but we can certainly avoid storing R type records in a list.
Here is how we can re-write the calcPerPartition from the above question -
def calcPerPartition(itr: Iterator[(InputKey, InputVal)]):
Iterator[Option[(ReportKey, ReportVal)]] = {
val workMap = mutable.HashMap.empty[WorkKey, WorkVal]
var finalWorkMap = true
new Iterator[Option[(ReportKey, ReportVal)]](){
override def hasNext: Boolean = itr.hasNext
override def next(): Option[(ReportKey, ReportVal)] = {
val curr = itr.next()
val iKey = curr._1
val iVal = curr._2
val eventKey = EventKey(openKey.date, openKey.symbol)
if (iKey.recordType == reportType) {
Some(getNewReportRecord(workMap, iKey, iVal))
}
else {
//otherwise update the generic interest map but don't accumulate anything
updateWorkMap(workMap, iKey, iVal)
if (itr.hasNext) {
next()
}
else {
if(finalWorkMap){
finalWorkMap = false //because we want a final only once
Some(workMap.map(convertToReport))
}
else {
None
}
}
}
}
}
}
Instead of storing results in a list, we defined an iterator. That solved most of the memory issues we had around this issue.
I just want to see a random number. So here's an example straight out of the docs for the Random library. I expect Random.generate to accept a generator and a seed and return a tuple containing a random value and a new seed, as in:
generate : Generator a -> Seed -> (a, Seed)
-- Main.elm
import Random
seed0 = Random.initialSeed 31415
randomNumber = Random.generate (Random.int 0 10) seed0
main =
-- print result of randomNumber here
The compiler errors show two type mismatches:
-- TYPE MISMATCH ---------------------------------------------------- -----------
The 2nd argument to function `generate` is causing a mismatch.
5| Random.generate (Random.int 0 10) seed0
^^^^^
Function `generate` is expecting the 2nd argument to be:
Random.Generator a
But it is:
Random.Seed
The 1st argument to function `generate` is causing a mismatch.
5| Random.generate (Random.int 0 10) seed0
^^^^^^^^^^^^^^^
Function `generate` is expecting the 1st argument to be:
a -> b
But it is:
Random.Generator Int
What am I missing here?
The version of the docs you refer to is Core 1.0.0, which is old. Current version of Core is 4.0.5. (docs for Random here)
The function with the signature you are looking for is now named step:
step : Generator a -> Seed -> (a, Seed)
So your refactored code would look something like this:
import Html exposing (text)
import Random
seed0 = Random.initialSeed 31415
(randomNumber, nextSeed) = Random.step (Random.int 0 10) seed0
main =
text <| toString randomNumber
Here is the shortest example I can think of.
Because it is giving a constant seed, it will return same boolean.
If you need random number get produced at runtime, then you
have to use Random.generate which produces Cmd
so that elm runtime can get the randomness.
In this case, some form of Platform.Program
is needed because it is the only way to run Cmd.
import Html exposing (text)
import Random exposing (..)
main =
text <| toString <| Tuple.first <| step bool (initialSeed 1)
Elm 2022 (v0.19) answer:
Here's an absolute minimal example for generating a number between 0 and 1000 in Elm 0.19.1, and a runnable Ellie, which depends on elm/random. You wouldn't usually have all the () littered throughout, and instead you'd have Msg and Model, but in the interest of minimizing code:
module Main exposing (main)
import Browser
import Html
import Random
view : () -> Html.Html ()
view model =
let
-- this generates the rng number
generator =
Random.int 0 1000
-- used to seed the generator
seed =
Random.initialSeed 12345
-- Random.step returns the generated value, and a new seed.
-- Usually you store the newSeed on your model so you don't always generate the same value
( value, newSeed ) =
Random.step
generator
seed
in
Html.text <| String.fromInt value
main : Program () () ()
main =
Browser.sandbox
{ init = ()
, view = view
, update = \msg model -> model
}
Other best practices include storing some global seed on the model:
type alias Model = { globalSeed : Random.Seed }
and then using it and updating the one on the model after:
update : Msg -> Model -> (Model, Cmd.none)
update msg model =
case msg of
GenerateValue ->
let
(newValue, newSeed) =
Random.step (Random.int 0 1000) model.globalSeed
_ =
Debug.log "generated a new value" newValue
in
( {model | globalSeed = newSeed}, Cmd.none)
The documentation for Elm's Random module states:
A good way to get an unexpected seed is to use the current time.
http://package.elm-lang.org/packages/elm-lang/core/1.1.0/Random
I don't see however a good example of how to perform such initialization logic in FRP application. What signal should I react to? How to do this with a minimum of code and maximum of clarity.
There are different ways to do this. Each has it's own benefits. I'll give you the three that I know with a similar example for each.
1) Add a time ticker input
One thing you could do is add time to the inputs of your program. An example of a tiny program using the current time every second for a random number:
import Time
import Time (Time, second)
import Text (asText)
import Mouse
import Signal
import Signal (Signal, (<~), (~))
import Random
import Random (Seed)
import Graphics.Element (Element)
randomInt : Seed -> Int
randomInt seed = seed |> (Random.generate <| Random.int 1 10) |> fst
otherInput : Signal (Int,Int)
otherInput = Mouse.position
timeSeed : Signal Seed
timeSeed = Random.initialSeed << round <~ Time.every second
inputs : Signal (Seed,(Int,Int))
inputs = (,) <~ timeSeed ~ otherInput
update : (Seed, (Int,Int)) -> (Int,Int) -> (Int,Int)
update (seed,(x,y)) (x',y') =
let num = randomInt seed
in (x-x'-num,y'-y+num) -- this update function is nonsense
main : Signal Element
main = asText <~ Signal.foldp update (0,0) inputs
If you need time as an input anyway, and sample your other inputs based on this time, it's the easiest way. (If you already use Time.fps for this, use Time.timestamp to get the actual time with it)
2) At startup with a signal
If you don't normally need time as an input to your program, the previous solution is not ideal. You may prefer to initialise your program state with the start time of the program and not have to ignore a time ticker for the rest of the time the program runs.
It's probably easiest to do this with the signal-extra package*. Use Signal.Time.startTime to get a signal that doesn't tick but only has the start time of the program as the initial value. Use Signal.Extra.foldp' so you can use the initial value of your inputs.
import Time
import Time (Time, second)
import Text (asText)
import Mouse
import Signal
import Signal (Signal, (<~), (~))
import Random
import Random (Seed)
import Graphics.Element (Element)
import Signal.Extra as SignalE
import Signal.Time as Time
randomInt : Seed -> (Int,Seed)
randomInt seed = (Random.generate <| Random.int 1 10) |> fst
otherInput : Signal (Int,Int)
otherInput = Mouse.position
startTimeSeed : Signal Seed
startTimeSeed = Random.initialSeed << round <~ Time.startTime
inputs : Signal (Seed,(Int,Int))
inputs = (,) <~ startTimeSeed ~ otherInput
update (x,y) (seed,(x',y')) =
let (num,seed') = randomInt seed
in (seed',(x-x'-num,y'-y+num))
main : Signal Element
main = asText <~ SignalE.foldp' (snd >> update) identity inputs
*I may be biased because I'm the author of the linked package. But I don't know of other packages offering the same functionality.
3) At startup with a port
If you find the previous solution unsatisfactory, because you have this not-changing Signal to add to your input, this solution is for you. Here we use JavaScript interop to get the program startup time, and Elm will accept it as a constant value (no Signal). The Elm code looks like so:
import Time
import Time (Time, second)
import Text (asText)
import Mouse
import Signal (Signal, (<~))
import Random
import Random (Seed)
import Graphics.Element (Element)
port startTime : Float
randomInt : Seed -> (Int,Seed)
randomInt seed = (Random.generate <| Random.int 1 10) |> fst
startTimeSeed : Seed
startTimeSeed = Random.initialSeed <| round startTime
update (x,y) (seed,(x',y')) =
let (num,seed') = randomInt seed
in (seed',(x-x'-num,y'-y+num))
main : Signal Element
main = asText <~ Signal.foldp update (startTimeSeed, (0,0)) Mouse.position
So what's the downside here? You need to write some JavaScript. Instead of the standard
<script>Elm.fullscreen(Elm.<YourModule>)</script>
, you need something like this in your html file:
<script>Elm.fullscreen(Elm.<YourModule>, {startTime: Date.now()})</script>
If you choose this way, perhaps it's a good idea to use a random number from JavaScript as your initial seed. I've read that that's more cryptographically secure (disclaimer: I don't know much about crypto). So you'd have a port aRandomNumber : Int and {aRandomNumber: Math.floor((Math.random() - 0.5) * 4294967295)}.
I reworked the third example from #Apanatshka above, trying to get to simpler code that feels more like the standard architecture, at least as seen in Mike Clark's training videos, and runs under Elm 0.16. Here is the refactored version I came up with:
module PortBasedRandom where
import Mouse
import Signal exposing (Signal, map)
import Random exposing (Seed)
import Graphics.Element exposing (Element, show)
port primer : Float
firstSeed : Seed
firstSeed =
Random.initialSeed <| round primer
type alias Model =
{ nextSeed : Seed
, currentInt : Int
}
initialModel : Model
initialModel =
{ nextSeed = firstSeed
, currentInt = 0
}
randomInt : Model -> Model
randomInt model =
let
(i, s) = Random.generate (Random.int 1 10) model.nextSeed
in
{ model | nextSeed = s, currentInt = i }
update : (Int, Int) -> Model -> Model
update (_, _) model =
randomInt model
main : Signal Element
main =
Signal.foldp update initialModel Mouse.position
|> map (\m -> show m.currentInt)
This needs special help in the HTML file, so here's a file containing two examples:
<html>
<head>
<title></title>
<script src="port_based_random.js"></script>
</head>
<body>
<p>Move your mouse to generate new random numbers between 1 and 10 inclusive.</p>
<script>Elm.fullscreen(Elm.PortBasedRandom, {primer: Date.now()})</script>
<script>Elm.fullscreen(Elm.PortBasedRandom, {primer: Math.floor((Math.random() - 0.5) * 4294967295)})</script>
</body>
</html>
If you are using the StartApp then you'll need to use a custom HTML file with
<script type="text/javascript">
var yourPgm = Elm.fullscreen(Elm.Main, {startTime: Date.now()});
</script>
Then to use the startTime as a seed:
startTimeSeed : Seed
startTimeSeed = Random.initialSeed <| round startTime
app =
StartApp.start
{ init = (init startTimeSeed, Effects.none)
, update = update
, view = view
, inputs = []
}
And then in the code you'll be doing something like
init : Seed -> List Int
init seed = fst <| Random.generate intList seed
where, for example:
intList : Random.Generator (List Int)
intList =
Random.list 5 (Random.int 0 100)
Just an update for people who got here from Google like I did: the recommended way to do this now is with flags instead of ports. The code in the other answers will not even compile now.
https://guide.elm-lang.org/interop/javascript.html
HTML
<script>
var app = Elm.Main.fullscreen({myRandomValue: Date.now()});
</script>
Elm
type alias Model = {
mySeed : String
}
type alias Flags = {
myRandomValue : String
}
init : Flags -> ( Model, Cmd Msg )
init flags =
{
mySeed = flags.myRandomValue
}
...
main : Program Flags Model Msg
main = programWithFlags
{
view = view,
init = init,
update = update
}