packages feed

keid-frp-banana-0.1.1.0: src/Engine/ReactiveBanana/Stateful.hs

{-# LANGUAGE RecursiveDo #-}

{- | "Crank the world"-style stateful process.

An input event comes in, an update step runs and an output event is fired.
-}

module Engine.ReactiveBanana.Stateful
  ( setup
  , runWorldWith
  , Thaw
  ) where

import Prelude

import Control.Monad.ST (ST)
import Reactive.Banana qualified as RB

setup
  :: RB.MonadMoment m
  => m acc -- ^ An action to produce the initial stat
  -> (a -> acc -> (x, acc)) -- ^ Step function
  -> RB.Event a -- ^ Step  event
  -> m (RB.Event x, RB.Behavior acc) -- ^ A post-step event and a current state snapshot
setup initialWorld action triggerE = mdo
  initial <- initialWorld
  RB.mapAccum initial $
    fmap action triggerE

-- | A helper to connect a world snapshot with its dynamic representation under an existential @s@.
type family Thaw world s

runWorldWith
  :: forall world update s
  .  (world -> ST s (Thaw world s)) -- ^ Thaw the world into 'STRef's
  -> (Thaw world s -> ST s world) -- ^ Read the world STRefs and freeze
  -> world -- ^ Previous world snapshot
  -> (Thaw world s -> ST s update) -- ^ Update procedure yielding a result and a new world snapshot
  -> ST s (update, world)
runWorldWith t f old action = do
  st <- t old
  res <- action st
  new <- f st
  pure (res, new)