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)}
.