Question

I'm wondering how reactive-banana was designed to handle the following situation:

Let's say I have a central data structure. The user is able to freely open and close any number of different types of windows that both display the data, and allow the user to modify it.

So, given the nature of this, I don't think just trying to create one big network would work well. Is this something where each window would have a network and somehow they were connected?

In other situations like this, I had put the data structure behind a single channel that everyone sent updates to. And then the data structure would "publish" updates (fire events) that the windows all "listened" to.

Was it helpful?

Solution

I have a related problem in one of my projects, which uses something akin to an MVC architecture. The central data structure is referenced as

-- the data is a tree, and it's kept as a zipper to the current node
Discrete MyDataZip

Here is a stripped-down version of my controller declaration:

data Controller st = Controller {
  dState      :: Discrete st
 ,eUpdateZip  :: Event (MyDataZip -> MyDataZip)
 ,eResponse   :: Event (IO ())
 ,bDiagChange :: Behavior (Diagram Cairo R2 -> Diagram Cairo R2)
 }

When constructed, most controllers get a reference to dZip :: Discrete MyDataZip, but have no way to modify it directly. The only way for a controller to specify an update is via the eUpdateZip stream within the Controller data structure.

Multiple controllers are assembled into a graph, which is just a list of Controllers placed in an existential type wrapper data EControl = forall st. EControl (Controller st). I just mconcat all of the individual eUpdateZip parameters to get a single stream of Event (MyDataZip -> MyDataZip) which is used to create the Discrete MyDataZip. The whole thing works because creating new controllers is pure, so it can be done in the same let-binding, allowing the recursive reference.

Opening new windows and other IO tasks are done in the eResponse stream, which is similarly mconcatd and then passed to reactimate.

You can examine the darcs repo for more details, look in "src/Jaek/UI/Controllers/" and "src/Jaek/UI/ControlGraph.hs".

EDIT: The central problem is that one big network is fairly unwieldy from a developer's POV. You can reduce the complexity by segmenting the networks, which is a good solution. In my design, I introduced a lot of structure to the network by making reactive participants conform to a specific controller model, and creating well-defined means for those models to interact. Since my controllers are persistent that's all set up statically, but it could be done dynamically, in which case I'd probably have controller manager running in one thread, and actions which generated new controllers (such as opening a new window) would send a message to the thread to create a new controller.

OTHER TIPS

At the moment, it is not possible to add or remove external events to an event network after it has been compiled (as of reactive-banana version 0.4.3). In other words, it is not possible to describe your task in a single event network. However, you can make use of external events or multiple event networks, which leads to solutions like the following:

  • Map a dynamic collection of windows to a static set of event sources (aka AddHandler things).
  • Or use one event network per window and make them communicate via external event sources.
  • Or recompile a single event network every time a new window is added or removed. Unfortunately, the internal state will be lost, so you have to make it explicit and capture it externally (for instance in an IORef). (This is probably the least satisfactory solution.)
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top