[Haskell-cafe] FRP memory leaks

Łukasz Dąbek sznurek at gmail.com
Thu Jun 6 22:33:24 CEST 2013


I was looking at reactive-banana and netwire (also Bacon.js).

Thank you for great explanation! Now I see that this kind of code must be
generating memory leak and solution using Bechavior was not clear for me
(as I am really new to the FRP stuff).

--
Łukasz Dąbek


2013/6/6 John Lato <jwlato at gmail.com>

> Which FRP frameworks have you been looking at?
>
> In my experience, the most publicized leaks have been time leaks, which
> are a particular type of memory leak related to switching.  However, the
> presence of time leaks mostly arises in terms of the FRP implementation.
>  Arrowized FRP (e.g. Yampa, netwire) do not typically suffer from this for
> example.  Some libraries that implement the semantics of Conal Elliott's
> "Push-pull functional reactive programming" (or similar semantics) have
> been susceptible to this, however recent implementations are not.  Sodium,
> elerea, and reactive-banana for example have generally overcome the worst
> issues present in earlier systems.  Leaks can still be present in current
> systems of course, but now they're generally due to the programmer
> unintentionally retaining data in a case that's much simpler to reason
> about.  That is, the situation today is more similar to forgetting to use
> deepseq or similar, rather than the prior leaks that were very difficult to
> reason about.
>
> I think the most common current issue is that a very natural way of
> accumulating reactive events across time can leak.  Suppose you have a
> library of reactive widgets, where each widget has an associated stream of
> IO actions that you want to run.  E.g. clicking a button prints it, sliding
> a scale prints the value, etc.
>
> > class Actionable a where
> >   actions :: a -> Event (IO ())
>
> suppose you have a collection that allows you to add/remove Actionable
> things to it (e.g. a button panel).  This panel has an action stream that's
> simply the concatenation of those of its components.  One possible
> implementation looks like this:
>
> > data ButtonPanel = ButtonPanel (Event (IO ())
>
> > emptyPanel = ButtonPanel mempty
>
> > addActionable :: Actionable a => ButtonPanel -> a -> ButtonPanel
> > addActionable (ButtonPanel e) a = ButtonPanel (e <> actions a)
>
> I've omitted all the parts for wiring up the gui, but suppose they're
> handled also, and removing a button from the panel just removes it from the
> gui and destroys the widget.  After that, the button's event stream is
> empty, so you can just leave the ButtonPanel's event stream unchanged,
> because the destroyed button will never fire.
>
> This is a memory leak.  The destroyed Button's event stream is still
> referenced in the ButtonPanel event stream, so data related to it never
> gets freed.  Over time your FRP network will grow, and eventually you'll
> hit scaling problems.
>
> The proper solution in this instance is to keep a list of each button's
> event stream within the button panel.  It's ok to keep a cached aggregate
> stream, but that cache needs to be re-built when a button is removed.  This
> is usually fairly natural to do with FRP, but your ButtonPanel may look
> like this instead:
>
> > data ButtonPanel = ButtonPanel  (Map Key (Event (IO ()))
>
> > addActionable :: Actionable a => ButtonPanel-> Key -> a -> ButtonPanel
> > removeActionable :: ButtonPanel -> Key -> ButtonPanel
>
> and now you need to manage some sort of Key for collection elements.
>
> This style isn't entirely idiomatic FRP.  Instead of these functions, you
> could have all your modifications handled via the FRP framework.  For
> example,
>
> > data ButtonPanel = ButtonPanel (Behavior (Map Key (Event (IO ()))))
> > buttonPanel :: Actionable a => Event (Key,a) -> Event Key -> ButtonPanel
>
> but you still need to be aware that objects can reference older objects.
>  Behaviors are frequently created via accumulators over events (e.g.
> accumB), and if the accumulation is doing something like 'mappend', a
> memory leak is likely.
>
> Basically, the issue is that when you're accumulating reactive stuff over
> time, you need to be sure that your accumulator doesn't reference data that
> is otherwise expired.  This example uses a push-pull style pseudocode
> because that's what I'm most familiar with.  I'm not entirely show how (or
> if) this translates to arrowized FRP, although it wouldn't surprise me if
> there's a similar pattern.
>
>
> On Thu, Jun 6, 2013 at 2:50 AM, Łukasz Dąbek <sznurek at gmail.com> wrote:
>
>> Hello, Cafe!
>>
>> I've heard that one of the problems of FRP (Functional Reactive
>> Programming) is that it's easy to create memory leaks. However I cannot
>> find any natural examples of such leaks. Could anybody post some
>> (pseudo)code demonstrating this phenomenon? Preferably something that
>> arises when one is writing bigger applications in FRP style.
>>
>> Thanks in advance!
>>
>> --
>> Łukasz Dąbek
>>
>> _______________________________________________
>> Haskell-Cafe mailing list
>> Haskell-Cafe at haskell.org
>> http://www.haskell.org/mailman/listinfo/haskell-cafe
>>
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://www.haskell.org/pipermail/haskell-cafe/attachments/20130606/5c51cd5c/attachment.htm>


More information about the Haskell-Cafe mailing list