For what it's worth, I had some success by re-working my problem and mixing some different approaches. I hope to have a proper Pong example I can share in the near-ish future, but I figured it's best to talk about some of the different approaches that helped while it's fresh on my mind:
Having neither one giant state per tick (which would defeat the purpose of FRP) nor lots of different Cells for everything - but rather, kindof have local state which is accumulated in Cells per-game object, and then expose the relevant info as needed. In hindsight it's kindof obvious - but the takeaway for me is that it's not an exact science of where to split things up like that, there's an art to it.
For example - instead of a big cGameState, or separate cVelocity, cPosition, cTrajectory - there's a cBallState which gets updated every tick and also a cPaddleState - and neither of them care about the other's "private" values. In other words - collision detection does get things like cBallPosition which is
cThingState.map(s => s.pos)
As an added bonus, this allowed using simple
accum and not needing to put different things in the same Transaction/Loop
I still had an issue where there's a conflict between stale and fresh values, especially regarding "rewinding time" on collision detection (i.e. moving interpenetrating objects back so they never render out of bounds). There's two things which did solve this for me, though it gets into some murky territory- and is especially confusing since I needed both of them to work:
I did use
defer in one place at the top level. I'm not sure why it fixes my problem, but it does... this is a little worrysome since it makes me think when the game gets more complex I might run into the same problem as above... and I also feel weird not understanding the solution... but I'm also hoping it might be fine since the only place that happens is for collision detection, which needs to see the resolved world, and I'm thinking maybe it's the only thing that will ever need that (rendering is just a listen which pushes to IO)- so a teensy tinsy bit of magic is okay
I don't think this is necessarily related to the more general issue here - but I also needed to push two updates per tick. That might just be a flaw in my logic... I guess this is kindof an arbitrary point without showing the code - so maybe i'll ping this later once it's up (happy to share now - it's just in a broken R&D state and probably better to wait till it's more polished)... in any case, this feels a little awkward, but it's not awful.