Yampa-0.12: src/FRP/Yampa.hs
{-# LANGUAGE GADTs, Rank2Types, CPP #-}
-----------------------------------------------------------------------------------------
-- |
-- Module : FRP.Yampa
-- Copyright : (c) Antony Courtney and Henrik Nilsson, Yale University, 2003
-- License : BSD-style (see the LICENSE file in the distribution)
--
-- Maintainer : ivan.perez@keera.co.uk
-- Stability : provisional
-- Portability : non-portable (GHC extensions)
--
--
-- Domain-specific language embedded in Haskell for programming hybrid (mixed
-- discrete-time and continuous-time) systems. Yampa is based on the concepts
-- of Functional Reactive Programming (FRP) and is structured using arrow
-- combinators.
--
-- You can find examples, screenshots, tutorials and documentation here:
--
-- <https://github.com/ivanperez-keera/Yampa>
--
-- <https://github.com/ivanperez-keera/Yampa/tree/master/examples>
--
-- <https://wiki.haskell.org/Yampa>
--
--
-- Structuring a hybrid system in Yampa is done based on two main concepts:
--
-- * Signal Functions: 'SF'. Yampa is based on the concept of Signal Functions,
-- which are functions from a typed input signal to a typed output signal.
-- Conceptually, signals are functions from Time to Value, where time are the
-- real numbers and, computationally, a very dense approximation (Double) is
-- used.
--
-- * Events: 'Event'. Values that may or may not occur (and would probably
-- occur rarely). It is often used for incoming network messages, mouse
-- clicks, etc. Events are used as values carried by signals.
--
-- A complete Yampa system is defined as one Signal Function from some
-- type @a@ to a type @b@. The execution of this signal transformer
-- with specific input can be accomplished by means of two functions:
-- 'reactimate' (which needs an initialization action,
-- an input sensing action and an actuation/consumer action and executes
-- until explicitly stopped), and 'react' (which executes only one cycle).
--
--
-- Main Yampa modules:
--
-- * "FRP.Yampa" -- This exports all FRP-related functions
--
-- * "FRP.Yampa.Task"
--
-- Minimal Complete FRP Definition:
--
-- * "FRP.Yampa.Core"
--
-- Different FRP aspects:
--
-- * "FRP.Yampa.Basic"
--
-- * "FRP.Yampa.Conditional"
--
-- * "FRP.Yampa.Delays"
--
-- * "FRP.Yampa.Event"
--
-- * "FRP.Yampa.EventS" -- Event consuming/producing SFs. To be renamed.
--
-- * "FRP.Yampa.Hybrid" -- Hybrid (discrete/continuous) SFs
--
-- * "FRP.Yampa.Integration"
--
-- * "FRP.Yampa.Loop"
--
-- * "FRP.Yampa.Random"
--
-- * "FRP.Yampa.Scan"
--
-- * "FRP.Yampa.Switches"
--
-- * "FRP.Yampa.Time"
--
-- * "FRP.Yampa.Simulation" -- Reactimation/evaluation
--
-- Internals:
--
-- * "FRP.Yampa.InternalCore" -- Module not exposed.
--
-- Geometry:
--
-- * "FRP.Yampa.Geometry"
--
-- * "FRP.Yampa.AffineSpace"
--
-- * "FRP.Yampa.VectorSpace"
--
-- * "FRP.Yampa.Point2"
--
-- * "FRP.Yampa.Point3"
--
-- * "FRP.Yampa.Vector2"
--
-- * "FRP.Yampa.Vector3"
--
-- Old legacy code:
--
-- * "FRP.Yampa.Diagnostics"
--
-- * "FRP.Yampa.Forceable"
--
-- * "FRP.Yampa.Internals" -- No longer in use
--
-- * "FRP.Yampa.MergeableRecord"
--
-- * "FRP.Yampa.Miscellany"
--
-- * "FRP.Yampa.Utilities"
--
-- This will be the last version of Yampa to include mergeable records, point2
-- and point3, vector2 and vector3, and other auxiliary definitions. The
-- internals have now changed. Also, please let us know if you see any problems
-- with the new project structure.
-- ToDo:
--
-- - Specialize def. of repeatedly. Could have an impact on invaders.
--
-- - New defs for accs using SFAcc
--
-- - Make sure opt worked: e.g.
--
-- - > repeatedly >>> count >>> arr (fmap sqr)
--
-- - Introduce SFAccHld.
--
-- - See if possible to unify AccHld wity Acc??? They are so close.
--
-- - Introduce SScan. BUT KEEP IN MIND: Most if not all opts would
-- - have been possible without GADTs???
--
-- - Look into pairs. At least pairing of SScan ought to be interesting.
--
-- - Would be nice if we could get rid of first & second with impunity
-- - thanks to Id optimizations. That's a clear win, with or without
-- - an explicit pair combinator.
--
-- - delayEventCat is a bit complicated ...
--
--
-- Random ideas:
--
-- - What if one used rules to optimize
-- - (arr :: SF a ()) to (constant ())
-- - (arr :: SF a a) to identity
-- But inspection of invader source code seem to indicate that
-- these are not very common cases at all.
--
-- - It would be nice if it was possible to come up with opt. rules
-- that are invariant of how signal function expressions are
-- parenthesized. Right now, we have e.g.
-- arr f >>> (constant c >>> sf)
-- being optimized to
-- cpAuxA1 f (cpAuxC1 c sf)
-- whereas it clearly should be possible to optimize to just
-- cpAuxC1 c sf
-- What if we didn't use SF' but
-- SFComp :: <tfun> -> SF' a b -> SF' b c -> SF' a c
-- ???
--
-- - The transition function would still be optimized in (pretty much)
-- the current way, but it would still be possible to look "inside"
-- composed signal functions for lost optimization opts.
-- Seems to me this could be done without too much extra effort/no dupl.
-- work.
-- E.g. new cpAux, the general case:
--
-- @
-- cpAux sf1 sf2 = SFComp tf sf1 sf2
-- where
-- tf dt a = (cpAux sf1' sf2', c)
-- where
-- (sf1', b) = (sfTF' sf1) dt a
-- (sf2', c) = (sfTF' sf2) dt b
-- @
--
-- - The ONLY change was changing the constructor from SF' to SFComp and
-- adding sf1 and sf2 to the constructor app.!
--
-- - An optimized case:
-- cpAuxC1 b sf1 sf2 = SFComp tf sf1 sf2
-- So cpAuxC1 gets an extra arg, and we change the constructor.
-- But how to exploit without writing 1000s of rules???
-- Maybe define predicates on SFComp to see if the first or second
-- sf are "interesting", and if so, make "reassociate" and make a
-- recursive call? E.g. we're in the arr case, and the first sf is another
-- arr, so we'd like to combine the two.
--
-- - It would also be intersting, then, to know when to STOP playing this
-- game, due to the overhead involved.
--
-- - Why don't we have a "SWITCH" constructor that indicates that the
-- structure will change, and thus that it is worthwile to keep
-- looking for opt. opportunities, whereas a plain "SF'" would
-- indicate that things NEVER are going to change, and thus we can just
-- as well give up?
-----------------------------------------------------------------------------------------
module FRP.Yampa (
-- Re-exported module, classes, and types
module Control.Arrow,
module FRP.Yampa.VectorSpace,
RandomGen(..),
Random(..),
-- * Basic definitions
Time, -- [s] Both for time w.r.t. some reference and intervals.
DTime, -- [s] Sampling interval, always > 0.
SF, -- Signal Function.
Event(..), -- Events; conceptually similar to Maybe (but abstract).
-- Temporary!
-- SF(..), sfTF',
-- Main instances
-- SF is an instance of Arrow and ArrowLoop. Method instances:
-- arr :: (a -> b) -> SF a b
-- (>>>) :: SF a b -> SF b c -> SF a c
-- (<<<) :: SF b c -> SF a b -> SF a c
-- first :: SF a b -> SF (a,c) (b,c)
-- second :: SF a b -> SF (c,a) (c,b)
-- (***) :: SF a b -> SF a' b' -> SF (a,a') (b,b')
-- (&&&) :: SF a b -> SF a b' -> SF a (b,b')
-- returnA :: SF a a
-- loop :: SF (a,c) (b,c) -> SF a b
-- Event is an instance of Functor, Eq, and Ord. Some method instances:
-- fmap :: (a -> b) -> Event a -> Event b
-- (==) :: Event a -> Event a -> Bool
-- (<=) :: Event a -> Event a -> Bool
-- ** Lifting
arrPrim, arrEPrim, -- For optimization
-- * Signal functions
-- ** Basic signal functions
identity, -- :: SF a a
constant, -- :: b -> SF a b
localTime, -- :: SF a Time
time, -- :: SF a Time, Other name for localTime.
-- ** Initialization
(-->), -- :: b -> SF a b -> SF a b, infixr 0
(-:>), -- :: b -> SF a b -> SF a b, infixr 0
(>--), -- :: a -> SF a b -> SF a b, infixr 0
(-=>), -- :: (b -> b) -> SF a b -> SF a b infixr 0
(>=-), -- :: (a -> a) -> SF a b -> SF a b infixr 0
initially, -- :: a -> SF a a
-- ** Simple, stateful signal processing
sscan, -- :: (b -> a -> b) -> b -> SF a b
sscanPrim, -- :: (c -> a -> Maybe (c, b)) -> c -> b -> SF a b
-- * Events
-- ** Basic event sources
never, -- :: SF a (Event b)
now, -- :: b -> SF a (Event b)
after, -- :: Time -> b -> SF a (Event b)
repeatedly, -- :: Time -> b -> SF a (Event b)
afterEach, -- :: [(Time,b)] -> SF a (Event b)
afterEachCat, -- :: [(Time,b)] -> SF a (Event [b])
delayEvent, -- :: Time -> SF (Event a) (Event a)
delayEventCat, -- :: Time -> SF (Event a) (Event [a])
edge, -- :: SF Bool (Event ())
iEdge, -- :: Bool -> SF Bool (Event ())
edgeTag, -- :: a -> SF Bool (Event a)
edgeJust, -- :: SF (Maybe a) (Event a)
edgeBy, -- :: (a -> a -> Maybe b) -> a -> SF a (Event b)
maybeToEvent, -- :: Maybe a -> Event a
-- ** Stateful event suppression
notYet, -- :: SF (Event a) (Event a)
once, -- :: SF (Event a) (Event a)
takeEvents, -- :: Int -> SF (Event a) (Event a)
dropEvents, -- :: Int -> SF (Event a) (Event a)
-- ** Pointwise functions on events
noEvent, -- :: Event a
noEventFst, -- :: (Event a, b) -> (Event c, b)
noEventSnd, -- :: (a, Event b) -> (a, Event c)
event, -- :: a -> (b -> a) -> Event b -> a
fromEvent, -- :: Event a -> a
isEvent, -- :: Event a -> Bool
isNoEvent, -- :: Event a -> Bool
tag, -- :: Event a -> b -> Event b, infixl 8
tagWith, -- :: b -> Event a -> Event b,
attach, -- :: Event a -> b -> Event (a, b), infixl 8
lMerge, -- :: Event a -> Event a -> Event a, infixl 6
rMerge, -- :: Event a -> Event a -> Event a, infixl 6
merge, -- :: Event a -> Event a -> Event a, infixl 6
mergeBy, -- :: (a -> a -> a) -> Event a -> Event a -> Event a
mapMerge, -- :: (a -> c) -> (b -> c) -> (a -> b -> c)
-- -> Event a -> Event b -> Event c
mergeEvents, -- :: [Event a] -> Event a
catEvents, -- :: [Event a] -> Event [a]
joinE, -- :: Event a -> Event b -> Event (a,b),infixl 7
splitE, -- :: Event (a,b) -> (Event a, Event b)
filterE, -- :: (a -> Bool) -> Event a -> Event a
mapFilterE, -- :: (a -> Maybe b) -> Event a -> Event b
gate, -- :: Event a -> Bool -> Event a, infixl 8
-- * Switching
-- ** Basic switchers
switch, dSwitch, -- :: SF a (b, Event c) -> (c -> SF a b) -> SF a b
rSwitch, drSwitch, -- :: SF a b -> SF (a,Event (SF a b)) b
kSwitch, dkSwitch, -- :: SF a b
-- -> SF (a,b) (Event c)
-- -> (SF a b -> c -> SF a b)
-- -> SF a b
-- ** Parallel composition and switching
-- *** Parallel composition and switching over collections with broadcasting
parB, -- :: Functor col => col (SF a b) -> SF a (col b)
pSwitchB,dpSwitchB, -- :: Functor col =>
-- col (SF a b)
-- -> SF (a, col b) (Event c)
-- -> (col (SF a b) -> c -> SF a (col b))
-- -> SF a (col b)
rpSwitchB,drpSwitchB, -- :: Functor col =>
-- col (SF a b)
-- -> SF (a, Event (col (SF a b)->col (SF a b)))
-- (col b)
-- *** Parallel composition and switching over collections with general routing
par, -- Functor col =>
-- (forall sf . (a -> col sf -> col (b, sf)))
-- -> col (SF b c)
-- -> SF a (col c)
pSwitch, dpSwitch, -- pSwitch :: Functor col =>
-- (forall sf . (a -> col sf -> col (b, sf)))
-- -> col (SF b c)
-- -> SF (a, col c) (Event d)
-- -> (col (SF b c) -> d -> SF a (col c))
-- -> SF a (col c)
rpSwitch,drpSwitch, -- Functor col =>
-- (forall sf . (a -> col sf -> col (b, sf)))
-- -> col (SF b c)
-- -> SF (a, Event (col (SF b c) -> col (SF b c)))
-- (col c)
--
-- * Discrete to continuous-time signal functions
-- ** Wave-form generation
hold, -- :: a -> SF (Event a) a
dHold, -- :: a -> SF (Event a) a
trackAndHold, -- :: a -> SF (Maybe a) a
-- ** Accumulators
accum, -- :: a -> SF (Event (a -> a)) (Event a)
accumHold, -- :: a -> SF (Event (a -> a)) a
dAccumHold, -- :: a -> SF (Event (a -> a)) a
accumBy, -- :: (b -> a -> b) -> b -> SF (Event a) (Event b)
accumHoldBy, -- :: (b -> a -> b) -> b -> SF (Event a) b
dAccumHoldBy, -- :: (b -> a -> b) -> b -> SF (Event a) b
accumFilter, -- :: (c -> a -> (c, Maybe b)) -> c
-- -> SF (Event a) (Event b)
-- * Delays
-- ** Basic delays
pre, -- :: SF a a
iPre, -- :: a -> SF a a
-- ** Timed delays
delay, -- :: Time -> a -> SF a a
-- ** Variable delay
pause, -- :: b -> SF a b -> SF a Bool -> SF a b
-- * State keeping combinators
-- ** Loops with guaranteed well-defined feedback
loopPre, -- :: c -> SF (a,c) (b,c) -> SF a b
loopIntegral, -- :: VectorSpace c s => SF (a,c) (b,c) -> SF a b
-- ** Integration and differentiation
integral, -- :: VectorSpace a s => SF a a
imIntegral, -- :: VectorSpace a s => a -> SF a a
impulseIntegral, -- :: VectorSpace a k => SF (a, Event a) a
count, -- :: Integral b => SF (Event a) (Event b)
derivative, -- :: VectorSpace a s => SF a a -- Crude!
-- Temporarily hidden, but will eventually be made public.
iterFrom, -- :: (a -> a -> DTime -> b -> b) -> b -> SF a b
-- * Noise (random signal) sources and stochastic event sources
noise, -- :: noise :: (RandomGen g, Random b) =>
-- g -> SF a b
noiseR, -- :: noise :: (RandomGen g, Random b) =>
-- (b,b) -> g -> SF a b
occasionally, -- :: RandomGen g => g -> Time -> b -> SF a (Event b)
-- * Execution/simulation
-- ** Reactimation
reactimate, -- :: IO a
-- -> (Bool -> IO (DTime, Maybe a))
-- -> (Bool -> b -> IO Bool)
-- -> SF a b
-- -> IO ()
ReactHandle,
reactInit, -- IO a -- init
-- -> (ReactHandle a b -> Bool -> b -> IO Bool) -- actuate
-- -> SF a b
-- -> IO (ReactHandle a b)
-- process a single input sample:
react, -- ReactHandle a b
-- -> (DTime,Maybe a)
-- -> IO Bool
-- ** Embedding
-- (tentative: will be revisited)
embed, -- :: SF a b -> (a, [(DTime, Maybe a)]) -> [b]
embedSynch, -- :: SF a b -> (a, [(DTime, Maybe a)]) -> SF Double b
deltaEncode, -- :: Eq a => DTime -> [a] -> (a, [(DTime, Maybe a)])
deltaEncodeBy, -- :: (a -> a -> Bool) -> DTime -> [a]
-- -> (a, [(DTime, Maybe a)])
FutureSF,
evalAtZero,
evalAt,
evalFuture,
-- * Auxiliary definitions
-- Reverse function composition and arrow plumbing aids
( # ), -- :: (a -> b) -> (b -> c) -> (a -> c), infixl 9
dup, -- :: a -> (a,a)
) where
import Control.Arrow
import FRP.Yampa.InternalCore
import FRP.Yampa.Basic
import FRP.Yampa.Conditional
import FRP.Yampa.Delays
import FRP.Yampa.Event
import FRP.Yampa.EventS
import FRP.Yampa.Hybrid
import FRP.Yampa.Integration
import FRP.Yampa.Loop
import FRP.Yampa.Miscellany (( # ), dup)
import FRP.Yampa.Random
import FRP.Yampa.Scan
import FRP.Yampa.Simulation
import FRP.Yampa.Switches
import FRP.Yampa.Time
import FRP.Yampa.VectorSpace
-- Vim modeline
-- vim:set tabstop=8 expandtab: