packages feed

bearriver-0.14.5: src/FRP/BearRiver/Conditional.hs

-- |
-- Module      : FRP.Yampa
-- Copyright   : (c) Ivan Perez, 2014-2022
--               (c) George Giorgidze, 2007-2012
--               (c) Henrik Nilsson, 2005-2006
--               (c) Antony Courtney and Henrik Nilsson, Yale University, 2003-2004
-- License     : BSD-style (see the LICENSE file in the distribution)
--
-- Maintainer  : ivan.perez@keera.co.uk
-- Stability   : provisional
-- Portability : non-portable (GHC extensions)
--
-- Apply SFs only under certain conditions.
module FRP.BearRiver.Conditional
    (
      -- * Guards and automata-oriented combinators
      provided

      -- * Variable pause
    , pause
    )
  where

-- External imports
import Control.Arrow ((&&&), (^>>))

import Data.MonadicStreamFunction.InternalCore (MSF (MSF, unMSF))

-- Internal imports
import FRP.BearRiver.Basic        (constant)
import FRP.BearRiver.EventS       (edge, snap)
import FRP.BearRiver.InternalCore (SF (..))
import FRP.BearRiver.Switches     (switch)

-- * Guards and automata-oriented combinators

-- | Runs a signal function only when a given predicate is satisfied, otherwise
-- runs the other signal function.
--
-- This is similar to 'ArrowChoice', except that this resets the SFs after each
-- transition.
--
-- For example, the following integrates the incoming input numbers, using one
-- integral if the numbers are even, and another if the input numbers are odd.
-- Note how, every time we "switch", the old value of the integral is discarded.
--
-- >>> embed (provided (even . round) integral integral) (deltaEncode 1 [1, 1, 1, 2, 2, 2, 1, 1, 1, 2, 2, 2 :: Double])
-- [0.0,1.0,2.0,0.0,2.0,4.0,0.0,1.0,2.0,0.0,2.0,4.0]
provided :: Monad m => (a -> Bool) -> SF m a b -> SF m a b -> SF m a b
provided p sft sff =
    switch (constant undefined &&& snap) $ \a0 ->
      if p a0 then stt else stf
  where
    stt = switch (sft &&& (not . p ^>> edge)) (const stf)
    stf = switch (sff &&& (p ^>> edge)) (const stt)

-- * Variable pause

-- | Given a value in an accumulator (b), a predicate signal function (sfC),
-- and a second signal function (sf), pause will produce the accumulator b if
-- sfC input is True, and will transform the signal using sf otherwise. It acts
-- as a pause with an accumulator for the moments when the transformation is
-- paused.
pause :: Monad m => b -> SF m a Bool -> SF m a b -> SF m a b
pause b sfC sf = MSF $ \a0 -> do
   (p, sfC') <- unMSF sfC a0
   case p of
     True  -> return (b, pause b sfC' sf)
     False -> do (b', sf') <- unMSF sf a0
                 return (b', pause b' sfC' sf')