1

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.

Bartosz
  • 3,318
  • 21
  • 31

2 Answers2

1

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.

Bartosz
  • 3,318
  • 21
  • 31
Heinrich Apfelmus
  • 11,034
  • 1
  • 39
  • 67
  • It's not exactly what I wanted (it accumulates yhe incoming lists, whereas I wanted to replace the source of coordinates every time new arrives), but I think it is not significant difference and it shows the approach. I now think I know what were my problem in thinking, and I will try to translate it to reflex as soon as I'm in front of computer, thanks! – Bartosz Nov 27 '16 at 09:42
  • Yep, worked with minor adjustements! It's great the FRP semantics are common so someone not using one particular lib can be of help:) – Bartosz Nov 28 '16 at 08:49
1

Naming the parts:

coords :: Event t [Coord]
ticks  :: Event t ()

If we want to remember the most recent Coord until the next firing of ticks, then we necessarily have to be in the some monad Reflex m. This is the monad that allow the transient Event to be persisted.

The core thing you'd like to remember is a stack of Coord. Let's try this:

data Stack a = CS {
    cs_lastPop :: Maybe a
  , cs_stack   :: [a]
  } deriving (Show)

stack0 = CS Nothing []

pop :: Stack a -> Stack a
pop (CS _ []    ) = CS Nothing []
pop (CS _ (x:xs)) = CS (Just x) xs

reset :: [a] -> Stack a -> Stack a
reset cs (CS l _) = CS l cs

Nothing reactive there yet, two functions that tweak the Stack Coord in the way you mention in your question.

The reflex code to drive this would build a Dynamic t (Stack Coord), by specifying its initial state and all the things that modify it:

  coordStack <- foldDyn ($) stack0 (leftmost [
      reset <$> coords
    , pop   <$  ticks
    ])

The leftmost here takes a list of Stack Coord -> Stack Coord functions, which are applied in turn to stack0 by foldDyn ($) (as long as coords and ticks never occur in same frame).

Driving all this in main:

main :: IO ()
main = mainWidget $ do
  t0 <- liftIO getCurrentTime

  -- Some make up 'coords' data, pretending (Coord ~ Char)
  coordTimes <- tickLossy 2.5 t0
  coords <- zipListWithEvent (\c _ -> c) ["greg","TOAST"] coordTimes

  ticks <- tickLossy 1 t0

  coordStack <- foldDyn ($) stack0 (leftmost [
      reset <$> coords
    , pop   <$  ticks
    ])

  display coordStack
ImAlsoGreg
  • 655
  • 6
  • 17
  • 1
    I like how it goes from just normal datatype/functions and then make it reactive in just one expression. Good to have that in mind, considering FRP seems so much of a paradigm shift that it's easy to get lost in its intricacies instead of keeping calm and just writing functional code ;) – Bartosz Mar 06 '17 at 11:04