Search code examples
frpreflex

Switching to an event wrapped in monadic context


My specific problem is like this:

Given an Event t [a] and an Event t () (let's say it's a tick event), I want to produce an Event t a, that is, an event that is giving me consecutive items from input list for every occurence of tick event.

Reflex has following helper:

zipListWithEvent :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> c) -> [a] -> Event t b -> m (Event t c)

which is doing exactly what I want, but does not take an event as an input, but just a list. Given that I have an Event t [a], I thought I could produce an event containing event and just switch, but the problem is that zipListWithEven operates in monadic context, therefore I can get:

Event t (m (Event t a))

which is something that switch primitive does not accept.

Now, maybe I'm approaching it in wrong way, so here's my general problem. Given an event that's producing list of coordinates and tick event, I want to produce an event that I can "use" to move an object along the coordinates. So each time tick fires, the position is updated. And each time I update the coordinates list, it begins to produce positions from that new list.


Solution

  • I'm not entirely sure if I understand the semantics of your desired functions correctly, but in the reactive-banana library, I would solve the problem like this:

    trickle :: MonadMoment m => Event [a] -> Event () -> Event a
    trickle eadd etick = do
        bitems <- accumB [] $ unions    -- 1
            [ flip (++) <$> eadd        -- 2
            , drop 1    <$  etick       -- 3
            ]
        return $ head <$> filterE (not . null) (bitems <@ etick) -- 4
    

    The code works as follows:

    1. The Behavior bitems records the current lists of items.
    2. Items are added when eadd happens, ...
    3. ... and one item is removed when etick happens.
    4. The result is an event that happens whenever etick happens, and that contains the first element of the (previously) current list whenever that list is nonempty.

    This solution does not seem to require any fancy or intricate reasoning.