I've been comparing pull-only FRP (ie netwire) with push-pull FRP (ie reactive-bannana) in the implementation of games. Are there advantages to one over the other? Things I've notices are:
- Push events make it easy to have events for mouse clicks / key presses from GLFW or GLUT
- Arrowized FRP that netwire uses has much less
IO
floating around, which is always better. - It looks like having pull-only responses to things like mouse movement could cause time leaks.
What else have I missed?
Edit, to make this less opinion-based: The main goal is to have something that is as expressive/concise as possible, without time leaks.
Update: A big issue I have found with Netwire is that there doesn't seem to be a convenient way to have more than one framerate, as described in the article "Fix your Timestep."
Update 2: The way i've gotten around this is to make my game simulation wire return a Float -> IO ()
that takes the alpha value and does all the GL calls with everything interpolated by that alpha. Ideally I should be able to pass this "draw function" into another thread via an MVar
and run it in that thread. Man, Haskell is awesome.
Update 3: In the six months since asking this question, I developed a simple rendering engine based around Netwire, and the concept of "entity-component programming." In this process I have actually not found the use of FRP to be terribly helpful. The expressiveness of Wire
s has actually turned out to be a hindrance in some circumstances. The problem centers around the "identity" of objects. When defining a value of Wire
type, it has no identity, i.e. it may be re-used in the overall network multiple times, and represent different physical things each time. This is a huge pain when you want to do something like collision detection, and have no way to traverse the scene graph, because it cannot exist without being fused into a single opaque wire. This problem does not occur in "toy" examples, because it is easy to specify exactly which properties of which objects interact with which other objects in which ways. I believe this is a problem with either type of FRP.
It would be awesome if some Haskell wizard were to prove me wrong on this, but I don't really see a way around it. Also I'm sorry if the explanation isn't great, but it's not really that easy to understand without trying it yourself.