[Haskell-cafe] FRP memory leaks
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).
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
> > 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
-------------- next part --------------
An HTML attachment was scrubbed...
More information about the Haskell-Cafe