2

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!

Misha Moroshko
  • 166,356
  • 226
  • 505
  • 746
icz
  • 537
  • 1
  • 7
  • 14

2 Answers2

4

Preamble

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.

Answer to your question

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:

  1. Ask to have this function added to the Time library on the mailing-list. (The feature request instructions are a little bloated for a request of such a function so you can skip stuff that's not applicable)
  2. Work around the problem, either from within Elm or in JavaScript, using Ports to connect to Elm.
  3. Find a way to not need a dynamically changing 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:

  1. If you specify an outgoing port for 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
  2. 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).

Apanatshka
  • 5,958
  • 27
  • 38
  • Wow, I really appreciate this detailed response! I'll have to test it before I rule it out, but I am worried about the performance hit caused by `every millisecond`. I'll probably submit a feature request soon. Thanks! – icz Jul 02 '14 at 13:03
0

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.

CheatEx
  • 2,082
  • 2
  • 19
  • 28
  • Though this is all completely true information, it doesn't seem to answer the OP's question. – Apanatshka Jul 02 '14 at 08:35
  • @Apanatshka The only question I saw was "How can I parameterise one signal based on another signal?". Sorry, I'm really bad in guessing. – CheatEx Jul 02 '14 at 10:21
  • Since the OP accepted your answer you must have guessed right, or at least provided enough helpful information. As my assumption that it *doesn't* answer question was also guesswork, I'll remove the -1. EDIT: apparently I can't remove it any more, sorry. – Apanatshka Jul 02 '14 at 11:11
  • @Apanatshka That was sarcasm. I tried to point out that you guessed the underlying problem and downvoted me for not doing so. – CheatEx Jul 02 '14 at 12:12
  • I have accepted @Apanatshka's answer instead of yours as it better answers my question; I wasn't expecting any more answers after yours. I really enjoyed that paper that you linked to, though. Thanks! – icz Jul 02 '14 at 13:07