reactive-banana icon indicating copy to clipboard operation
reactive-banana copied to clipboard

Pruning old `reactimate` calls

Open PaulJohnson opened this issue 5 years ago • 1 comments

I'm using Reactive Banana with gi-gtk to build a GUI application. I want to create a widget in response to an event and have that widget respond to changes in a Behavior and generate events for output. However I have a space-time leak from the interaction of reactimate with execute.

-- | Display the value in the behavior and emit events 
-- in response to user actions.
makeMyWidget :: Behavior DisplayValue -> MomentIO (Widget, Event Edit)

-- | Extract data specified by Foo into DisplayValue
getDisplay :: Foo -> AppState -> DisplayValue

triggerEvent :: Event Foo   -- The event that causes the widget to appear.

globalState :: Behavior AppState

-- | Add the argument to the GUI widget tree, dstroying any previous one.
showWidgetInGui :: Widget -> IO ()

So now I can write this:

runDisplays :: MomentIO (Event Edit)
   displays <- execute $ 
      makeMyWidget (\f -> getDisplay f <$> globalState) <$> triggerEvent
   reactimate $ showWidgetInGui . fst <$> displays
   switchE $ snd <$> displays

This seems to work fine. Every time the triggerEvent happens a new window pops up containing data extracted from globalState.

However when I trace the execution of getDisplay I see it being run for every past invocation as well as the one currently displayed. Presumably this is because the makeMyWidget includes reactimate' calls to update its display from the input behavior.

execute allows me to dynamically add processing to the event network, but I can't see any way of dynamically pruning old processing that is no longer needed.

I'm thinking of an API along the following lines:

-- | A reference to an existing "reactimate"
data Cookie

-- | The same as the existing function, but returning a cookie.
reactimate :: Event (IO ()) -> MomentIO (Cookie)

-- | Dynamically remove a reactimate from the network.
stopReactimate :: Cookie -> IO ()

With this I could put a bunch of reactimate calls inside an execute, collect all their cookies, and then have the corresponding stopReactimate calls as a callback from the destruction of the widget that they were updating. Having stopReactimate in the IO monad makes it easy to put in a callback like this. The data structures associated with the reactimate should also be cleared or dropped to allow the widget to be garbage collected along with everthing else.

PaulJohnson avatar May 29 '19 13:05 PaulJohnson

I've put in pull request #199 for an extension similar to what I described above.

PaulJohnson avatar Jun 06 '19 08:06 PaulJohnson