packages feed

keid-frp-banana-0.1.2.1: src/Engine/ReactiveBanana/Utils.hs

module Engine.ReactiveBanana.Utils where

import RIO

import Reactive.Banana qualified as RB
import Reactive.Banana.Frameworks qualified as RBF

{-# INLINE (<@@>) #-}
(<@@>) :: RB.Event a -> RB.Behavior (a -> b) -> RB.Event b
(<@@>) = flip (RB.<@>)

{-# INLINE ($>>) #-}
($>>) :: {- forall {a} {b} .  -}RB.Event a -> RB.Event b -> RB.Event ()
a $>> b =
  RB.mergeWith
    (const ())
    (const ())
    (\_ _ -> ())
    a
    b

delay :: RB.Event a -> RBF.MomentIO (RB.Event a)
delay e = do
  (delayed, fire) <- RBF.newEvent
  RBF.reactimate $ fmap fire e
  pure delayed

delayOnce :: RB.Event a -> RBF.MomentIO (RB.Event a)
delayOnce e = delay e >>= RB.once

lockstep :: a -> RB.Event a -> RBF.MomentIO (RB.Event a, RB.Behavior a)
lockstep initial e = do
  b <- RB.stepper initial e
  e' <- delay e
  pure (e', b)

accumE_ :: RB.MonadMoment m => [RB.Event a] -> m (RB.Event a)
accumE_ = RB.accumE undefined . unionsConst

unionsConst :: [RB.Event a] -> RB.Event (a -> a)
unionsConst = RB.unions . map (fmap const)

-- | Only pass events when their value changes.
--
-- The event is delayed for behavior value to match current value.
filterChange
  :: Eq a
  => a
  -> RB.Event a
  -> RBF.MomentIO (RB.Event (a, a), RB.Behavior a)
filterChange initial e = do
  b <- RB.stepper initial e
  let
    changeE = RB.filterJust $
      e <@@> do
        old <- b
        pure \new ->
          if old == new then
            Nothing
          else
            Just (old, new)
  syncE <- delay changeE
  pure (syncE, b)