I really have to get to bed, but I just did a little test and am so excited I need to say something:
AFRP sucks. The incredible amount of trouble I had to go through just to implement extremely simple physics with a dynamic number of balls within FRP (none of that cheating by factoring the physics elsewhere; while that may be a good thing to do, it’s a point for the power of FRP if you can do it directly in FRP) indicates that AFRP is absolutely not suitable for games. The short reason is that is chokes when faced with dynamic behavior.
On the other hand, classical comonadic FRP is great; it supports such dynamism without a sweat. That is, if it weren’t for the spacetime leak that results when you have value recursion like:
e = integral 1 e
Which is horrible because that is the form of every differential simulation (read: games).
Because of that leak, I tried three different approaches with arrows, with varying flexibility trade-offs, but they were all bad for making games. I wanted to go back to comonadic FRP so badly, but the spacetime leak loomed in the darkness, condemning my programs to progressively slow down (and eat up more and more memory) as they ran.
As refresher, the data structure for comonadic FRP is:
type DTime = Double data Signal a = Signal a (DTime -> Signal a)
And today something hit me, something so simple that it made me reconsider my sanity:
-- 'runSignal step sig' produces the infinite list of -- values every 'step' seconds from 'sig' runSignal :: Double -> Signal a -> [a] runSignal = ... -- listSignal is the inverse of runSignal, constructing -- a signal from a list listSignal :: Double -> [a] -> Signal a listSignal = ... buffer :: Double -> Signal a -> Signal a buffer step = listSignal step . runSignal step
The buffer function just samples a signal at regular intervals, caching the results internally in a list. The implementation of these three functions combined is 13 simple lines; the concepts are easy too. Again, foreheadslap! Why? This is why:
e = buffer 0.05 $ integral 1 e
No more spacetime leak! The nth timestep of e is found in linear time and constant space. To generalize the technique, just put a buffer between any value recursion to fix the leak. No, it’s not a perfect solution, but it’s a good compromise. You still get perfect granularity in your event handling (to me, an essential property), and you get to use comonadic FRP.
I’m so excited!