-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | FRP Yampa replacement implemented with Monadic Stream Functions. -- -- Yampa is a popular Functional Reactive Programming (FRP) -- implementation that has been used extensively for all kinds of -- applications, including robotics and games. -- -- Monadic Stream Functions are a new abstraction for data -- processors that combine arrows and monads. The library dunai -- provides a default implementation. -- -- Bearriver (a tributary to the Yampa river) provides the same API as -- Yampa, but implemented using dunai underneath. The goal is to -- facilitate understanding what's different about Yampa, and other FRP -- and Reactive Programming libraries, by creating wrappers around dunai -- defined precisely by those differences. -- -- Because dunai is particularly fast, especially with optimizations -- enabled, this implementation is faster than traditional Yampa for -- medium-sized and large applications. @package bearriver @version 0.14 module FRP.BearRiver -- | Time is used both for time intervals (duration), and time w.r.t. some -- agreed reference point in time. type Time = Double -- | DTime is the time type for lengths of sample intervals. Conceptually, -- DTime = R+ = { x in R | x > 0 }. Don't assume Time and DTime have -- the same representation. type DTime = Double -- | Extensible signal function (signal function with a notion of time, but -- which can be extended with actions). Signal function that transforms a -- signal carrying values of some type a into a signal carrying -- values of some type b. You can think of it as (Signal a -> -- Signal b). A signal is, conceptually, a function from Time to -- value. type SF m = MSF (ClockInfo m) -- | Information on the progress of time. type ClockInfo m = ReaderT DTime m -- | A single possible event occurrence, that is, a value that may or may -- not occur. Events are used to represent values that are not produced -- continuously, such as mouse clicks (only produced when the mouse is -- clicked, as opposed to mouse positions, which are always defined). data Event a Event :: a -> Event a NoEvent :: Event a -- | Lifts a pure function into a signal function (applied pointwise). arrPrim :: Monad m => (a -> b) -> SF m a b -- | Lifts a pure function into a signal function applied to events -- (applied pointwise). arrEPrim :: Monad m => (Event a -> b) -> SF m (Event a) b -- | Identity: identity = arr id -- -- Using identity is preferred over lifting id, since the arrow -- combinators know how to optimise certain networks based on the -- transformations being applied. identity :: Monad m => SF m a a -- | Identity: constant b = arr (const b) -- -- Using constant is preferred over lifting const, since the arrow -- combinators know how to optimise certain networks based on the -- transformations being applied. constant :: Monad m => b -> SF m a b -- | Outputs the time passed since the signal function instance was -- started. localTime :: Monad m => SF m a Time -- | Alternative name for localTime. time :: Monad m => SF m a Time -- | Initialization operator (cf. Lustre/Lucid Synchrone). -- -- The output at time zero is the first argument, and from that point on -- it behaves like the signal function passed as second argument. (-->) :: Monad m => b -> SF m a b -> SF m a b infixr 0 --> -- | Output pre-insert operator. -- -- Insert a sample in the output, and from that point on, behave like the -- given sf. (-:>) :: Monad m => b -> SF m a b -> SF m a b infixr 0 -:> -- | Input initialization operator. -- -- The input at time zero is the first argument, and from that point on -- it behaves like the signal function passed as second argument. (>--) :: Monad m => a -> SF m a b -> SF m a b infixr 0 >-- (>=-) :: Monad m => (a -> a) -> SF m a b -> SF m a b infixr 0 >=- initially :: Monad m => a -> SF m a a -- | Applies a function point-wise, using the last output as next input. -- This creates a well-formed loop based on a pure, auxiliary function. sscan :: Monad m => (b -> a -> b) -> b -> SF m a b -- | Generic version of sscan, in which the auxiliary function -- produces an internal accumulator and an "held" output. -- -- Applies a function point-wise, using the last known Just output -- to form the output, and next input accumulator. If the output is -- Nothing, the last known accumulators are used. This creates a -- well-formed loop based on a pure, auxiliary function. sscanPrim :: Monad m => (c -> a -> Maybe (c, b)) -> c -> b -> SF m a b -- | Event source that never occurs. never :: Monad m => SF m a (Event b) -- | Event source with a single occurrence at time 0. The value of the -- event is given by the function argument. now :: Monad m => b -> SF m a (Event b) -- | Event source with a single occurrence at or as soon after (local) time -- q as possible. after :: Monad m => Time -> b -> SF m a (Event b) -- | Event source with repeated occurrences with interval q. Note: If the -- interval is too short w.r.t. the sampling intervals, the result will -- be that events occur at every sample. However, no more than one event -- results from any sampling interval, thus avoiding an "event backlog" -- should sampling become more frequent at some later point in time. repeatedly :: Monad m => Time -> b -> SF m a (Event b) -- | Event source with consecutive occurrences at the given intervals. -- Should more than one event be scheduled to occur in any sampling -- interval, only the first will in fact occur to avoid an event backlog. afterEach :: Monad m => [(Time, b)] -> SF m a (Event b) -- | Event source with consecutive occurrences at the given intervals. -- Should more than one event be scheduled to occur in any sampling -- interval, the output list will contain all events produced during that -- interval. afterEachCat :: Monad m => [(Time, b)] -> SF m a (Event [b]) -- | Apply an MSF to every input. Freezes temporarily if the input -- is NoEvent, and continues as soon as an Event is -- received. mapEventS :: Monad m => MSF m a b -> MSF m (Event a) (Event b) eventToMaybe :: Event a -> Maybe a boolToEvent :: Bool -> Event () -- | A rising edge detector. Useful for things like detecting key presses. -- It is initialised as up, meaning that events occurring at time -- 0 will not be detected. edge :: Monad m => SF m Bool (Event ()) -- | A rising edge detector that can be initialized as up (True, -- meaning that events occurring at time 0 will not be detected) or down -- (False, meaning that events occurring at time 0 will be -- detected). iEdge :: Monad m => Bool -> SF m Bool (Event ()) -- | Like edge, but parameterized on the tag value. -- -- From Yampa edgeTag :: Monad m => a -> SF m Bool (Event a) -- | Edge detector particularized for detecting transtitions on a -- Maybe signal from Nothing to Just. -- -- From Yampa edgeJust :: Monad m => SF m (Maybe a) (Event a) -- | Edge detector parameterized on the edge detection function and initial -- state, i.e., the previous input sample. The first argument to the edge -- detection function is the previous sample, the second the current one. edgeBy :: Monad m => (a -> a -> Maybe b) -> a -> SF m a (Event b) -- | Convert a maybe value into a event (Event is isomorphic to -- Maybe). maybeToEvent :: Maybe a -> Event a edgeFrom :: Monad m => Bool -> SF m Bool (Event ()) -- | Suppression of initial (at local time 0) event. notYet :: Monad m => SF m (Event a) (Event a) -- | Suppress all but the first event. once :: Monad m => SF m (Event a) (Event a) -- | Suppress all but the first n events. takeEvents :: Monad m => Int -> SF m (Event a) (Event a) -- | Suppress first n events. dropEvents :: Monad m => Int -> SF m (Event a) (Event a) -- | Make the NoEvent constructor available. Useful e.g. for -- initialization, ((-->) & friends), and it's easily available -- anyway (e.g. mergeEvents []). noEvent :: Event a -- | Suppress any event in the first component of a pair. noEventFst :: (Event a, b) -> (Event c, b) -- | Suppress any event in the second component of a pair. noEventSnd :: (a, Event b) -> (a, Event c) -- | An event-based version of the maybe function. event :: a -> (b -> a) -> Event b -> a -- | Extract the value from an event. Fails if there is no event. fromEvent :: Event a -> a -- | Tests whether the input represents an actual event. isEvent :: Event a -> Bool -- | Negation of isEvent. isNoEvent :: Event a -> Bool -- | Tags an (occurring) event with a value ("replacing" the old value). -- -- Applicative-based definition: tag = ($>) tag :: Event a -> b -> Event b -- | Tags an (occurring) event with a value ("replacing" the old value). -- Same as tag with the arguments swapped. -- -- Applicative-based definition: tagWith = (<$) tagWith :: b -> Event a -> Event b -- | Attaches an extra value to the value of an occurring event. attach :: Event a -> b -> Event (a, b) -- | Left-biased event merge (always prefer left event, if present). lMerge :: Event a -> Event a -> Event a -- | Right-biased event merge (always prefer right event, if present). rMerge :: Event a -> Event a -> Event a -- | Unbiased event merge: simultaneous occurrence is an error. merge :: Event a -> Event a -> Event a mergeBy :: (a -> a -> a) -> Event a -> Event a -> Event a -- | A generic event merge-map utility that maps event occurrences, merging -- the results. The first three arguments are mapping functions, the -- third of which will only be used when both events are present. -- Therefore, mergeBy = mapMerge id id -- -- Applicative-based definition: mapMerge lf rf lrf le re = (f $ -- le * re) | (lf $ le) | (rf $ re) mapMerge :: (a -> c) -> (b -> c) -> (a -> b -> c) -> Event a -> Event b -> Event c -- | Merge a list of events; foremost event has priority. -- -- Foldable-based definition: mergeEvents :: Foldable t => t (Event a) -- -> Event a mergeEvents = asum mergeEvents :: [Event a] -> Event a -- | Collect simultaneous event occurrences; no event if none. -- -- Traverable-based definition: catEvents :: Foldable t => t (Event a) -- -> Event (t a) carEvents e = if (null e) then NoEvent else -- (sequenceA e) catEvents :: [Event a] -> Event [a] -- | Join (conjunction) of two events. Only produces an event if both -- events exist. -- -- Applicative-based definition: joinE = liftA2 (,) joinE :: Event a -> Event b -> Event (a, b) -- | Split event carrying pairs into two events. splitE :: Event (a, b) -> (Event a, Event b) -- | Filter out events that don't satisfy some predicate. filterE :: (a -> Bool) -> Event a -> Event a -- | Combined event mapping and filtering. Note: since Event is a -- Functor, see fmap for a simpler version of this function -- with no filtering. mapFilterE :: (a -> Maybe b) -> Event a -> Event b -- | Enable/disable event occurences based on an external condition. gate :: Event a -> Bool -> Event a -- | Basic switch. -- -- By default, the first signal function is applied. Whenever the second -- value in the pair actually is an event, the value carried by the event -- is used to obtain a new signal function to be applied *at that time -- and at future times*. Until that happens, the first value in the pair -- is produced in the output signal. -- -- Important note: at the time of switching, the second signal function -- is applied immediately. If that second SF can also switch at time -- zero, then a double (nested) switch might take place. If the second SF -- refers to the first one, the switch might take place infinitely many -- times and never be resolved. -- -- Remember: The continuation is evaluated strictly at the time of -- switching! switch :: Monad m => SF m a (b, Event c) -> (c -> SF m a b) -> SF m a b -- | Switch with delayed observation. -- -- By default, the first signal function is applied. -- -- Whenever the second value in the pair actually is an event, the value -- carried by the event is used to obtain a new signal function to be -- applied *at future times*. -- -- Until that happens, the first value in the pair is produced in the -- output signal. -- -- Important note: at the time of switching, the second signal function -- is used immediately, but the current input is fed by it (even though -- the actual output signal value at time 0 is discarded). -- -- If that second SF can also switch at time zero, then a double (nested) -- -- switch might take place. If the second SF refers to the first one, -- the switch might take place infinitely many times and never be -- resolved. -- -- Remember: The continuation is evaluated strictly at the time of -- switching! dSwitch :: Monad m => SF m a (b, Event c) -> (c -> SF m a b) -> SF m a b -- | Spatial parallel composition of a signal function collection. Given a -- collection of signal functions, it returns a signal function that -- broadcasts its input signal to every element of the collection, to -- return a signal carrying a collection of outputs. See par. -- -- For more information on how parallel composition works, check -- https://www.antonycourtney.com/pubs/hw03.pdf parB :: Monad m => [SF m a b] -> SF m a [b] -- | Decoupled parallel switch with broadcasting (dynamic collection of -- signal functions spatially composed in parallel). See -- dpSwitch. -- -- For more information on how parallel composition works, check -- https://www.antonycourtney.com/pubs/hw03.pdf dpSwitchB :: (Functor m, Monad m, Traversable col) => col (SF m a b) -> SF m (a, col b) (Event c) -> (col (SF m a b) -> c -> SF m a (col b)) -> SF m a (col b) -- | Apply an SF to every element of a list. -- -- Example: -- --
-- >>> embed (parC integral) (deltaEncode 0.1 [[1, 2], [2, 4], [3, 6], [4.0, 8.0 :: Float]]) -- [[0.0,0.0],[0.1,0.2],[0.3,0.6],[0.6,1.2]] ---- -- The number of SFs or expected inputs is determined by the first input -- list, and not expected to vary over time. -- -- If more inputs come in a subsequent list, they are ignored. -- --
-- >>> embed (parC (arr (+1))) (deltaEncode 0.1 [[0], [1, 1], [3, 4], [6, 7, 8], [1, 1], [0, 0], [1, 9, 8]]) -- [[1],[2],[4],[7],[2],[1],[2]] ---- -- If less inputs come in a subsequent list, an exception is thrown. -- --
-- >>> embed (parC (arr (+1))) (deltaEncode 0.1 [[0, 0], [1, 1], [3, 4], [6, 7, 8], [1, 1], [0, 0], [1, 9, 8]]) -- [[1,1],[2,2],[4,5],[7,8],[2,2],[1,1],[2,10]] --parC :: Monad m => SF m a b -> SF m [a] [b] -- | Zero-order hold. -- -- Converts a discrete-time signal into a continuous-time signal, by -- holding the last value until it changes in the input signal. The given -- parameter may be used for time zero, and until the first event occurs -- in the input signal, so hold is always well-initialized. -- --
-- >>> embed (hold 1) (deltaEncode 0.1 [NoEvent, NoEvent, Event 2, NoEvent, Event 3, NoEvent]) -- [1,1,2,2,3,3] --hold :: Monad m => a -> SF m (Event a) a -- | Accumulator parameterized by the accumulation function. accumBy :: Monad m => (b -> a -> b) -> b -> SF m (Event a) (Event b) -- | Zero-order hold accumulator parameterized by the accumulation -- function. accumHoldBy :: Monad m => (b -> a -> b) -> b -> SF m (Event a) b -- | Loop with an initial value for the signal being fed back. loopPre :: Monad m => c -> SF m (a, c) (b, c) -> SF m a b -- | Integration using the rectangle rule. integral :: (Monad m, Fractional s, VectorSpace a s) => SF m a a -- | Integrate using an auxiliary function that takes the current and the -- last input, the time between those samples, and the last output, and -- returns a new output. integralFrom :: (Monad m, Fractional s, VectorSpace a s) => a -> SF m a a -- | A very crude version of a derivative. It simply divides the value -- difference by the time difference. Use at your own risk. derivative :: (Monad m, Fractional s, VectorSpace a s) => SF m a a derivativeFrom :: (Monad m, Fractional s, VectorSpace a s) => a -> SF m a a iterFrom :: Monad m => (a -> a -> DTime -> b -> b) -> b -> SF m a b -- | Stochastic event source with events occurring on average once every -- t_avg seconds. However, no more than one event results from any one -- sampling interval in the case of relatively sparse sampling, thus -- avoiding an "event backlog" should sampling become more frequent at -- some later point in time. occasionally :: MonadRandom m => Time -> b -> SF m a (Event b) -- | Convenience function to run a signal function indefinitely, using a IO -- actions to obtain new input and process the output. -- -- This function first runs the initialization action, which provides the -- initial input for the signal transformer at time 0. -- -- Afterwards, an input sensing action is used to obtain new input (if -- any) and the time since the last iteration. The argument to the input -- sensing function indicates if it can block. If no new input is -- received, it is assumed to be the same as in the last iteration. -- -- After applying the signal function to the input, the actuation IO -- action is executed. The first argument indicates if the output has -- changed, the second gives the actual output). Actuation functions may -- choose to ignore the first argument altogether. This action should -- return True if the reactimation must stop, and False if it should -- continue. -- -- Note that this becomes the program's main loop, which makes -- using this function incompatible with GLUT, Gtk and other graphics -- libraries. It may also impose a sizeable constraint in larger projects -- in which different subparts run at different time steps. If you need -- to control the main loop yourself for these or other reasons, use -- reactInit and react. reactimate :: Monad m => m a -> (Bool -> m (DTime, Maybe a)) -> (Bool -> b -> m Bool) -> SF Identity a b -> m () -- | Evaluate an SF, and return an output and an initialized SF. -- -- WARN: Do not use this function for standard simulation. This -- function is intended only for debugging/testing. Apart from being -- potentially slower and consuming more memory, it also breaks the FRP -- abstraction by making samples discrete and step based. evalAtZero :: SF Identity a b -> a -> (b, SF Identity a b) -- | Evaluate an initialized SF, and return an output and a continuation. -- -- WARN: Do not use this function for standard simulation. This -- function is intended only for debugging/testing. Apart from being -- potentially slower and consuming more memory, it also breaks the FRP -- abstraction by making samples discrete and step based. evalAt :: SF Identity a b -> DTime -> a -> (b, SF Identity a b) -- | Given a signal function and time delta, it moves the signal function -- into the future, returning a new uninitialized SF and the initial -- output. -- -- While the input sample refers to the present, the time delta refers to -- the future (or to the time between the current sample and the next -- sample). -- -- WARN: Do not use this function for standard simulation. This -- function is intended only for debugging/testing. Apart from being -- potentially slower and consuming more memory, it also breaks the FRP -- abstraction by making samples discrete and step based. evalFuture :: SF Identity a b -> a -> DTime -> (b, SF Identity a b) replaceOnce :: Monad m => a -> SF m a a dup :: b -> (b, b) instance GHC.Show.Show a => GHC.Show.Show (FRP.BearRiver.Event a) instance GHC.Classes.Ord a => GHC.Classes.Ord (FRP.BearRiver.Event a) instance GHC.Classes.Eq a => GHC.Classes.Eq (FRP.BearRiver.Event a) instance GHC.Base.Functor FRP.BearRiver.Event instance GHC.Base.Applicative FRP.BearRiver.Event instance GHC.Base.Monad FRP.BearRiver.Event instance Control.Monad.Fail.MonadFail FRP.BearRiver.Event instance GHC.Base.Alternative FRP.BearRiver.Event instance Control.DeepSeq.NFData a => Control.DeepSeq.NFData (FRP.BearRiver.Event a) module FRP.Yampa -- | Kleisli arrows of a monad. newtype Kleisli (m :: Type -> Type) a b Kleisli :: (a -> m b) -> Kleisli (m :: Type -> Type) a b [runKleisli] :: Kleisli (m :: Type -> Type) a b -> a -> m b class Arrow a => ArrowZero (a :: TYPE LiftedRep -> TYPE LiftedRep -> Type) zeroArrow :: ArrowZero a => a b c -- | A monoid on arrows. class ArrowZero a => ArrowPlus (a :: TYPE LiftedRep -> TYPE LiftedRep -> Type) -- | An associative operation with identity zeroArrow. (<+>) :: ArrowPlus a => a b c -> a b c -> a b c infixr 5 <+> -- | The ArrowApply class is equivalent to Monad: any monad -- gives rise to a Kleisli arrow, and any instance of -- ArrowApply defines a monad. newtype ArrowMonad (a :: Type -> Type -> Type) b ArrowMonad :: a () b -> ArrowMonad (a :: Type -> Type -> Type) b -- | The loop operator expresses computations in which an output -- value is fed back as input, although the computation occurs only once. -- It underlies the rec value recursion construct in arrow -- notation. loop should satisfy the following laws: -- --
-- assoc ((a,b),c) = (a,(b,c)) -- unassoc (a,(b,c)) = ((a,b),c) --class Arrow a => ArrowLoop (a :: TYPE LiftedRep -> TYPE LiftedRep -> Type) loop :: ArrowLoop a => a (b, d) (c, d) -> a b c -- | Choice, for arrows that support it. This class underlies the -- if and case constructs in arrow notation. -- -- Instances should satisfy the following laws: -- --
left (arr f) = arr (left -- f)
left (f >>> g) = left f >>> -- left g
f >>> arr Left = arr -- Left >>> left f
left f >>> arr (id +++ g) = -- arr (id +++ g) >>> left f
left (left f) >>> arr assocsum -- = arr assocsum >>> left f
-- assocsum (Left (Left x)) = Left x -- assocsum (Left (Right y)) = Right (Left y) -- assocsum (Right z) = Right (Right z) ---- -- The other combinators have sensible default definitions, which may be -- overridden for efficiency. class Arrow a => ArrowChoice (a :: TYPE LiftedRep -> TYPE LiftedRep -> Type) -- | Feed marked inputs through the argument arrow, passing the rest -- through unchanged to the output. left :: ArrowChoice a => a b c -> a (Either b d) (Either c d) -- | A mirror image of left. -- -- The default definition may be overridden with a more efficient version -- if desired. right :: ArrowChoice a => a b c -> a (Either d b) (Either d c) -- | Split the input between the two argument arrows, retagging and merging -- their outputs. Note that this is in general not a functor. -- -- The default definition may be overridden with a more efficient version -- if desired. (+++) :: ArrowChoice a => a b c -> a b' c' -> a (Either b b') (Either c c') -- | Fanin: Split the input between the two argument arrows and merge their -- outputs. -- -- The default definition may be overridden with a more efficient version -- if desired. (|||) :: ArrowChoice a => a b d -> a c d -> a (Either b c) d infixr 2 ||| infixr 2 +++ -- | Some arrows allow application of arrow inputs to other inputs. -- Instances should satisfy the following laws: -- --
first (arr (\x -> arr (\y -> -- (x,y)))) >>> app = id
first (arr (g >>>)) >>> -- app = second g >>> app
first (arr (>>> h)) >>> -- app = app >>> h
arr id = id
arr (f >>> g) = arr f >>> -- arr g
first (arr f) = arr (first -- f)
first (f >>> g) = first f >>> -- first g
first f >>> arr fst = -- arr fst >>> f
first f >>> arr (id *** g) = -- arr (id *** g) >>> first f
first (first f) >>> arr assoc = -- arr assoc >>> first f
-- assoc ((a,b),c) = (a,(b,c)) ---- -- The other combinators have sensible default definitions, which may be -- overridden for efficiency. class Category a => Arrow (a :: TYPE LiftedRep -> TYPE LiftedRep -> Type) -- | Lift a function to an arrow. arr :: Arrow a => (b -> c) -> a b c -- | Send the first component of the input through the argument arrow, and -- copy the rest unchanged to the output. first :: Arrow a => a b c -> a (b, d) (c, d) -- | A mirror image of first. -- -- The default definition may be overridden with a more efficient version -- if desired. second :: Arrow a => a b c -> a (d, b) (d, c) -- | Split the input between the two argument arrows and combine their -- output. Note that this is in general not a functor. -- -- The default definition may be overridden with a more efficient version -- if desired. (***) :: Arrow a => a b c -> a b' c' -> a (b, b') (c, c') -- | Fanout: send the input to both argument arrows and combine their -- output. -- -- The default definition may be overridden with a more efficient version -- if desired. (&&&) :: Arrow a => a b c -> a b c' -> a b (c, c') infixr 3 &&& infixr 3 *** -- | The identity arrow, which plays the role of return in arrow -- notation. returnA :: Arrow a => a b b -- | Any instance of ArrowApply can be made into an instance of -- ArrowChoice by defining left = leftApp. leftApp :: ArrowApply a => a b c -> a (Either b d) (Either c d) -- | Precomposition with a pure function. (^>>) :: Arrow a => (b -> c) -> a c d -> a b d infixr 1 ^>> -- | Postcomposition with a pure function (right-to-left variant). (^<<) :: Arrow a => (c -> d) -> a b c -> a b d infixr 1 ^<< -- | Postcomposition with a pure function. (>>^) :: Arrow a => a b c -> (c -> d) -> a b d infixr 1 >>^ -- | Precomposition with a pure function (right-to-left variant). (<<^) :: Arrow a => a c d -> (b -> c) -> a b d infixr 1 <<^ -- | Left-to-right composition (>>>) :: forall {k} cat (a :: k) (b :: k) (c :: k). Category cat => cat a b -> cat b c -> cat a c infixr 1 >>> -- | Right-to-left composition (<<<) :: forall {k} cat (b :: k) (c :: k) (a :: k). Category cat => cat b c -> cat a b -> cat a c infixr 1 <<< -- | Outputs every input sample, with a given message prefix, when a -- condition is met, and waits for some input / enter to continue. pauseOn :: Show a => (a -> Bool) -> String -> MSF IO a a -- | Outputs every input sample, with a given message prefix, using an -- auxiliary printing function, when a condition is met. traceWhen :: (Monad m, Show a) => (a -> Bool) -> (String -> m ()) -> String -> MSF m a a -- | Outputs every input sample, with a given message prefix, using an -- auxiliary printing function. traceWith :: (Monad m, Show a) => (String -> m ()) -> String -> MSF m a a -- | Generate outputs using a step-wise generation function and an initial -- value. unfold :: forall (m :: Type -> Type) a b. Monad m => (a -> (b, a)) -> a -> MSF m () b -- | Applies a transfer function to the input and an accumulator, returning -- the updated accumulator and output. mealy :: forall (m :: Type -> Type) a s b. Monad m => (a -> s -> (b, s)) -> s -> MSF m a b -- | Applies a function to the input and an accumulator, outputting the -- updated accumulator. Equal to f s0 -> feedback s0 $ arr -- (uncurry f >>> dup). accumulateWith :: forall (m :: Type -> Type) a s. Monad m => (a -> s -> s) -> s -> MSF m a s -- | Accumulate the inputs, starting from an initial monoid value. mappendFrom :: forall n (m :: Type -> Type). (Monoid n, Monad m) => n -> MSF m n n -- | Accumulate the inputs, starting from mempty. mappendS :: forall n (m :: Type -> Type). (Monoid n, Monad m) => MSF m n n -- | Sums the inputs, starting from an initial vector. sumFrom :: forall v s (m :: Type -> Type). (VectorSpace v s, Monad m) => v -> MSF m v v -- | Sums the inputs, starting from zero. sumS :: forall v s (m :: Type -> Type). (VectorSpace v s, Monad m) => MSF m v v -- | Count the number of simulation steps. Produces 1, 2, 3,... count :: forall n (m :: Type -> Type) a. (Num n, Monad m) => MSF m a n -- | Buffers and returns the elements in FIFO order, returning -- Nothing whenever the buffer is empty. fifo :: forall (m :: Type -> Type) a. Monad m => MSF m [a] (Maybe a) -- | Preprends a fixed output to an MSF, shifting the output. next :: forall (m :: Type -> Type) b a. Monad m => b -> MSF m a b -> MSF m a b -- | Preprends a fixed output to an MSF. The first input is -- completely ignored. iPost :: forall (m :: Type -> Type) b a. Monad m => b -> MSF m a b -> MSF m a b -- | Delay a signal by one sample. iPre :: forall (m :: Type -> Type) a. Monad m => a -> MSF m a a -- | Produces an additional side effect and passes the input unchanged. withSideEffect_ :: Monad m => m b -> MSF m a a -- | Applies a function to produce an additional side effect and passes the -- input unchanged. withSideEffect :: Monad m => (a -> m b) -> MSF m a a -- | Apply an MSF to every input. Freezes temporarily if the input -- is Nothing, and continues as soon as a Just is received. mapMaybeS :: forall (m :: Type -> Type) a b. Monad m => MSF m a b -> MSF m (Maybe a) (Maybe b) -- | A stream is an MSF that produces outputs, while ignoring the -- input. It can obtain the values from a monadic context. type MStream (m :: Type -> Type) a = MSF m () a -- | A sink is an MSF that consumes inputs, while producing no -- output. It can consume the values with side effects. type MSink (m :: Type -> Type) a = MSF m a () -- | Apply trans-monadic actions (in an arbitrary way). -- -- This is just a convenience function when you have a function to move -- across monads, because the signature of morphGS is a bit -- complex. morphS :: (Monad m2, Monad m1) => (forall c. () => m1 c -> m2 c) -> MSF m1 a b -> MSF m2 a b -- | Lift inner monadic actions in monad stacks. liftTransS :: forall (t :: (Type -> Type) -> Type -> Type) (m :: Type -> Type) a b. (MonadTrans t, Monad m, Monad (t m)) => MSF m a b -> MSF (t m) a b -- | Lift the second MSF into the monad of the first. (>>>^) :: forall (m1 :: Type -> Type) (m2 :: Type -> Type) a b c. MonadBase m1 m2 => MSF m2 a b -> MSF m1 b c -> MSF m2 a c -- | Lift the first MSF into the monad of the second. (^>>>) :: forall (m1 :: Type -> Type) (m2 :: Type -> Type) a b c. MonadBase m1 m2 => MSF m1 a b -> MSF m2 b c -> MSF m2 a c -- | Lift innermost monadic actions in monad stack (generalisation of -- liftIO). liftBaseS :: forall (m2 :: Type -> Type) (m1 :: Type -> Type) a b. (Monad m2, MonadBase m1 m2) => MSF m1 a b -> MSF m2 a b -- | Monadic lifting from one monad into another liftBaseM :: forall (m2 :: Type -> Type) m1 a b. (Monad m2, MonadBase m1 m2) => (a -> m1 b) -> MSF m2 a b -- | Apply a monadic transformation to every element of the input stream. -- -- Generalisation of arr from Arrow to monadic functions. arrM :: Monad m => (a -> m b) -> MSF m a b -- | Lifts a monadic computation into a Stream. constM :: Monad m => m b -> MSF m a b -- | Apply a monadic stream function to a list. -- -- Because the result is in a monad, it may be necessary to traverse the -- whole list to evaluate the value in the results to WHNF. For example, -- if the monad is the maybe monad, this may not produce anything if the -- MSF produces Nothing at any point, so the output stream -- cannot consumed progressively. -- -- To explore the output progressively, use arrM and -- (>>>)', together with some action that -- consumes/actuates on the output. -- -- This is called runSF in Liu, Cheng, Hudak, "Causal -- Commutative Arrows and Their Optimization" embed :: Monad m => MSF m a b -> [a] -> m [b] -- | Well-formed looped connection of an output component as a future -- input. feedback :: forall (m :: Type -> Type) c a b. Monad m => c -> MSF m (a, c) (b, c) -> MSF m a b -- | Generic lifting of a morphism to the level of MSFs. -- -- Natural transformation to the level of MSFs. -- -- Mathematical background: The type a -> m (b, c) is -- a functor in c, and MSF m a b is its greatest -- fixpoint, i.e. it is isomorphic to the type a -> m (b, MSF m a -- b), by definition. The types m, a and -- b are parameters of the functor. Taking a fixpoint is -- functorial itself, meaning that a morphism (a natural transformation) -- of two such functors gives a morphism (an ordinary function) of their -- fixpoints. -- -- This is in a sense the most general "abstract" lifting function, i.e. -- the most general one that only changes input, output and side effect -- types, and doesn't influence control flow. Other handling functions -- like exception handling or ListT broadcasting necessarily -- change control flow. morphGS :: Monad m2 => (forall c. () => (a1 -> m1 (b1, c)) -> a2 -> m2 (b2, c)) -> MSF m1 a1 b1 -> MSF m2 a2 b2 -- | Stepwise, side-effectful MSFs without implicit knowledge of -- time. -- -- MSFs should be applied to streams or executed indefinitely or -- until they terminate. See reactimate and reactimateB -- for details. In general, calling the value constructor MSF or -- the function unMSF is discouraged. data MSF (m :: Type -> Type) a b -- | Vector space type relation. -- -- A vector space is a set (type) closed under addition and -- multiplication by a scalar. The type of the scalar is the field -- of the vector space, and it is said that v is a vector space -- over a. -- -- The encoding uses a type class |VectorSpace| v a, where -- v represents the type of the vectors and a -- represents the types of the scalars. class VectorSpace v a | v -> a -- | Vector with no magnitude (unit for addition). zeroVector :: VectorSpace v a => v -- | Multiplication by a scalar. (*^) :: VectorSpace v a => a -> v -> v -- | Division by a scalar. (^/) :: VectorSpace v a => v -> a -> v -- | Vector addition (^+^) :: VectorSpace v a => v -> v -> v -- | Vector subtraction (^-^) :: VectorSpace v a => v -> v -> v -- | Vector negation. Addition with a negated vector should be same as -- subtraction. negateVector :: VectorSpace v a => v -> v -- | Dot product (also known as scalar or inner product). -- -- For two vectors, mathematically represented as a = -- a1,a2,...,an and b = b1,b2,...,bn, the dot product is -- a . b = a1*b1 + a2*b2 + ... + an*bn. -- -- Some properties are derived from this. The dot product of a vector -- with itself is the square of its magnitude (norm), and the dot -- product of two orthogonal vectors is zero. dot :: VectorSpace v a => v -> v -> a -- | Vector's norm (also known as magnitude). -- -- For a vector represented mathematically as a = a1,a2,...,an, -- the norm is the square root of a1^2 + a2^2 + ... + an^2. norm :: VectorSpace v a => v -> a -- | Return a vector with the same origin and orientation (angle), but such -- that the norm is one (the unit for multiplication by a scalar). normalize :: VectorSpace v a => v -> v infix 7 `dot` infixl 6 ^-^ infixl 6 ^+^ infixl 9 ^/ infixr 9 *^ -- | A single possible event occurrence, that is, a value that may or may -- not occur. Events are used to represent values that are not produced -- continuously, such as mouse clicks (only produced when the mouse is -- clicked, as opposed to mouse positions, which are always defined). data Event a Event :: a -> Event a NoEvent :: Event a -- | Information on the progress of time. type ClockInfo m = ReaderT DTime m -- | DTime is the time type for lengths of sample intervals. Conceptually, -- DTime = R+ = { x in R | x > 0 }. Don't assume Time and DTime have -- the same representation. type DTime = Double -- | Time is used both for time intervals (duration), and time w.r.t. some -- agreed reference point in time. type Time = Double -- | Lifts a pure function into a signal function (applied pointwise). arrPrim :: Monad m => (a -> b) -> SF m a b -- | Lifts a pure function into a signal function applied to events -- (applied pointwise). arrEPrim :: Monad m => (Event a -> b) -> SF m (Event a) b -- | Identity: identity = arr id -- -- Using identity is preferred over lifting id, since the arrow -- combinators know how to optimise certain networks based on the -- transformations being applied. identity :: Monad m => SF m a a -- | Identity: constant b = arr (const b) -- -- Using constant is preferred over lifting const, since the arrow -- combinators know how to optimise certain networks based on the -- transformations being applied. constant :: Monad m => b -> SF m a b -- | Outputs the time passed since the signal function instance was -- started. localTime :: Monad m => SF m a Time -- | Alternative name for localTime. time :: Monad m => SF m a Time -- | Initialization operator (cf. Lustre/Lucid Synchrone). -- -- The output at time zero is the first argument, and from that point on -- it behaves like the signal function passed as second argument. (-->) :: Monad m => b -> SF m a b -> SF m a b infixr 0 --> -- | Output pre-insert operator. -- -- Insert a sample in the output, and from that point on, behave like the -- given sf. (-:>) :: Monad m => b -> SF m a b -> SF m a b infixr 0 -:> -- | Input initialization operator. -- -- The input at time zero is the first argument, and from that point on -- it behaves like the signal function passed as second argument. (>--) :: Monad m => a -> SF m a b -> SF m a b infixr 0 >-- (>=-) :: Monad m => (a -> a) -> SF m a b -> SF m a b infixr 0 >=- initially :: Monad m => a -> SF m a a -- | Applies a function point-wise, using the last output as next input. -- This creates a well-formed loop based on a pure, auxiliary function. sscan :: Monad m => (b -> a -> b) -> b -> SF m a b -- | Generic version of sscan, in which the auxiliary function -- produces an internal accumulator and an "held" output. -- -- Applies a function point-wise, using the last known Just output -- to form the output, and next input accumulator. If the output is -- Nothing, the last known accumulators are used. This creates a -- well-formed loop based on a pure, auxiliary function. sscanPrim :: Monad m => (c -> a -> Maybe (c, b)) -> c -> b -> SF m a b -- | Event source that never occurs. never :: Monad m => SF m a (Event b) -- | Event source with a single occurrence at time 0. The value of the -- event is given by the function argument. now :: Monad m => b -> SF m a (Event b) -- | Event source with a single occurrence at or as soon after (local) time -- q as possible. after :: Monad m => Time -> b -> SF m a (Event b) -- | Event source with repeated occurrences with interval q. Note: If the -- interval is too short w.r.t. the sampling intervals, the result will -- be that events occur at every sample. However, no more than one event -- results from any sampling interval, thus avoiding an "event backlog" -- should sampling become more frequent at some later point in time. repeatedly :: Monad m => Time -> b -> SF m a (Event b) -- | Event source with consecutive occurrences at the given intervals. -- Should more than one event be scheduled to occur in any sampling -- interval, only the first will in fact occur to avoid an event backlog. afterEach :: Monad m => [(Time, b)] -> SF m a (Event b) -- | Event source with consecutive occurrences at the given intervals. -- Should more than one event be scheduled to occur in any sampling -- interval, the output list will contain all events produced during that -- interval. afterEachCat :: Monad m => [(Time, b)] -> SF m a (Event [b]) -- | Apply an MSF to every input. Freezes temporarily if the input -- is NoEvent, and continues as soon as an Event is -- received. mapEventS :: Monad m => MSF m a b -> MSF m (Event a) (Event b) eventToMaybe :: Event a -> Maybe a boolToEvent :: Bool -> Event () -- | A rising edge detector. Useful for things like detecting key presses. -- It is initialised as up, meaning that events occurring at time -- 0 will not be detected. edge :: Monad m => SF m Bool (Event ()) -- | A rising edge detector that can be initialized as up (True, -- meaning that events occurring at time 0 will not be detected) or down -- (False, meaning that events occurring at time 0 will be -- detected). iEdge :: Monad m => Bool -> SF m Bool (Event ()) -- | Like edge, but parameterized on the tag value. -- -- From Yampa edgeTag :: Monad m => a -> SF m Bool (Event a) -- | Edge detector particularized for detecting transtitions on a -- Maybe signal from Nothing to Just. -- -- From Yampa edgeJust :: Monad m => SF m (Maybe a) (Event a) -- | Edge detector parameterized on the edge detection function and initial -- state, i.e., the previous input sample. The first argument to the edge -- detection function is the previous sample, the second the current one. edgeBy :: Monad m => (a -> a -> Maybe b) -> a -> SF m a (Event b) -- | Convert a maybe value into a event (Event is isomorphic to -- Maybe). maybeToEvent :: Maybe a -> Event a edgeFrom :: Monad m => Bool -> SF m Bool (Event ()) -- | Suppression of initial (at local time 0) event. notYet :: Monad m => SF m (Event a) (Event a) -- | Suppress all but the first event. once :: Monad m => SF m (Event a) (Event a) -- | Suppress all but the first n events. takeEvents :: Monad m => Int -> SF m (Event a) (Event a) -- | Suppress first n events. dropEvents :: Monad m => Int -> SF m (Event a) (Event a) -- | Make the NoEvent constructor available. Useful e.g. for -- initialization, ((-->) & friends), and it's easily available -- anyway (e.g. mergeEvents []). noEvent :: Event a -- | Suppress any event in the first component of a pair. noEventFst :: (Event a, b) -> (Event c, b) -- | Suppress any event in the second component of a pair. noEventSnd :: (a, Event b) -> (a, Event c) -- | An event-based version of the maybe function. event :: a -> (b -> a) -> Event b -> a -- | Extract the value from an event. Fails if there is no event. fromEvent :: Event a -> a -- | Tests whether the input represents an actual event. isEvent :: Event a -> Bool -- | Negation of isEvent. isNoEvent :: Event a -> Bool -- | Tags an (occurring) event with a value ("replacing" the old value). -- -- Applicative-based definition: tag = ($>) tag :: Event a -> b -> Event b -- | Tags an (occurring) event with a value ("replacing" the old value). -- Same as tag with the arguments swapped. -- -- Applicative-based definition: tagWith = (<$) tagWith :: b -> Event a -> Event b -- | Attaches an extra value to the value of an occurring event. attach :: Event a -> b -> Event (a, b) -- | Left-biased event merge (always prefer left event, if present). lMerge :: Event a -> Event a -> Event a -- | Right-biased event merge (always prefer right event, if present). rMerge :: Event a -> Event a -> Event a -- | Unbiased event merge: simultaneous occurrence is an error. merge :: Event a -> Event a -> Event a mergeBy :: (a -> a -> a) -> Event a -> Event a -> Event a -- | A generic event merge-map utility that maps event occurrences, merging -- the results. The first three arguments are mapping functions, the -- third of which will only be used when both events are present. -- Therefore, mergeBy = mapMerge id id -- -- Applicative-based definition: mapMerge lf rf lrf le re = (f $ -- le * re) | (lf $ le) | (rf $ re) mapMerge :: (a -> c) -> (b -> c) -> (a -> b -> c) -> Event a -> Event b -> Event c -- | Merge a list of events; foremost event has priority. -- -- Foldable-based definition: mergeEvents :: Foldable t => t (Event a) -- -> Event a mergeEvents = asum mergeEvents :: [Event a] -> Event a -- | Collect simultaneous event occurrences; no event if none. -- -- Traverable-based definition: catEvents :: Foldable t => t (Event a) -- -> Event (t a) carEvents e = if (null e) then NoEvent else -- (sequenceA e) catEvents :: [Event a] -> Event [a] -- | Join (conjunction) of two events. Only produces an event if both -- events exist. -- -- Applicative-based definition: joinE = liftA2 (,) joinE :: Event a -> Event b -> Event (a, b) -- | Split event carrying pairs into two events. splitE :: Event (a, b) -> (Event a, Event b) -- | Filter out events that don't satisfy some predicate. filterE :: (a -> Bool) -> Event a -> Event a -- | Combined event mapping and filtering. Note: since Event is a -- Functor, see fmap for a simpler version of this function -- with no filtering. mapFilterE :: (a -> Maybe b) -> Event a -> Event b -- | Enable/disable event occurences based on an external condition. gate :: Event a -> Bool -> Event a -- | Basic switch. -- -- By default, the first signal function is applied. Whenever the second -- value in the pair actually is an event, the value carried by the event -- is used to obtain a new signal function to be applied *at that time -- and at future times*. Until that happens, the first value in the pair -- is produced in the output signal. -- -- Important note: at the time of switching, the second signal function -- is applied immediately. If that second SF can also switch at time -- zero, then a double (nested) switch might take place. If the second SF -- refers to the first one, the switch might take place infinitely many -- times and never be resolved. -- -- Remember: The continuation is evaluated strictly at the time of -- switching! switch :: Monad m => SF m a (b, Event c) -> (c -> SF m a b) -> SF m a b -- | Switch with delayed observation. -- -- By default, the first signal function is applied. -- -- Whenever the second value in the pair actually is an event, the value -- carried by the event is used to obtain a new signal function to be -- applied *at future times*. -- -- Until that happens, the first value in the pair is produced in the -- output signal. -- -- Important note: at the time of switching, the second signal function -- is used immediately, but the current input is fed by it (even though -- the actual output signal value at time 0 is discarded). -- -- If that second SF can also switch at time zero, then a double (nested) -- -- switch might take place. If the second SF refers to the first one, -- the switch might take place infinitely many times and never be -- resolved. -- -- Remember: The continuation is evaluated strictly at the time of -- switching! dSwitch :: Monad m => SF m a (b, Event c) -> (c -> SF m a b) -> SF m a b -- | Spatial parallel composition of a signal function collection. Given a -- collection of signal functions, it returns a signal function that -- broadcasts its input signal to every element of the collection, to -- return a signal carrying a collection of outputs. See par. -- -- For more information on how parallel composition works, check -- https://www.antonycourtney.com/pubs/hw03.pdf parB :: Monad m => [SF m a b] -> SF m a [b] -- | Decoupled parallel switch with broadcasting (dynamic collection of -- signal functions spatially composed in parallel). See -- dpSwitch. -- -- For more information on how parallel composition works, check -- https://www.antonycourtney.com/pubs/hw03.pdf dpSwitchB :: (Functor m, Monad m, Traversable col) => col (SF m a b) -> SF m (a, col b) (Event c) -> (col (SF m a b) -> c -> SF m a (col b)) -> SF m a (col b) -- | Apply an SF to every element of a list. -- -- Example: -- --
-- >>> embed (parC integral) (deltaEncode 0.1 [[1, 2], [2, 4], [3, 6], [4.0, 8.0 :: Float]]) -- [[0.0,0.0],[0.1,0.2],[0.3,0.6],[0.6,1.2]] ---- -- The number of SFs or expected inputs is determined by the first input -- list, and not expected to vary over time. -- -- If more inputs come in a subsequent list, they are ignored. -- --
-- >>> embed (parC (arr (+1))) (deltaEncode 0.1 [[0], [1, 1], [3, 4], [6, 7, 8], [1, 1], [0, 0], [1, 9, 8]]) -- [[1],[2],[4],[7],[2],[1],[2]] ---- -- If less inputs come in a subsequent list, an exception is thrown. -- --
-- >>> embed (parC (arr (+1))) (deltaEncode 0.1 [[0, 0], [1, 1], [3, 4], [6, 7, 8], [1, 1], [0, 0], [1, 9, 8]]) -- [[1,1],[2,2],[4,5],[7,8],[2,2],[1,1],[2,10]] --parC :: Monad m => SF m a b -> SF m [a] [b] -- | Zero-order hold. -- -- Converts a discrete-time signal into a continuous-time signal, by -- holding the last value until it changes in the input signal. The given -- parameter may be used for time zero, and until the first event occurs -- in the input signal, so hold is always well-initialized. -- --
-- >>> embed (hold 1) (deltaEncode 0.1 [NoEvent, NoEvent, Event 2, NoEvent, Event 3, NoEvent]) -- [1,1,2,2,3,3] --hold :: Monad m => a -> SF m (Event a) a -- | Accumulator parameterized by the accumulation function. accumBy :: Monad m => (b -> a -> b) -> b -> SF m (Event a) (Event b) -- | Zero-order hold accumulator parameterized by the accumulation -- function. accumHoldBy :: Monad m => (b -> a -> b) -> b -> SF m (Event a) b -- | Loop with an initial value for the signal being fed back. loopPre :: Monad m => c -> SF m (a, c) (b, c) -> SF m a b -- | Integration using the rectangle rule. integral :: (Monad m, Fractional s, VectorSpace a s) => SF m a a -- | Integrate using an auxiliary function that takes the current and the -- last input, the time between those samples, and the last output, and -- returns a new output. integralFrom :: (Monad m, Fractional s, VectorSpace a s) => a -> SF m a a -- | A very crude version of a derivative. It simply divides the value -- difference by the time difference. Use at your own risk. derivative :: (Monad m, Fractional s, VectorSpace a s) => SF m a a derivativeFrom :: (Monad m, Fractional s, VectorSpace a s) => a -> SF m a a iterFrom :: Monad m => (a -> a -> DTime -> b -> b) -> b -> SF m a b -- | Stochastic event source with events occurring on average once every -- t_avg seconds. However, no more than one event results from any one -- sampling interval in the case of relatively sparse sampling, thus -- avoiding an "event backlog" should sampling become more frequent at -- some later point in time. occasionally :: MonadRandom m => Time -> b -> SF m a (Event b) -- | Convenience function to run a signal function indefinitely, using a IO -- actions to obtain new input and process the output. -- -- This function first runs the initialization action, which provides the -- initial input for the signal transformer at time 0. -- -- Afterwards, an input sensing action is used to obtain new input (if -- any) and the time since the last iteration. The argument to the input -- sensing function indicates if it can block. If no new input is -- received, it is assumed to be the same as in the last iteration. -- -- After applying the signal function to the input, the actuation IO -- action is executed. The first argument indicates if the output has -- changed, the second gives the actual output). Actuation functions may -- choose to ignore the first argument altogether. This action should -- return True if the reactimation must stop, and False if it should -- continue. -- -- Note that this becomes the program's main loop, which makes -- using this function incompatible with GLUT, Gtk and other graphics -- libraries. It may also impose a sizeable constraint in larger projects -- in which different subparts run at different time steps. If you need -- to control the main loop yourself for these or other reasons, use -- reactInit and react. reactimate :: Monad m => m a -> (Bool -> m (DTime, Maybe a)) -> (Bool -> b -> m Bool) -> SF Identity a b -> m () -- | Evaluate an SF, and return an output and an initialized SF. -- -- WARN: Do not use this function for standard simulation. This -- function is intended only for debugging/testing. Apart from being -- potentially slower and consuming more memory, it also breaks the FRP -- abstraction by making samples discrete and step based. evalAtZero :: SF Identity a b -> a -> (b, SF Identity a b) -- | Evaluate an initialized SF, and return an output and a continuation. -- -- WARN: Do not use this function for standard simulation. This -- function is intended only for debugging/testing. Apart from being -- potentially slower and consuming more memory, it also breaks the FRP -- abstraction by making samples discrete and step based. evalAt :: SF Identity a b -> DTime -> a -> (b, SF Identity a b) -- | Given a signal function and time delta, it moves the signal function -- into the future, returning a new uninitialized SF and the initial -- output. -- -- While the input sample refers to the present, the time delta refers to -- the future (or to the time between the current sample and the next -- sample). -- -- WARN: Do not use this function for standard simulation. This -- function is intended only for debugging/testing. Apart from being -- potentially slower and consuming more memory, it also breaks the FRP -- abstraction by making samples discrete and step based. evalFuture :: SF Identity a b -> a -> DTime -> (b, SF Identity a b) replaceOnce :: Monad m => a -> SF m a a dup :: b -> (b, b) -- | Signal function (conceptually, a function between signals that -- respects causality). type SF = SF Identity -- | Future signal function (conceptually, a function between fugure -- signals that respects causality). -- -- A future signal is a signal that is only defined for positive times. type FutureSF = SF Identity