Journal entry for


I’ve continued working on understanding how Reflex works and trying to implement FRP using an occurs :: Event a -> m (Maybe a) implementation primitive. Such a primitive might help with making a reasonably efficiont implementation which doesn’t rely on many event merge primitives such as mergeIncrementalG and mergeIncrementalWithMoveG from Reflex (mergeCheapWithMove, mergeCheap, and mergeIntCheap in the implementation). It might also make coincidence and functions like push in Reflex share code.

I’m doing this on two fronts: one is my simple FRP implementation, for which I copied part of Reflex’ test suite, and the other is working on Reflex’ Spider implementation. While looking at the latter I discovered some duplicated code between coincidence and switch which I factored out. Now I’m looking at the various merge functions and trying to see what they share.

For the simple implementation I’ve been copying code from Reflex.Spider but I haven’t yet made my tests run with this implementation (before I had a different one which passed all the tests but wasn’t lazy enough to express some definitions without looping). Getting the tests to run is my priority now.

I’ll also grab a local copy of Reflex and apply my refactorings there so I can run the official test suite and be more confident that I haven’t messed up.

For future reference, here’s a pure implementation of my simple FRP library using occurs where I could:

instance (Enum t, Ord t) => Frp (Pure t) where
  newtype Event (Pure t) a = Event { unEvent :: t -> Maybe a }
  newtype Behavior (Pure t) a = Behavior { unBehavior :: t -> a }
    deriving (Functor, Applicative, Monad) via (->) t
  type Now (Pure t) = (->) t
  sample = unBehavior
  never = toEvent $ pure Nothing
  sampleAtMaybe f = toEvent . runMaybeT . (MaybeT . f <=< MaybeT . occurs)
  coincidence = Frp.sampleAtMaybe occurs
  hold a e'@(Event e) fromT = Behavior $ \sampleT -> do
    if sampleT <= fromT
      then a
      else case e (pred sampleT) of
             Nothing -> unBehavior (hold a e' fromT) (pred sampleT)
             Just a' -> a'
  now = Event . (\t' -> guard . (t' ==))
  mergeE a b = toEvent $ align <$> occurs a <*> occurs b
  switch = toEvent . (occurs <=< Frp.sample)

occurs :: Event (Pure t) a -> Now (Pure t) (Maybe a)
occurs = unEvent

toEvent :: Now (Pure t) (Maybe a) -> Event (Pure t) a
toEvent = Event

nonEmpty :: Foldable t => t a -> Maybe (t a)
nonEmpty xs = if null xs then Nothing else Just xs

mergeIntMap :: (Witherable f, Foldable f) => f (Event (Pure t) a) -> Event (Pure t) (f a)
mergeIntMap =
   toEvent . fmap nonEmpty . wither occurs