reactive-banana-wx `sink` does not generate an event enabling post-sink processing
Asked Answered
M

1

6

I am currently redesigning a piece of legacy wxHaskell using the reactive-banana and reactive-banana-wx packages. However, in order to avoid dynamic network construction (where I ran into a thread block on an MVar), I now mimic this by preconstructing a fixed set of wxHaskell widgets of which I set visibility as necessary. Visibility is set by the sink function taking a Behavior. However, wxHaskell requires that after all these widgets have been appropriately modified via sink, a subsequent change of the layout of the panel holding these widgets is required. That would mean that sink-ing actually should be part of a network, so it is an event which can be triggered and waited upon for a layout change. As it currently is, a sink takes you 'out' of the event network, it is not possible to trigger an event after the sink action is completed. I did try to adapt sink to something like this:

sink' :: Frameworks t =>
    w -> [Prop' t w] -> Moment t (Event t ())
sink' widget props = do
    es <- mapM sink1 props
    return $ unions es
  where
    sink1 (attr :== b) = do
        x <- initial b
        liftIOLater $ set widget [attr := x]
        e <- changes b
        return $ (\x -> unsafePerformIO $ set widget [attr := x]) <$> e

However, the unsafePerformIO did not get executed. How can the desired behavior be achieved, i.e. allow a (wxHaskell) IO to be waited on by means of an Event?

Meredi answered 27/11, 2012 at 8:30 Comment(0)
O
2

Basically, it appears that you want to ensure that the IO actions in reactimate are executed in a certain order? Namely, you want to make sure that the layout is set after the widget properties have been set.

There are several methods to specify order:

  1. Use union, unionWith and/or collect to determine the order of simultaneous events.
  2. Use the fact that reactimate are executed in the order they appear in the Moment monad. (Though strictly speaking, this is no longer true when you use the observeE combinator from dynamic event switching.)

In your particular case, these ideas can be applied as follows.

For 1, you can make an event that contains an IO action and combine it with the layout later

sink' :: Frameworks t =>
    w -> [Prop' t w] -> Moment t (Event t (IO ()))
sink' widget props = do
    es <- mapM sink1 props
    return $ foldr1 (unionWith (>>)) es
  where
    sink1 (attr :== b) = do
        x <- initial b
        liftIOLater $ set widget [attr := x]
        e <- changes b
        return $ (\x -> set widget [attr := x]) <$> e

For 2, you can simply use the ordinary sink functions and just make sure that the layout is set last.

do
    sink widget1 [ visible :== bBool ]
    sink window1 [ layout  :== bLayout ]

The ordering of the sink functions in the monad guarantees that the layout is set last.


Also note that since reactive-banana 0.7, you can use dynamic event switching to model a variable set of widgets. See the BarTab.hs example for a demonstration. This example also sets the layout.

You indicated that you run into an MVar block when you use dynamic networks. This is probably because you create widgets in a way that triggers another event in the network. Unfortunately, this is semantically unsound — it corresponds to values depending on future versions of themselves — and the program responds by plunging into rock bottom.

Overtly answered 27/11, 2012 at 16:46 Comment(2)
Thanks! About the BarTab.hs example, I did copy its behavior. BarTab dynamically computes an increment of a list of widgets, this increment is independent of the previous list. The increment I tried to construct however does depend on the previous list. I guess it went wrong there, and will probably retry it later on, maybe giving rise to another post.Meredi
Ok, let me know if anything else pops up.Overtly

© 2022 - 2024 — McMap. All rights reserved.