reflex-frp / reflex

Interactive programs without callbacks or side-effects. Functional Reactive Programming (FRP) uses composable events and time-varying values to describe interactive systems as pure functions. Just like other pure functional code, functional reactive code is easier to get right on the first try, maintain, and reuse.
https://reflex-frp.org
BSD 3-Clause "New" or "Revised" License
1.07k stars 147 forks source link

Cleanup resources of never-occurring root events #34

Open bennofs opened 8 years ago

bennofs commented 8 years ago

Problem

I would like that when a root event's trigger is garbage collected, that this root is removed from the event network, no longer reclaiming any resources (it should act just as if it was never). This is currently not the case, since it'll still consume memory in the parents list of any merges it is registered in and there will be an IORef for saving its occurence (which we know will always be Nothing), just to name a few examples. This optimization should be safe because there is no way to ever trigger a firing of the event again if the trigger was collected.

Why would this be useful?

Consider the following situation. You want to use reflex with a framework that supports objects. objects are just values that have some members and some associated reflex events (for example, there is an event when a member of the object is changed by the framework). Further, let's assume that the lifetime of the object depends on the framework and you cannot control that: for example, if the object is passed to JavaScript, events can be generated as long as JavaScript has a reference to it, because JavaScript can change members of the object which will trigger a reflex event.

For the example, it suffices to consider a simpler case: we only need objects that have exactly one member, and we will restrict that member to be of type Int, so there'll also be only event for the object: Event t Int, that fires whenever that single member if changed by JavaScript code.

Now, I would like to expose the following function to work with the framework from reflex:

createObject :: (Reflex t, MonadFramework t m) => Event t Int -> m (Event t (Event t Int))

This function will create a new object whenever the input event fires with a new initial value for the object member. Creating a new object will also fire the output event, containing the Event for observing the changes to the member of the newly created object.

The framework provides us with a way to execute some haskell code when the object is garbage collected (last reference dropped) in JavaScript. We then know that the event which createObject returned when it created the object can never fire again, so we would like it not to retain any resources. This is currently not the case, since it'll consume memory in the parents list of any merges it is registered in and there will be an IORef for saving its occurence (which we know will always be Nothing), just to name a few examples.

ryantrinkle commented 8 years ago

This is a very good suggestion. Thanks!

ziriax commented 8 years ago

Is this actually possible to implement for a Javascript target, since JS does not support weak references or GC finalizers? Or does GHCJS use some kind of reference counting or a custom garbage collector for GHCJS managed objects?

oliver-batchelor commented 8 years ago

GHCJS has it's own weak reference system, furthermore the ability to GC unused parts of the Event network is key to Reflex performing well and depends entirely on weak references!

On Thu, Mar 17, 2016 at 11:38 PM, Peter Verswyvelen < notifications@github.com> wrote:

Is this actually possible to implement for a Javascript target, since JS does not support weak references or GC finalizers? Or does GHCJS use some kind of reference counting or a custom garbage collector for GHCJS managed objects?

— You are receiving this because you are subscribed to this thread. Reply to this email directly or view it on GitHub https://github.com/reflex-frp/reflex/issues/34#issuecomment-197812831