-- | -- 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.Yampa.Conditional ( -- * Guards and automata-oriented combinators provided -- * Variable pause , pause ) where -- External imports import Control.Arrow ((&&&), (^>>)) -- Internal imports import FRP.Yampa.Basic (constant) import FRP.Yampa.EventS (edge, snap) import FRP.Yampa.InternalCore (SF (..), SF' (..), Transition, sfTF') import FRP.Yampa.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 :: (a -> Bool) -> SF a b -> SF a b -> SF 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 :: b -> SF a Bool -> SF a b -> SF a b pause bInit (SF { sfTF = tfP}) (SF {sfTF = tf10}) = SF {sfTF = tf0} where -- Initial transformation (no time delta): If the condition is True, return -- the accumulator bInit) Otherwise transform the input normally and -- recurse. tf0 a0 = case tfP a0 of (c, True) -> (pauseInit bInit tf10 c, bInit) (c, False) -> (pause' b0 k c, b0) where (k, b0) = tf10 a0 -- Similar deal, but with a time delta pauseInit :: b -> (a -> Transition a b) -> SF' a Bool -> SF' a b pauseInit bInit' tf10' c = SF' tf0' where tf0' dt a = case (sfTF' c) dt a of (c', True) -> (pauseInit bInit' tf10' c', bInit') (c', False) -> (pause' b0 k c', b0) where (k, b0) = tf10' a -- Very same deal (almost alpha-renameable) pause' :: b -> SF' a b -> SF' a Bool -> SF' a b pause' bInit' tf10' tfP' = SF' tf0' where tf0' dt a = case (sfTF' tfP') dt a of (tfP'', True) -> (pause' bInit' tf10' tfP'', bInit') (tfP'', False) -> (pause' b0' tf10'' tfP'', b0') where (tf10'', b0') = (sfTF' tf10') dt a