Dynamically updating the user interface based on previous updates

I am studying the practical aspects of FRP for the UI and I have been struggling with implementing the following functionality using reactive banana: depending on the value of the select box, a variable number of lists are displayed, which shows some results. (I am using WxHaskell.)

It was pretty easy to implement this using a bunch of prepared lists that are hidden and shown based on the behavior of the result, but this time I want it to create and destroy the lists as needed, each list associated with the results behavior.

So far I have had the following ingredients:

  • eParam

    select box event
  • behavior bResults :: Behavior t [[String]]

    defined with eParam

    (and stepper

    ) that contains all results (lists of items in a list)
  • an update function updateResultControls :: [SingleListBox ()] -> [[String]] -> IO [SingleListBox ()]

    that kills or builds lists based on the results. Note that the return type is in IO.

Looking at BarTab I tried to implement the following:

  • behavior bResultControls :: Behavior t [SingleListBox ()]

    with lists defined as stepper [] eUpdateResultControls

    .
  • an event eUpdateResultControls :: Event t [SingleListBox ()]

    that updates the user interface. This event depends on the behavior bResultControls

    and bResults

    . However, it must also update the network and run the IO, so I suspect that will be involved Moment

    and execute

    . This is where I am stuck.

My last try:

rec
  let
    bResultControls = stepper [] eResultControls
    bResultControlsUpdate = updateResultControls <$> bResultControls <*> bResults

  eResultControls <- execute $ FrameworksMoment . liftIO <$> (bResultControlsUpdate <@ eParam)

      

But I am getting the following error like:

Couldn't match type `m0 [SingleListBox ()]'
              with `forall t1. Frameworks t1 => Moment t1 [SingleListBox ()]'
Expected type: IO [SingleListBox ()]
               -> forall t. Frameworks t => Moment t [SingleListBox ()]
  Actual type: IO [SingleListBox ()] -> m0 [SingleListBox ()]
In the second argument of `(.)', namely `liftIO'
In the first argument of `(<$>)', namely
  `FrameworksMoment . liftIO'
In the second argument of `($)', namely
  `FrameworksMoment . liftIO <$> (bResultControlsUpdate <@ eParam)'

      

I suspect this will truncate some of the behaviors, or perhaps I'm not all wrong about that.

+3


source to share


1 answer


After some additional reading and experimentation, I got it to work with some careful trimming and refactoring (as Heinrich hinted at):

networkDescription :: forall t. Frameworks t => Moment t ()
networkDescription = do
  eParam <- choiceSelection cParam

  let bResults = results <$> stepper x eParam

  bResults_ <- trimB bResults

  rec
    let
      bResultControls = stepper [] eResultControls

      mkResultControls :: [SingleListBox ()] -> [[String]] -> FrameworksMoment [SingleListBox ()]
      mkResultControls cs rs = FrameworksMoment $ do
        slResults <- liftIO $ updateResultControls cs rs

        bResults <- now bResults_

        sequence_ [sink sl [items :== (!! i) <$> bResults] | sl <- slResults | i <- [0..]]

        liftIO $ do
          let n = length rs
          set f [clientSize := sz (150 * n) 200]
          set pResults [layout := fill $ boxed "results" $ row n (map (fill . widget) slResults)]
          refit f

        return slResults

    eResultControls <- execute $ (mkResultControls <$> stepper [] eResultControls <*> bResults) <@ eParam

  return ()

      



(There was just a small bug where the event fires before the behavior was updated, but that should be easy to fix.)

+2


source







All Articles