Reputation: 547
How can I parameterise one signal based on another signal? e.g. Suppose I wanted to modify the fps based on the x-position of the mouse. The types are:
Mouse.x : Signal Int
fps : number -> Signal Time
How could I make Elm understand something along the lines of this pseudocode:
fps (Mouse.x) : Signal Time
Obviously, lift
doesn't work in this case. I think the result would be Signal (Signal Time)
(but I'm still quite new to Elm).
Thanks!
Upvotes: 2
Views: 203
Reputation: 5958
fps Mouse.x
Results in a type-error that fps requires an Int
, not a Signal Int
.
lift fps Mouse.x : Signal (Signal Int)
You are correct there. As CheatX's answers mentions, you cannot using these "nested signals" in Elm.
It seems like you're asking for something that doesn't exist yet in the Standard Libraries. If I understand your question correctly you would like a time (or fps) signal of which the timing can be changed dynamically. Something like:
dynamicFps : Signal Int -> Signal Time
Using the built-in functions like lift
does not give you the ability to construct such a function yourself from a function of type Int -> Signal Time
.
I think you have three options here:
Time
signal.I advise option 1. Option 3 is sad, you should be able to what you asked in Elm. Option 2 is perhaps not a good idea if you're new to Elm. Option 1 is not a lot of work, and the folks on the mailing-list don't bite ;)
To elaborate on option 2, should you want to go for that:
Signal Int
and an incoming port for Signal Time
you can write your own dynamic time function in JavaScript. See http://elm-lang.org/learn/Ports.elm If you want to do this from within Elm, it'll take an uglier hack:
dynamicFps frames =
let start = (0,0)
time = every millisecond -- this strains your program enormously
input = (,) <~ frames ~ time
step (frameTime,now) (oldDelta,old) =
let delta = now - old
in if (oldDelta,old) == (0,0)
then (frameTime,now) -- this is to skip the (0,0) start
else if delta * frameTime >= second
then (delta,now)
else (0,old)
in dropIf ((==) 0) 0 <| fst <~ foldp step start input
Basically, you remember an absolute timestamp, ask for the new time as fast as you can, and check if the time between the remembered time and now is big enough to fit the timeframe you want. If so you send out that time delta (fps gives time deltas) and remember now as the new timestamp. Because foldp sends out everything it is to remember, you get both the new delta and the new time. So using fst <~
you keep only the delta. But the input time is (likely) much faster than the timeframe you want so you also get a lot of (0,old)
from foldp
. That's why there is a dropIf ((==) 0)
.
Upvotes: 4
Reputation: 2124
Nested signals are explicitly forbidden by the Elm's type system [part 3.2 of this paper].
As far as I understand FRP, nested signals are only useful when some kind of flattering provided (monadic 'join' function for example). And that operation is hard to be implemented without keeping an entire signal history.
Upvotes: 0