[reactive] More on delayed switching

Patai Gergely patai_gergely at fastmail.fm
Wed Jul 8 02:57:17 EDT 2009


Hello all,

I opened a thread some time ago where I asked whether it's enough to
have only immediate switching. But it dawned on me that Reactive
switches are actually all delayed, since even though the delay is
infinitesimal in principle, we still have to wait for the next point of
observation until we can see its effect. This is a huge problem in
practice, because this way every stateful signal imposes a delay equal
to the length of the period between observations. Here's a simple test
case for illustration:

> import Control.Applicative
> import FRP.Reactive
> 
> tick :: Event ()
> tick = atTimes [0..]
> 
> behs :: [Behavior Double]
> behs = pure 1 : map (integral tick) behs
> 
> eventList :: Event a -> [a]
> eventList e = x : eventList e'
>     where (x,e') = firstRestE e
> 
> testBeh :: Behavior a -> [a]
> testBeh b = take 10 $ eventList (snapshot_ b tick)
> 
> test :: IO ()
> test = mapM_ (print . testBeh) (take 5 behs)

Running test prints the following:

[1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0]
[0.0,0.0,1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0]
[0.0,0.0,0.0,1.0,3.0,6.0,10.0,15.0,21.0,28.0]
[0.0,0.0,0.0,0.0,1.0,4.0,10.0,20.0,35.0,56.0]
[0.0,0.0,0.0,0.0,0.0,1.0,5.0,15.0,35.0,70.0]

I ran into the very same problem when working on the first version of
Elerea. Transfer functions in version 0.1.0 behave exactly the same way,
and I switched to immediate transfer functions from 0.2.0. The
difference is obvious if you look at the breakout example in action: in
0.1.0, the ball takes long to react to collisions, since the dependency
cycle consists of three items (bricks, velocity, position), so any
change takes just as many frames to propagate. From 0.2.0 onwards,
collisions are much more accurate without any change in the example
code.

Sure, it is possible to work around the delay by combining the switching
value with the one that caused its switch, but this adds a lot of
complexity to the code on the user end. You can make life easier by
introducing immediate versions as helper functions, but then you just
reintroduced this distinction. Of course this is not surprising, since
neither delayed nor immediate switching is sufficient on its own. But
this means that Reactive should support both out of the box and not
encourage ad hoc workarounds that are likely to break in unexpected
ways. Also, immediate switching seems the more sensible default, because
it's more straightforward to derive the delayed version from it than the
other way around, and it is what we need most of the time.

Are there any plans to address this problem? Or a completely new angle
to look at it from?

Gergely

-- 
http://www.fastmail.fm - Faster than the air-speed velocity of an
                          unladen european swallow



More information about the Reactive mailing list