{-| This module is about continuous signals. For a general introduction to signals, see the documentation of "FRP.Grapefruit.Signal". -} module FRP.Grapefruit.Signal.Continuous ( -- * Continuous signal type CSignal, -- * Conversion fromSSignal, -- * Connectors producer ) where -- Control import Control.Applicative as Applicative #if __GLASGOW_HASKELL__ >= 610 import Control.Arrow as Arrow #else import Control.Arrow as Arrow hiding (pure) #endif import Control.Compose as Compose -- Data import Data.Unique as Unique -- Internal import Internal.Capsule as Capsule import Internal.CSeg as CSeg hiding (producer) import qualified Internal.CSeg as CSeg import Internal.Signal as Signal import Internal.Signal.Discrete (DSignal) import qualified Internal.Signal.Discrete as DSignal import Internal.Signal.Segmented as SSignal -- Internal import Internal.Circuit as Circuit -- * Continuous signal type {-| The type of continuous signals. A continuous signal denotes a mapping from times to values. You can think of @CSignal /era/ /val/@ as being equivalent to @Time /era/ -> /val/@ where @Time /era/@ is the type of all times of the given era. Continuous signals are used to describe continuously changing values. They are also used for values changing at discrete times if there is no possibility of being notified about such changes. If there is a notification mechanism then segemented signals, provided by "FRP.Grapefruit.Signal.Segmented", should be used. -} data CSignal era val = CSignal (Capsule val) !(SSignal era (CSeg val)) {- The strictness annotation ensures that reducing the CSignal reduces the SSignal, thereby triggering reading of continous sources the SSignal depends on. -} instance Functor (CSignal era) where fmap fun (CSignal initCap segs) = CSignal (fmap fun initCap) ((fmap . fmap) fun segs) instance Applicative (CSignal era) where pure val = CSignal (pure val) ((pure . pure) val) CSignal funInitCap funSegs <*> CSignal argInitCap argSegs = CSignal initCap' segs' where initCap' = funInitCap <*> argInitCap segs' = liftA2 (<*>) funSegs argSegs instance Signal CSignal where osfSwitch signal@(SSignal init _) = CSignal (initCap init) segs' where segs' = osfSwitch (segsSignal signal) ssfSwitch (SSignal init upd) (CSignal initCap segs) = ssfSwitch sampler segs where sampler = polySSignal (fixInitCapForInit init initCap) (polyTimeIDApp (fixInitCapForUpd <$> upd) <#> segs) initCap :: CSignal era val -> Capsule val initCap (CSignal initCap _) = initCap segsSignal :: SSignal era (forall era'. CSignal era' val) -> SSignal era (forall era'. SSignal era' (CSeg val)) segsSignal = fmap polySegs polySegs :: (forall era'. CSignal era' val) -> (forall era'. SSignal era' (CSeg val)) polySegs signal = segs signal segs :: CSignal era' val -> SSignal era' (CSeg val) segs (CSignal _ segs) = segs polySSignal :: (forall era'. SSignal era' (CSeg val) -> SignalFun era' shape) -> DSignal era (forall era'. SSignal era' (CSeg val) -> SignalFun era' shape) -> SSignal era (forall era'. SSignal era' (CSeg val) -> SignalFun era' shape) polySSignal init upd = SSignal init upd fixInitCapForInit :: (forall era'. CSignal era' val -> signalFun era' shape) -> Capsule val -> (forall era'. SSignal era' (CSeg val) -> signalFun era' shape) fixInitCapForInit fun initCap segs = fun (CSignal initCap segs) fixInitCapForUpd :: (forall era'. CSignal era' val -> signalFun era' shape) -> Unique -> CSeg val -> (forall era'. SSignal era' (CSeg val) -> signalFun era' shape) fixInitCapForUpd fun timeID initSeg segs = fun (CSignal (currentValCapsule timeID initSeg) segs) polyTimeIDApp :: DSignal era (Unique -> CSeg val -> forall era'. SSignal era' (CSeg val) -> SignalFun era' shape) -> DSignal era (CSeg val -> forall era'. SSignal era' (CSeg val) -> SignalFun era' shape) polyTimeIDApp signal = DSignal.timeIDApp signal instance Samplee CSignal where dSample sampler (CSignal _ segs) = (DSignal.crackCapsules . DSignal.timeIDApp) $ timeIDToCapsule <$> sampler <#> segs where timeIDToCapsule fun seg = fmap fun . flip currentValCapsule seg sSample (SSignal samplerInit samplerUpd) signal@(CSignal (Capsule init) _) = signal' where signal' = SSignal (samplerInit init) (samplerUpd <#> signal) -- * Conversion {-| Converts a segmented signal into a continous signal, dropping the information about update points. -} fromSSignal :: SSignal era val -> CSignal era val fromSSignal signal@(SSignal init _) = CSignal (pure init) (fmap pure signal) -- * Connectors {-| Converts a value read action into a continuous signal producer. The producer @producer /readVal/@ produces a continuous signal whose current value is determined by executing @/readVal/@. -} producer :: IO val -> Producer CSignal val producer readVal = Producer $ proc _ -> do seg <- CSeg.producer readVal -< () startTimeID <- getStartTimeID -< () returnA -< CSignal (currentValCapsule startTimeID seg) (pure seg)