netwire 4.0.7 → 5.0.0
raw patch · 43 files changed
+2756/−3380 lines, 43 filesdep +netwiredep +paralleldep +transformersdep −bifunctorsdep −lifted-basedep −monad-controldep ~basedep ~containersdep ~semigroupssetup-changednew-component:exe:netwire-test
Dependencies added: netwire, parallel, transformers
Dependencies removed: bifunctors, lifted-base, monad-control, mtl, profunctors, tagged, vector-space
Dependency ranges changed: base, containers, semigroups
Files
- Control/Wire.hs +29/−327
- Control/Wire/Classes.hs +0/−46
- Control/Wire/Core.hs +421/−0
- Control/Wire/Event.hs +338/−0
- Control/Wire/Interval.hs +184/−0
- Control/Wire/Prefab.hs +0/−35
- Control/Wire/Prefab/Accum.hs +0/−132
- Control/Wire/Prefab/Analyze.hs +0/−243
- Control/Wire/Prefab/Effect.hs +0/−117
- Control/Wire/Prefab/Event.hs +0/−307
- Control/Wire/Prefab/List.hs +0/−36
- Control/Wire/Prefab/Move.hs +0/−247
- Control/Wire/Prefab/Noise.hs +0/−96
- Control/Wire/Prefab/Queue.hs +0/−88
- Control/Wire/Prefab/Sample.hs +0/−85
- Control/Wire/Prefab/Simple.hs +0/−67
- Control/Wire/Prefab/Time.hs +0/−45
- Control/Wire/Run.hs +63/−0
- Control/Wire/Session.hs +71/−199
- Control/Wire/Switch.hs +250/−0
- Control/Wire/Time.hs +38/−0
- Control/Wire/TimedMap.hs +0/−118
- Control/Wire/Trans.hs +0/−25
- Control/Wire/Trans/Combine.hs +0/−110
- Control/Wire/Trans/Embed.hs +0/−47
- Control/Wire/Trans/Event.hs +0/−211
- Control/Wire/Trans/Simple.hs +0/−49
- Control/Wire/Trans/Switch.hs +0/−101
- Control/Wire/Trans/Time.hs +0/−31
- Control/Wire/Types.hs +0/−161
- Control/Wire/Unsafe/Event.hs +78/−0
- Control/Wire/Wire.hs +0/−376
- FRP/Netwire.hs +46/−0
- FRP/Netwire/Analyze.hs +310/−0
- FRP/Netwire/Move.hs +77/−0
- FRP/Netwire/Noise.hs +98/−0
- FRP/Netwire/Utils/Timeline.hs +176/−0
- LICENSE +2/−2
- README.md +437/−0
- Setup.lhs +2/−2
- default.nix +30/−0
- netwire.cabal +86/−77
- test/Test.hs +20/−0
Control/Wire.hs view
@@ -1,351 +1,53 @@ -- | -- Module: Control.Wire--- Copyright: (c) 2012 Ertugrul Soeylemez+-- Copyright: (c) 2013 Ertugrul Soeylemez -- License: BSD3 -- Maintainer: Ertugrul Soeylemez <es@ertes.de>------ Netwire is a library for functional reactive programming, that is for--- time-varying values. It allows you to express various reactive--- systems elegantly and concisely by using an embedded domain-specific--- language. Examples of such systems include------ * games,------ * network applications with time-varying components,------ * simulations,------ * stateful web applications,------ * widget-based user interfaces.------ This library is based on an extension of the automaton arrow. The--- usage is explained in the following tutorial. module Control.Wire- ( -- * Quickstart tutorial- -- $quickstart_intro-- -- ** Running wires- -- $quickstart_running-- -- ** Constructing wires- -- $quickstart_constructing-- -- ** Signal inhibition and events- -- $quickstart_inhibition-- -- ** Custom wires- -- $quickstart_custom-- -- * Netwire reexports- module Control.Wire.Classes,- module Control.Wire.Prefab,+ ( -- * Reexports+ module Control.Wire.Core,+ module Control.Wire.Event,+ module Control.Wire.Interval,+ module Control.Wire.Run, module Control.Wire.Session,- module Control.Wire.Trans,- module Control.Wire.Types,- module Control.Wire.Wire,+ module Control.Wire.Switch,+ module Control.Wire.Time, - -- * Other reexports+ -- * Convenient type aliases+ WireP,+ SimpleWire,++ -- * External module Control.Applicative, module Control.Arrow, module Control.Category,- module Data.Proxy,- module System.Random,- Profunctor(..),- Exception(..),- SomeException(..)+ module Data.Semigroup,+ Identity(..),+ NominalDiffTime ) where import Control.Applicative import Control.Arrow import Control.Category-import Control.Exception (Exception(..), SomeException(..))-import Control.Wire.Classes-import Control.Wire.Prefab+import Control.Wire.Core+import Control.Wire.Event+import Control.Wire.Interval+import Control.Wire.Run import Control.Wire.Session-import Control.Wire.Trans-import Control.Wire.Types-import Control.Wire.Wire hiding (constant, identity, never)-import Data.Profunctor-import Data.Proxy (Proxy(..))-import System.Random---{- $quickstart_intro--This section is a quickstart tutorial for the experienced, impatient-Haskell programmer.--The main concept used in Netwire is a family of /wire categories/:--> data Wire e m a b--A value of type @Wire e m a b@ represents a function that takes as-arguments--* a time delta of type 'Time' (which is just 'Double') that will be- explained below,--* an input value of type @a@.--From these inputs it--* either produces an output value of type @b@ or /inhibits/ with a value- of type @e@,--* produces a new wire of type @Wire e m a b@.--So you can think of 'Wire' as:--> newtype Wire e m a b =-> Wire {-> stepWire :: Time -> a -> m (Either e b, Wire e m a b)-> }--To summarize a wire of type @Wire e m a b@ takes a value of type @a@ and-supposedly produces a value of type @b@. It can be invoked multiple-times, where each invocation is called an /instant/ and the wire can-behave differently at every instant. Additionally it can choose not to-produce anything, but instead inhibit with an /inhibition exception/ of-type @e@. This is Netwire's notion of a time-varying value. -}---{- $quickstart_running--To actually invoke a wire you can use the 'stepWire' function-(simplified type):--> stepWire ::-> (Monad m) =>-> Wire e m a b ->-> Time ->-> a ->-> m (Either e b, Wire e m a b)--The idea is simple: You have an application loop that invokes a given-wire with a time delta, which is just the number of seconds passed since-the last instant and an application-specific input value. It then does-something with the output value (or inhibition value) and restarts the-loop with the new wire produced by the current wire. Such an-application loop based on @stepWire@ could look like this:--> loop w' = do-> dt <- timeDeltaToLastInstant-> (mx, w) <- stepWire w' dt ()-> case mx of-> Left ex -> printf "Inhibited: %s\n" (show ex)-> Right x -> printf "Produced: %s\n" (show x)-> loop w--Usually the time deltas are based on actual clock time. To simplify-invocation for this common case there is a set of convenience functions-like 'stepSession' for stepping that calculate the time deltas for you:--> stepSession ::-> (Monad m) =>-> Wire e m a b ->-> Session m ->-> a ->-> m (Either e b, Wire e m a b, Session m)--To construct the initial session value you can use 'clockSession' or one-of the other predefined intial session values:--> clockSession :: (MonadIO m) => Session m--This simplifies the application loop, because you don't have to-calculate the time deltas yourself:--> loop w' session' = do-> (mx, w, session) <- stepSession w' session' ()-> case mx of-> Left ex -> printf "Inhibited: %s\n" (show ex)-> Right x -> printf "Produced: %s\n" (show x)-> loop w session--For the common case where the wire's underlying monad is 'Identity', but-the application monad is something else, there are convenience functions-like 'stepWireP', 'stepSessionP' and other @*P@ variants.--We haven't covered constructing wires yet. This is explained in the-next section. But we now have everything necessary to write our first-small application:--> module Main where->-> import Control.Monad.Identity (Identity)-> import Control.Wire-> import Prelude hiding ((.), id)-> import Text.Printf->-> testApp :: Wire () Identity a Time-> testApp = timeFrom 10->-> main :: IO ()-> main = loop testApp clockSession-> where-> loop w' session' = do-> (mx, w, session) <- stepSessionP w' session' ()-> case mx of-> Left ex -> putStrLn ("Inhibited: " ++ show ex)-> Right x -> putStrLn ("Produced: " ++ show x)-> loop w session--When you run this program, it will continuously display a number of-seconds starting with 10. That's the @timeFrom 10@ wire. Notice that-the "Prelude" module is imported with hidden 'Prelude..' and-'Prelude.id'. Don't worry, the "Control.Wire" module reexports the-"Control.Category" module, which includes generalized version of both.--}---{- $quickstart_constructing--A number of convenience types are defined in the "Control.Wire.Types"-module, in particular the 'WireP' type:--> type WireP = Wire LastException Identity--Wires can be composed categorically, applicatively or by using wire-combinators. To feed the output of one wire @w1@ into another wire @w2@-you just use categorical composition:--> w2 . w1--For example the 'noise' wire generates random noise based on the given-random number generator. If its output type is 'Double', it generates-noise 0 <= x t < 1. The 'avg' wire calculates the average value of its-input over the last given number of samples:--> let myNoise = noise (mkStdGen 0) :: WireP a Double-> myAvg = avg 1000-> in myAvg . myNoise--That wire should produce values near 0.5, the average noise value over-the last 1000 samples of random noise between 0 and 1. There is a bit-of cruft here to tell the type system that noise's output type is-'Double'. To make this easier you can simply use 'outAs' or 'inAs':--> avg 1000 . outAs pDouble (noise (mkStdGen 0))--The 'Wire' type gives rise to a family of applicative functors. Using-applicative style you can apply a function to the output of a wire or-zip together the outputs of two wires (the "Control.Applicative" module-is reexported by this module):--> timeString = fmap (printf "%8.2f") time->-> noisyTime = liftA2 (+) time (noise (mkStdGen 0))--Constant wires can be produced using 'pure'. The following wire starts-at 0 and increases with a constant speed of 3:--> integral_ 0 . pure 3--There are lots of convenience instances for wires. For example there-are instances for 'Num', 'Fractional' and 'Data.String.IsString', so you-can actually just use regular arithmetical operators and numeric-literals. If you have enabled the @OverloadedStrings@ extension you can-also write string literals:--> let n = noise (mkStdGen 0)-> in time + 3*n->-> integral_ 0 . 3--There is a large library of predefined wires below the-"Control.Wire.Prefab" tree.--}+import Control.Wire.Switch+import Control.Wire.Time+import Data.Functor.Identity+import Data.Semigroup+import Data.Time.Clock -{- $quickstart_inhibition--As noted a few times wires can choose not to produce a value. In those-cases the wire /inhibits/ the signal. This is where the @e@ type comes-into play. That type is called the /inhibition monoid/.--Signal inhibition is what makes Netwire different. The 'Wire' type is-an 'Alternative' functor, where the 'empty' wire always inhibits and-wires can be combined with the following semantics:--> w1 <|> w2--If @w1@ inhibits, then the combination @w1 \<|\> w2@ acts like @w2@. In-other words, the combination chooses the first wire that produces. If-both inhibit, then the combination inhibits.--Events are modelled around this. An event wire is usually a wire that-acts like the identity wire, but it may inhibit depending on whether an-event has occurred or not. One simple event wire is the 'for' wire:--> for 3--This wire acts like the identity wire for three seconds and then stops-producing forever. You can use it to construct a wire that produces-"yes" for three seconds and then switches to "no":--> "yes" . for 3 <|> "no"--Another useful event wire is the 'wackelkontakt' wire (a Netwire running-gag; it's the German word for slack joint):--> wackelkontakt 0.9--This wire acts like the identity wire most of the time (90%), but-occasionally inhibits (10%). Using it you can produce a broken clock,-which occasionally refuses to display the current time:--> brokenClock =-> printf "%8.2f" <$> wackelkontakt 0.9 . time <|>-> "sorry, slack joint"--The 'periodically' wire produces once every given number of seconds.-The following wire produces once every two seconds:--> periodically 2--There are various combinators for event wires in the-"Control.Wire.Trans.Event" module, most notably 'hold' and 'holdFor'.-Given an inhibiting wire the @hold@ combinator holds the last produced-value, so it turns instantaneous events into continuous ones:--> secondClock = printf "%8.2f" <$> hold (periodically 1 . time)--This one displays the time in seconds and is only updated every second.-The 'holdFor' combinator allows you to limit the time the last output is-held for:--> jumpyClock =-> printf "%8.2f" <$> holdFor 0.5 (periodically 1 . time) <|>-> "wait 500ms for the next second"+-- | Pure wires. -You find a library of predefined event wires in the-"Control.Wire.Prefab.Event" module.--}+type WireP s e = Wire s e Identity -{- $quickstart_custom--From time to time you will want to write your own wire on a lower level.-In this case there are a number of options. The simplest option is-'mkPure':--> mkPure ::-> (Time -> a -> (Either e b, Wire e m a b)) ->-> Wire e m a b--The type quite literally tells what this function does. It takes a-function and turns it into a wire quite straightforwardly. Another-option is to use 'mkState', which is equivalent to @mkPure@, but allows-you to express the wire as a local state transformer:--> mkState ::-> s ->-> (Time -> (a, s) -> (Either e b, s)) ->-> Wire e m a b+-- | Simple wires with time. -The first argument is a starting state, the second is the state-transformation function.--}+type SimpleWire = Wire (Timed NominalDiffTime ()) () Identity
− Control/Wire/Classes.hs
@@ -1,46 +0,0 @@--- |--- Module: Control.Wire.Classes--- Copyright: (c) 2012 Ertugrul Soeylemez--- License: BSD3--- Maintainer: Ertugrul Soeylemez <es@ertes.de>------ Various type classes.--module Control.Wire.Classes- ( -- * Effects- MonadRandom(..),-- -- * Utility classes- Injectable(..)- )- where--import Data.Monoid-import System.Random----- | Class for injectable values. See--- 'Control.Wire.Prefab.Event.inject'.--class Injectable e f where- toSignal :: f a -> Either e a--instance (Monoid e) => Injectable e Maybe where- toSignal = maybe (Left mempty) Right--instance Injectable e (Either e) where- toSignal = id----- | Monads with a random number generator.--class (Monad m) => MonadRandom m where- -- | Get a random number.- getRandom :: (Random a) => m a-- -- | Get a random number in the given range.- getRandomR :: (Random a) => (a, a) -> m a--instance MonadRandom IO where- getRandom = randomIO- getRandomR = randomRIO
+ Control/Wire/Core.hs view
@@ -0,0 +1,421 @@+-- |+-- Module: Control.Wire.Core+-- Copyright: (c) 2013 Ertugrul Soeylemez+-- License: BSD3+-- Maintainer: Ertugrul Soeylemez <es@ertes.de>++module Control.Wire.Core+ ( -- * Wires+ Wire(..),+ stepWire,++ -- * Constructing wires+ mkConst,+ mkEmpty,+ mkGen,+ mkGen_,+ mkGenN,+ mkId,+ mkPure,+ mkPure_,+ mkPureN,+ mkSF,+ mkSF_,+ mkSFN,++ -- * Data flow and dependencies+ delay,+ evalWith,+ force,+ forceNF,++ -- * Utilities+ (&&&!),+ (***!),+ lstrict,+ mapWire+ )+ where++import qualified Data.Semigroup as Sg+import Control.Applicative+import Control.Arrow+import Control.Category+import Control.DeepSeq hiding (force)+import Control.Monad+import Control.Monad.Fix+import Control.Parallel.Strategies+import Data.Monoid+import Data.String+import Prelude hiding ((.), id)+++-- | A wire is a signal function. It maps a reactive value to another+-- reactive value.++data Wire s e m a b where+ WArr :: (Either e a -> Either e b) -> Wire s e m a b+ WConst :: Either e b -> Wire s e m a b+ WGen :: (s -> Either e a -> m (Either e b, Wire s e m a b)) -> Wire s e m a b+ WId :: Wire s e m a a+ WPure :: (s -> Either e a -> (Either e b, Wire s e m a b)) -> Wire s e m a b++instance (Monad m, Monoid e) => Alternative (Wire s e m a) where+ empty = WConst (Left mempty)++ w1' <|> w2' =+ WGen $ \ds mx' ->+ liftM2 (\(mx1, w1) (mx2, w2) -> lstrict (choose mx1 mx2, w1 <|> w2))+ (stepWire w1' ds mx')+ (stepWire w2' ds mx')++ where+ choose mx1@(Right _) _ = mx1+ choose _ mx2@(Right _) = mx2+ choose (Left ex1) (Left ex2) = Left (ex1 <> ex2)++instance (Monad m) => Applicative (Wire s e m a) where+ pure = WConst . Right++ wf' <*> wx' =+ WGen $ \ds mx' ->+ liftM2 (\(mf, wf) (mx, wx) -> lstrict (mf <*> mx, wf <*> wx))+ (stepWire wf' ds mx')+ (stepWire wx' ds mx')++instance (Monad m) => Arrow (Wire s e m) where+ arr f = WArr (fmap f)++ first w' =+ WGen $ \ds mxy' ->+ liftM (\(mx, w) -> lstrict (liftA2 (,) mx (fmap snd mxy'), first w))+ (stepWire w' ds (fmap fst mxy'))++instance (Monad m, Monoid e) => ArrowChoice (Wire s e m) where+ left w' =+ WGen $ \ds mmx' ->+ liftM (fmap Left ***! left) .+ stepWire w' ds $+ case mmx' of+ Right (Left x) -> Right x+ Right (Right _) -> Left mempty+ Left ex -> Left ex++ right w' =+ WGen $ \ds mmx' ->+ liftM (fmap Right ***! right) .+ stepWire w' ds $+ case mmx' of+ Right (Right x) -> Right x+ Right (Left _) -> Left mempty+ Left ex -> Left ex++ wl' +++ wr' =+ WGen $ \ds mmx' ->+ case mmx' of+ Right (Left x) -> do+ liftM2 (\(mx, wl) (_, wr) -> lstrict (fmap Left mx, wl +++ wr))+ (stepWire wl' ds (Right x))+ (stepWire wr' ds (Left mempty))+ Right (Right x) -> do+ liftM2 (\(_, wl) (mx, wr) -> lstrict (fmap Right mx, wl +++ wr))+ (stepWire wl' ds (Left mempty))+ (stepWire wr' ds (Right x))+ Left ex ->+ liftM2 (\(_, wl) (_, wr) -> lstrict (Left ex, wl +++ wr))+ (stepWire wl' ds (Left ex))+ (stepWire wr' ds (Left ex))++ wl' ||| wr' =+ WGen $ \ds mmx' ->+ case mmx' of+ Right (Left x) -> do+ liftM2 (\(mx, wl) (_, wr) -> lstrict (mx, wl ||| wr))+ (stepWire wl' ds (Right x))+ (stepWire wr' ds (Left mempty))+ Right (Right x) -> do+ liftM2 (\(_, wl) (mx, wr) -> lstrict (mx, wl ||| wr))+ (stepWire wl' ds (Left mempty))+ (stepWire wr' ds (Right x))+ Left ex ->+ liftM2 (\(_, wl) (_, wr) -> lstrict (Left ex, wl ||| wr))+ (stepWire wl' ds (Left ex))+ (stepWire wr' ds (Left ex))++instance (MonadFix m) => ArrowLoop (Wire s e m) where+ loop w' =+ WGen $ \ds mx' ->+ liftM (fmap fst ***! loop) .+ mfix $ \ ~(mx, _) ->+ let d | Right (_, d) <- mx = d+ | otherwise = error "Feedback broken by inhibition"+ in stepWire w' ds (fmap (, d) mx')++instance (Monad m, Monoid e) => ArrowPlus (Wire s e m) where+ (<+>) = (<|>)++instance (Monad m, Monoid e) => ArrowZero (Wire s e m) where+ zeroArrow = empty++instance (Monad m) => Category (Wire s e m) where+ id = WId++ w2' . w1' =+ WGen $ \ds mx0 -> do+ (mx1, w1) <- stepWire w1' ds mx0+ (mx2, w2) <- stepWire w2' ds mx1+ mx2 `seq` return (mx2, w2 . w1)++instance (Monad m, Floating b) => Floating (Wire s e m a b) where+ (**) = liftA2 (**)+ acos = fmap acos+ acosh = fmap acosh+ asin = fmap asin+ asinh = fmap asinh+ atan = fmap atan+ atanh = fmap atanh+ cos = fmap cos+ cosh = fmap cosh+ exp = fmap exp+ log = fmap log+ logBase = liftA2 logBase+ pi = pure pi+ sin = fmap sin+ sinh = fmap sinh+ sqrt = fmap sqrt+ tan = fmap tan+ tanh = fmap tanh++instance (Monad m, Fractional b) => Fractional (Wire s e m a b) where+ (/) = liftA2 (/)+ recip = fmap recip+ fromRational = pure . fromRational++instance (Monad m) => Functor (Wire s e m a) where+ fmap f (WArr g) = WArr (fmap f . g)+ fmap f (WConst mx) = WConst (fmap f mx)+ fmap f (WGen g) = WGen (\ds -> liftM (fmap f ***! fmap f) . g ds)+ fmap f WId = WArr (fmap f)+ fmap f (WPure g) = WPure (\ds -> (fmap f ***! fmap f) . g ds)++instance (Monad m, IsString b) => IsString (Wire s e m a b) where+ fromString = pure . fromString++instance (Monad m, Monoid b) => Monoid (Wire s e m a b) where+ mempty = pure mempty+ mappend = liftA2 mappend++instance (Monad m, Num b) => Num (Wire s e m a b) where+ (+) = liftA2 (+)+ (-) = liftA2 (-)+ (*) = liftA2 (*)+ abs = fmap abs+ negate = fmap negate+ signum = fmap signum+ fromInteger = pure . fromInteger++instance (Monad m, Sg.Semigroup b) => Sg.Semigroup (Wire s e m a b) where+ (<>) = liftA2 (Sg.<>)+++-- | Left-strict version of '&&&' for functions.++(&&&!) :: (a -> b) -> (a -> c) -> (a -> (b, c))+(&&&!) f g x' =+ let (x, y) = (f x', g x')+ in x `seq` (x, y)+++-- | Left-strict version of '***' for functions.++(***!) :: (a -> c) -> (b -> d) -> ((a, b) -> (c, d))+(***!) f g (x', y') =+ let (x, y) = (f x', g y')+ in x `seq` (x, y)+++-- | This wire delays its input signal by the smallest possible+-- (semantically infinitesimal) amount of time. You can use it when you+-- want to use feedback ('ArrowLoop'): If the user of the feedback+-- depends on /now/, delay the value before feeding it back. The+-- argument value is the replacement signal at the beginning.+--+-- * Depends: before now.++delay :: a -> Wire s e m a a+delay x' = mkSFN $ \x -> (x', delay x)+++-- | Evaluate the input signal using the given 'Strategy' here. This+-- wire evaluates only produced values.+--+-- * Depends: now.++evalWith :: Strategy a -> Wire s e m a a+evalWith s =+ WArr $ \mx ->+ case mx of+ Right x -> (x `using` s) `seq` mx+ Left _ -> mx+++-- | Force the input signal to WHNF here. This wire forces both+-- produced values and inhibition values.+--+-- * Depends: now.++force :: Wire s e m a a+force =+ WArr $ \mx ->+ case mx of+ Right x -> x `seq` mx+ Left ex -> ex `seq` mx+++-- | Force the input signal to NF here. This wire forces only produced+-- values.+--+-- * Depends: now.++forceNF :: (NFData a) => Wire s e m a a+forceNF =+ WArr $ \mx ->+ case mx of+ Right x -> x `deepseq` mx+ Left _ -> mx+++-- | Left-strict tuple.++lstrict :: (a, b) -> (a, b)+lstrict (x, y) = x `seq` (x, y)+++-- | Apply the given monad morphism to the wire's underlying monad.++mapWire ::+ (Monad m', Monad m)+ => (forall a. m' a -> m a)+ -> Wire s e m' a b+ -> Wire s e m a b+mapWire _ (WArr g) = WArr g+mapWire _ (WConst mx) = WConst mx+mapWire f (WGen g) = WGen (\ds -> liftM (lstrict . second (mapWire f)) . f . g ds)+mapWire _ WId = WId+mapWire f (WPure g) = WPure (\ds -> lstrict . second (mapWire f) . g ds)+++-- | Construct a stateless wire from the given signal mapping function.++mkConst :: Either e b -> Wire s e m a b+mkConst = WConst+++-- | Construct the empty wire, which inhibits forever.++mkEmpty :: (Monoid e) => Wire s e m a b+mkEmpty = mkConst (Left mempty)+++-- | Construct a stateful wire from the given transition function.++mkGen :: (Monad m, Monoid s) => (s -> a -> m (Either e b, Wire s e m a b)) -> Wire s e m a b+mkGen f = loop mempty+ where+ loop s' =+ WGen $ \ds mx ->+ let s = s' <> ds in+ s `seq`+ case mx of+ Left ex -> return (Left ex, loop s)+ Right x' -> liftM lstrict (f s x')+++-- | Construct a stateless wire from the given transition function.++mkGen_ :: (Monad m) => (a -> m (Either e b)) -> Wire s e m a b+mkGen_ f = loop+ where+ loop =+ WGen $ \_ mx ->+ case mx of+ Left ex -> return (Left ex, loop)+ Right x -> liftM (lstrict . (, loop)) (f x)+++-- | Construct a stateful wire from the given transition function.++mkGenN :: (Monad m) => (a -> m (Either e b, Wire s e m a b)) -> Wire s e m a b+mkGenN f = loop+ where+ loop =+ WGen $ \_ mx ->+ case mx of+ Left ex -> return (Left ex, loop)+ Right x' -> liftM lstrict (f x')+++-- | Construct the identity wire.++mkId :: Wire s e m a a+mkId = WId+++-- | Construct a pure stateful wire from the given transition function.++mkPure :: (Monoid s) => (s -> a -> (Either e b, Wire s e m a b)) -> Wire s e m a b+mkPure f = loop mempty+ where+ loop s' =+ WPure $ \ds mx ->+ let s = s' <> ds in+ s `seq`+ case mx of+ Left ex -> (Left ex, loop s)+ Right x' -> lstrict (f s x')+++-- | Construct a pure stateless wire from the given transition function.++mkPure_ :: (a -> Either e b) -> Wire s e m a b+mkPure_ f = WArr $ (>>= f)+++-- | Construct a pure stateful wire from the given transition function.++mkPureN :: (a -> (Either e b, Wire s e m a b)) -> Wire s e m a b+mkPureN f = loop+ where+ loop =+ WPure $ \_ mx ->+ case mx of+ Left ex -> (Left ex, loop)+ Right x' -> lstrict (f x')+++-- | Construct a pure stateful wire from the given signal function.++mkSF :: (Monoid s) => (s -> a -> (b, Wire s e m a b)) -> Wire s e m a b+mkSF f = mkPure (\ds -> lstrict . first (Right) . f ds)+++-- | Construct a pure stateless wire from the given function.++mkSF_ :: (a -> b) -> Wire s e m a b+mkSF_ f = WArr (fmap f)+++-- | Construct a pure stateful wire from the given signal function.++mkSFN :: (a -> (b, Wire s e m a b)) -> Wire s e m a b+mkSFN f = mkPureN (lstrict . first (Right) . f)+++-- | Perform one step of the given wire.++stepWire :: (Monad m) => Wire s e m a b -> s -> Either e a -> m (Either e b, Wire s e m a b)+stepWire w@(WArr f) _ mx' = return (f mx', w)+stepWire w@(WConst mx) _ mx' = return (mx' *> mx, w)+stepWire (WGen f) ds mx' = f ds mx'+stepWire w@WId _ mx' = return (mx', w)+stepWire (WPure f) ds mx' = return (f ds mx')
+ Control/Wire/Event.hs view
@@ -0,0 +1,338 @@+-- |+-- Module: Control.Wire.Event+-- Copyright: (c) 2013 Ertugrul Soeylemez+-- License: BSD3+-- Maintainer: Ertugrul Soeylemez <es@ertes.de>++module Control.Wire.Event+ ( -- * Events+ Event,++ -- * Time-based+ at,+ never,+ now,+ periodic,+ periodicList,++ -- * Signal analysis+ became,+ noLonger,++ -- * Modifiers+ (<&),+ (&>),+ dropE,+ dropWhileE,+ filterE,+ merge,+ mergeL,+ mergeR,+ notYet,+ once,+ takeE,+ takeWhileE,++ -- * Scans+ accumE,+ accum1E,+ iterateE,+ -- ** Special scans+ maximumE,+ minimumE,+ productE,+ sumE+ )+ where++import Control.Applicative+import Control.Arrow+import Control.Monad.Fix+import Control.Wire.Core+import Control.Wire.Session+import Control.Wire.Unsafe.Event+import Data.Fixed+++-- | Merge events with the leftmost event taking precedence. Equivalent+-- to using the monoid interface with 'First'. Infixl 5.+--+-- * Depends: now on both.+--+-- * Inhibits: when any of the two wires inhibit.++(<&) :: (Monad m) => Wire s e m a (Event b) -> Wire s e m a (Event b) -> Wire s e m a (Event b)+(<&) = liftA2 (merge const)++infixl 5 <&+++-- | Merge events with the rightmost event taking precedence.+-- Equivalent to using the monoid interface with 'Last'. Infixl 5.+--+-- * Depends: now on both.+--+-- * Inhibits: when any of the two wires inhibit.++(&>) :: (Monad m) => Wire s e m a (Event b) -> Wire s e m a (Event b) -> Wire s e m a (Event b)+(&>) = liftA2 (merge (const id))++infixl 5 &>+++-- | Left scan for events. Each time an event occurs, apply the given+-- function.+--+-- * Depends: now.++accumE ::+ (b -> a -> b) -- ^ Fold function+ -> b -- ^ Initial value.+ -> Wire s e m (Event a) (Event b)+accumE f = loop+ where+ loop x' =+ mkSFN $+ event (NoEvent, loop x')+ (\y -> let x = f x' y in (Event x, loop x))+++-- | Left scan for events with no initial value. Each time an event+-- occurs, apply the given function. The first event is produced+-- unchanged.+--+-- * Depends: now.++accum1E ::+ (a -> a -> a) -- ^ Fold function+ -> Wire s e m (Event a) (Event a)+accum1E f = initial+ where+ initial =+ mkSFN $ event (NoEvent, initial) (Event &&& accumE f)+++-- | At the given point in time.+--+-- * Depends: now when occurring.++at ::+ (HasTime t s)+ => t -- ^ Time of occurrence.+ -> Wire s e m a (Event a)+at t' =+ mkSF $ \ds x ->+ let t = t' - dtime ds+ in if t <= 0+ then (Event x, never)+ else (NoEvent, at t)+++-- | Occurs each time the predicate becomes true for the input signal,+-- for example each time a given threshold is reached.+--+-- * Depends: now.++became :: (a -> Bool) -> Wire s e m a (Event a)+became p = off+ where+ off = mkSFN $ \x -> if p x then (Event x, on) else (NoEvent, off)+ on = mkSFN $ \x -> (NoEvent, if p x then on else off)+++-- | Forget the first given number of occurrences.+--+-- * Depends: now.++dropE :: Int -> Wire s e m (Event a) (Event a)+dropE n | n <= 0 = mkId+dropE n =+ fix $ \again ->+ mkSFN $ \mev ->+ (NoEvent, if occurred mev then dropE (pred n) else again)+++-- | Forget all initial occurrences until the given predicate becomes+-- false.+--+-- * Depends: now.++dropWhileE :: (a -> Bool) -> Wire s e m (Event a) (Event a)+dropWhileE p =+ fix $ \again ->+ mkSFN $ \mev ->+ case mev of+ Event x | not (p x) -> (mev, mkId)+ _ -> (NoEvent, again)+++-- | Forget all occurrences for which the given predicate is false.+--+-- * Depends: now.++filterE :: (a -> Bool) -> Wire s e m (Event a) (Event a)+filterE p =+ mkSF_ $ \mev ->+ case mev of+ Event x | p x -> mev+ _ -> NoEvent+++-- | On each occurrence, apply the function the event carries.+--+-- * Depends: now.++iterateE :: a -> Wire s e m (Event (a -> a)) (Event a)+iterateE = accumE (\x f -> f x)+++-- | Maximum of all events.+--+-- * Depends: now.++maximumE :: (Ord a) => Wire s e m (Event a) (Event a)+maximumE = accum1E max+++-- | Minimum of all events.+--+-- * Depends: now.++minimumE :: (Ord a) => Wire s e m (Event a) (Event a)+minimumE = accum1E min+++-- | Left-biased event merge.++mergeL :: Event a -> Event a -> Event a+mergeL = merge const+++-- | Right-biased event merge.++mergeR :: Event a -> Event a -> Event a+mergeR = merge (const id)+++-- | Never occurs.++never :: Wire s e m a (Event b)+never = mkConst (Right NoEvent)+++-- | Occurs each time the predicate becomes false for the input signal,+-- for example each time a given threshold is no longer exceeded.+--+-- * Depends: now.++noLonger :: (a -> Bool) -> Wire s e m a (Event a)+noLonger p = off+ where+ off = mkSFN $ \x -> if p x then (NoEvent, off) else (Event x, on)+ on = mkSFN $ \x -> (NoEvent, if p x then off else on)+++-- | Forget the first occurrence.+--+-- * Depends: now.++notYet :: Wire s e m (Event a) (Event a)+notYet =+ mkSFN $ event (NoEvent, notYet) (const (NoEvent, mkId))+++-- | Occurs once immediately.+--+-- * Depends: now when occurring.++now :: Wire s e m a (Event a)+now = mkSFN $ \x -> (Event x, never)+++-- | Forget all occurrences except the first.+--+-- * Depends: now when occurring.++once :: Wire s e m (Event a) (Event a)+once =+ mkSFN $ \mev ->+ (mev, if occurred mev then never else once)+++-- | Periodic occurrence with the given time period. First occurrence+-- is now.+--+-- * Depends: now when occurring.++periodic :: (HasTime t s) => t -> Wire s e m a (Event a)+periodic int | int <= 0 = error "periodic: Non-positive interval"+periodic int = mkSFN $ \x -> (Event x, loop int)+ where+ loop 0 = loop int+ loop t' =+ mkSF $ \ds x ->+ let t = t' - dtime ds+ in if t <= 0+ then (Event x, loop (mod' t int))+ else (NoEvent, loop t)+++-- | Periodic occurrence with the given time period. First occurrence+-- is now. The event values are picked one by one from the given list.+-- When the list is exhausted, the event does not occur again.++periodicList :: (HasTime t s) => t -> [b] -> Wire s e m a (Event b)+periodicList int _ | int <= 0 = error "periodic: Non-positive interval"+periodicList _ [] = never+periodicList int (x:xs) = mkSFN $ \_ -> (Event x, loop int xs)+ where+ loop _ [] = never+ loop 0 xs = loop int xs+ loop t' xs0@(x:xs) =+ mkSF $ \ds _ ->+ let t = t' - dtime ds+ in if t <= 0+ then (Event x, loop (mod' t int) xs)+ else (NoEvent, loop t xs0)+++-- | Product of all events.+--+-- * Depends: now.++productE :: (Num a) => Wire s e m (Event a) (Event a)+productE = accumE (*) 1+++-- | Sum of all events.+--+-- * Depends: now.++sumE :: (Num a) => Wire s e m (Event a) (Event a)+sumE = accumE (+) 0+++-- | Forget all but the first given number of occurrences.+--+-- * Depends: now.++takeE :: Int -> Wire s e m (Event a) (Event a)+takeE n | n <= 0 = never+takeE n =+ fix $ \again ->+ mkSFN $ \mev ->+ (mev, if occurred mev then takeE (pred n) else again)+++-- | Forget all but the initial occurrences for which the given+-- predicate is true.+--+-- * Depends: now.++takeWhileE :: (a -> Bool) -> Wire s e m (Event a) (Event a)+takeWhileE p =+ fix $ \again ->+ mkSFN $ \mev ->+ case mev of+ Event x | not (p x) -> (NoEvent, never)+ _ -> (mev, again)
+ Control/Wire/Interval.hs view
@@ -0,0 +1,184 @@+-- |+-- Module: Control.Wire.Interval+-- Copyright: (c) 2013 Ertugrul Soeylemez+-- License: BSD3+-- Maintainer: Ertugrul Soeylemez <es@ertes.de>++module Control.Wire.Interval+ ( -- * Basic intervals+ inhibit,++ -- * Time intervals+ after,+ for,++ -- * Signal analysis+ unless,+ when,++ -- * Event-based intervals+ asSoonAs,+ between,+ hold,+ holdFor,+ until+ )+ where++import Control.Arrow+import Control.Wire.Core+import Control.Wire.Event+import Control.Wire.Session+import Control.Wire.Unsafe.Event+import Data.Monoid+import Prelude hiding (until)+++-- | After the given time period.+--+-- * Depends: now after the given time period.+--+-- * Inhibits: for the given time period.++after :: (HasTime t s, Monoid e) => t -> Wire s e m a a+after t' =+ mkPure $ \ds x ->+ let t = t' - dtime ds in+ if t <= 0+ then (Right x, mkId)+ else (Left mempty, after t)+++-- | Alias for 'hold'.++asSoonAs :: (Monoid e) => Wire s e m (Event a) a+asSoonAs = hold+++-- | Start each time the left event occurs, stop each time the right+-- event occurs.+--+-- * Depends: now when active.+--+-- * Inhibits: after the right event occurred, before the left event+-- occurs.++between :: (Monoid e) => Wire s e m (a, Event b, Event c) a+between =+ mkPureN $ \(x, onEv, _) ->+ event (Left mempty, between)+ (const (Right x, active))+ onEv++ where+ active =+ mkPureN $ \(x, _, offEv) ->+ event (Right x, active)+ (const (Left mempty, between))+ offEv+++-- | For the given time period.+--+-- * Depends: now for the given time period.+--+-- * Inhibits: after the given time period.++for :: (HasTime t s, Monoid e) => t -> Wire s e m a a+for t' =+ mkPure $ \ds x ->+ let t = t' - dtime ds in+ if t <= 0+ then (Left mempty, mkEmpty)+ else (Right x, for t)+++-- | Start when the event occurs for the first time reflecting its+-- latest value.+--+-- * Depends: now.+--+-- * Inhibits: until the event occurs for the first time.++hold :: (Monoid e) => Wire s e m (Event a) a+hold =+ mkPureN $+ event (Left mempty, hold)+ (Right &&& holdWith)++ where+ holdWith x =+ mkPureN $+ event (Right x, holdWith x)+ (Right &&& holdWith)+++-- | Hold each event occurrence for the given time period. Inhibits+-- when no event occurred for the given amount of time. New occurrences+-- override old occurrences, even when they are still held.+--+-- * Depends: now.+--+-- * Inhibits: when no event occurred for the given amount of time.++holdFor :: (HasTime t s, Monoid e) => t -> Wire s e m (Event a) a+holdFor int | int <= 0 = error "holdFor: Non-positive interval."+holdFor int = off+ where+ off =+ mkPure $ \_ ->+ event (Left mempty, off)+ (Right &&& on int)++ on t' x' =+ mkPure $ \ds ->+ let t = t' - dtime ds in+ event (if t <= 0+ then (Left mempty, off)+ else (Right x', on t x'))+ (Right &&& on int)+++-- | Inhibit forever with the given value.+--+-- * Inhibits: always.++inhibit :: e -> Wire s e m a b+inhibit = mkConst . Left+++-- | When the given predicate is false for the input signal.+--+-- * Depends: now.+--+-- * Inhibits: unless the predicate is false.++unless :: (Monoid e) => (a -> Bool) -> Wire s e m a a+unless p =+ mkPure_ $ \x ->+ if p x then Left mempty else Right x+++-- | Produce until the given event occurs. When it occurs, inhibit with+-- its value forever.+--+-- * Depends: now until event occurs.+--+-- * Inhibits: forever after event occurs.++until :: (Monoid e) => Wire s e m (a, Event b) a+until =+ mkPureN . uncurry $ \x ->+ event (Right x, until) (const (Left mempty, mkEmpty))+++-- | When the given predicate is true for the input signal.+--+-- * Depends: now.+--+-- * Inhibits: when the predicate is false.++when :: (Monoid e) => (a -> Bool) -> Wire s e m a a+when p =+ mkPure_ $ \x ->+ if p x then Right x else Left mempty
− Control/Wire/Prefab.hs
@@ -1,35 +0,0 @@--- |--- Module: Control.Wire.Prefab--- Copyright: (c) 2012 Ertugrul Soeylemez--- License: BSD3--- Maintainer: Ertugrul Soeylemez <es@ertes.de>------ Proxy module for the prefab wires.--module Control.Wire.Prefab- ( -- * Reexports- module Control.Wire.Prefab.Accum,- module Control.Wire.Prefab.Analyze,- module Control.Wire.Prefab.Effect,- module Control.Wire.Prefab.Event,- module Control.Wire.Prefab.List,- module Control.Wire.Prefab.Move,- module Control.Wire.Prefab.Noise,- module Control.Wire.Prefab.Queue,- module Control.Wire.Prefab.Sample,- module Control.Wire.Prefab.Simple,- module Control.Wire.Prefab.Time- )- where--import Control.Wire.Prefab.Accum-import Control.Wire.Prefab.Analyze-import Control.Wire.Prefab.Effect-import Control.Wire.Prefab.Event-import Control.Wire.Prefab.List-import Control.Wire.Prefab.Move-import Control.Wire.Prefab.Noise-import Control.Wire.Prefab.Queue-import Control.Wire.Prefab.Sample-import Control.Wire.Prefab.Simple-import Control.Wire.Prefab.Time
− Control/Wire/Prefab/Accum.hs
@@ -1,132 +0,0 @@--- |--- Module: Control.Wire.Prefab.Accum--- Copyright: (c) 2012 Ertugrul Soeylemez--- License: BSD3--- Maintainer: Ertugrul Soeylemez <es@ertes.de>------ Accumulation wires. These are left-scan equivalents of several--- sorts.--module Control.Wire.Prefab.Accum- ( -- * General- -- ** Accumulation- accum,- accumT,- accum1,- accumT1,- -- ** Function iteration- iterateW,- iterateWT,- -- ** Generic unfolding- unfold,- unfoldT,-- -- * Special- countFrom,- enumFromW,- mconcatW- )- where--import Control.Wire.Wire-import Data.AdditiveGroup-import Data.Monoid-import Prelude hiding (enumFrom, iterate)----- | The most general accumulator. This wire corresponds to a left--- scan.------ * Depends: previous instant.--accum :: (b -> a -> b) -> b -> Wire e m a b-accum f = accumT (const f)----- | Non-delaying variant of 'accum'.------ * Depends: current instant.--accum1 :: (b -> a -> b) -> b -> Wire e m a b-accum1 f = accumT1 (const f)----- | Like 'accum', but the accumulation function also receives the--- current time delta.------ * Depends: previous instant.--accumT :: (Time -> b -> a -> b) -> b -> Wire e m a b-accumT f x' =- mkPure $ \dt x ->- x' `seq` (Right x', accumT f (f dt x' x))----- | Non-delaying variant of 'accumT'.------ * Depends: current instant.--accumT1 :: (Time -> b -> a -> b) -> b -> Wire e m a b-accumT1 f x' =- mkPure $ \dt x ->- let y = f dt x' x in- x' `seq` (Right y, accumT1 f y)----- | Counts from the given vector adding the current input for the next--- instant.------ * Depends: previous instant.--countFrom :: (AdditiveGroup b) => b -> Wire e m b b-countFrom = accum (^+^)----- | Enumerates from the given element.--enumFromW :: (Enum b) => b -> Wire e m a b-enumFromW = accum (\x _ -> succ x)----- | Apply the input function continously. Corresponds to 'iterate' for--- lists.--iterateW :: (b -> b) -> b -> Wire e m a b-iterateW f = accum (\x _ -> f x)----- | Like 'iterate', but the accumulation function also receives the--- current time delta.--iterateWT :: (Time -> b -> b) -> b -> Wire e m a b-iterateWT f = accumT (\dt x _ -> f dt x)----- | Running 'Monoid' sum.------ * Depends: previous instant.--mconcatW :: (Monoid b) => Wire e m b b-mconcatW = accum mappend mempty----- | Corresponds to 'unfoldr' for lists.------ * Depends: current instant, if the unfolding function is strict in--- its second argument.--unfold :: (s -> a -> (b, s)) -> s -> Wire e m a b-unfold = unfoldT . const----- | Like 'unfold', but the accumulation function also receives the--- current time delta.------ * Depends: current instant, if the given function is strict in its--- third argument.--unfoldT :: (Time -> s -> a -> (b, s)) -> s -> Wire e m a b-unfoldT f s' =- mkPure $ \dt x' ->- let (x, s) = f dt s' x' in- s' `seq` (Right x, unfoldT f s)
− Control/Wire/Prefab/Analyze.hs
@@ -1,243 +0,0 @@--- |--- Module: Control.Wire.Prefab.Analyze--- Copyright: (c) 2012 Ertugrul Soeylemez--- License: BSD3--- Maintainer: Ertugrul Soeylemez <es@ertes.de>------ Signal analysis wires.--module Control.Wire.Prefab.Analyze- ( -- * Statistics- -- ** Average- avg,- avgInt,- avgAll,- avgFps,- avgFpsInt,- -- ** Peak- highPeak,- lowPeak,- peakBy,-- -- * Monitoring- collect,- firstSeen,- lastSeen- )- where--import qualified Data.Map as M-import qualified Data.Sequence as Seq-import Control.Category-import Control.Wire.Prefab.Time-import Control.Wire.Wire-import Data.Map (Map)-import Data.Monoid-import Data.Sequence (Seq, ViewL(..), (|>), viewl)-import Data.VectorSpace-import Prelude hiding ((.), id)----- | Calculate the average of the signal over the given number of last--- samples. If you need an average over all samples ever produced,--- consider using 'avgAll' instead.------ * Complexity: O(n) space wrt number of samples.------ * Depends: current instant.--avg ::- forall a m e v.- (Fractional a, VectorSpace v, Scalar v ~ a)- => Int- -> Wire e m v v-avg n | n <= 0 = error "avg: The number of samples must be positive"-avg n =- mkPure $ \_ x ->- (Right x, avg' (Seq.replicate n (x ^/ d)) x)-- where- avg' :: Seq v -> v -> Wire e m v v- avg' samples'' a' =- mkPure $ \_ x ->- let xa = x ^/ d- xa' :< samples' = viewl samples''- samples = samples' |> xa- a = a' ^-^ xa' ^+^ xa- in a `seq` (Right a, avg' samples a)-- d :: Scalar v- d = realToFrac n----- | Calculate the average of the input signal over all samples. This--- is usually not what you want. In most cases the 'avg' wire is--- preferable.------ * Depends: current instant.--avgAll ::- forall a m e v.- (Fractional a, VectorSpace v, Scalar v ~ a)- => Wire e m v v-avgAll = mkPure $ \_ x -> (Right x, avgAll' 1 x)- where- avgAll' :: a -> v -> Wire e m v v- avgAll' n' a' =- mkPure $ \_ x ->- let n = n' + 1- a = a' ^+^ (x ^-^ a') ^/ n- in a' `seq` (Right a, avgAll' n a)----- | Calculate the average number of instants per second for the last--- given number of instants. In a continuous game or simulation this--- corresponds to the average number of frames per second, hence the--- name.------ * Complexity: O(n) space wrt number of samples.------ * Depends: time.--avgFps :: (Monad m) => Int -> Wire e m a Double-avgFps n = recip (avg n) . dtime----- | Like 'avgFps', but sample in discrete intervals only. This can--- greatly enhance the performance, when you have an inefficient clock--- source.------ * Complexity: O(n) space wrt number of samples.------ * Depends: time.--avgFpsInt ::- (Monad m)- => Int -- ^ Sampling interval.- -> Int -- ^ Number of samples.- -> Wire e m a Double-avgFpsInt int n = recip (avgInt int n) . dtime----- | Same as 'avg', but with a sampling interval. This can be used to--- increase the performance, if the input is complicated.------ * Complexity: O(n) space wrt number of samples.------ * Depends: current instant.--avgInt ::- forall a m e v.- (Fractional a, VectorSpace v, Scalar v ~ a)- => Int -- ^ Sampling interval.- -> Int -- ^ Number of samples.- -> Wire e m v v-avgInt _ n | n <= 0 = error "avg: The number of samples must be positive"-avgInt int n =- mkPure $ \_ x ->- (Right x, avg' 0 (Seq.replicate n (x ^/ d)) x)-- where- avg' :: Int -> Seq v -> v -> Wire e m v v- avg' si samples'' a' | si < int = mkPure $ \_ _ -> (Right a', avg' (si + 1) samples'' a')- avg' _ samples'' a' =- mkPure $ \_ x ->- let xa = x ^/ d- xa' :< samples' = viewl samples''- samples = samples' |> xa- a = a' ^-^ xa' ^+^ xa- in a `seq` (Right a, avg' 0 samples a)-- d :: Scalar v- d = realToFrac n----- | Collect all distinct inputs ever received together with a count.--- Elements not appearing in the map have not been observed yet.------ * Complexity: O(n) space.------ * Depends: current instant.--collect :: forall b m e. (Ord b) => Wire e m b (Map b Int)-collect = collect' M.empty- where- collect' :: Map b Int -> Wire e m b (Map b Int)- collect' m' =- mkPure $ \_ x ->- let m = M.insertWith (+) x 1 m' in- m `seq` (Right m, collect' m)----- | Outputs the first local time the input was seen.------ * Complexity: O(n) space, O(log n) time wrt number of samples so far.------ * Depends: current instant, time.--firstSeen :: forall a m e. (Ord a) => Wire e m a Time-firstSeen = seen' 0 M.empty- where- seen' :: Time -> Map a Time -> Wire e m a Time- seen' t' m' =- mkPure $ \dt x ->- let t = t' + dt in- t `seq`- case M.lookup x m' of- Just xt -> (Right xt, seen' t m')- Nothing ->- let m = M.insert x t m' in- m `seq` (Right t, seen' t m)----- | High peak.------ * Depends: current instant.--highPeak :: (Ord b) => Wire e m b b-highPeak = peakBy compare----- | Outputs the local time the input was previously seen.------ * Complexity: O(n) space, O(log n) time wrt number of samples so far.------ * Depends: current instant, time.------ * Inhibits: if this is the first time the input is seen.--lastSeen :: forall a m e. (Monoid e, Ord a) => Wire e m a Time-lastSeen = seen' 0 M.empty- where- seen' :: Time -> Map a Time -> Wire e m a Time- seen' t' m' =- mkPure $ \dt x ->- let t = t' + dt- m = M.insert x t m' in- t `seq` m `seq`- case M.lookup x m' of- Just xt -> (Right xt, seen' t m)- Nothing -> (Left mempty, seen' t m)----- | Low peak.------ * Depends: current instant.--lowPeak :: (Ord b) => Wire e m b b-lowPeak = peakBy (flip compare)----- | Output the peak with respect to the given comparison function.------ * Depends: current instant.--peakBy :: forall b m e. (b -> b -> Ordering) -> Wire e m b b-peakBy f = mkPure $ \_ x -> (Right x, peak' x)- where- peak' :: b -> Wire e m b b- peak' x' =- mkPure $ \_ x ->- case f x' x of- GT -> (Right x', peak' x')- _ -> (Right x, peak' x)
− Control/Wire/Prefab/Effect.hs
@@ -1,117 +0,0 @@--- |--- Module: Control.Wire.Prefab.Effect--- Copyright: (c) 2012 Ertugrul Soeylemez--- License: BSD3--- Maintainer: Ertugrul Soeylemez <es@ertes.de>------ Effectful wires.--module Control.Wire.Prefab.Effect- ( -- * Monadic effects- -- ** Simple- perform,- -- ** Exception-aware- execute,- execute_,- executeWith,- executeWith_,-- -- * Branching- branch,- quit,- quitWith- )- where--import qualified Data.Bifunctor as Bi-import Control.Exception.Lifted-import Control.Monad-import Control.Monad.Trans.Control-import Control.Wire.Types-import Control.Wire.Wire-import Data.List-import Data.Monoid----- | Branch according to the unterlying 'MonadPlus' instance. Note that--- this wire branches at every instant.------ * Depends: current instant.--branch :: (MonadPlus m) => Wire e m [a] a-branch = mkFixM $ \_ -> liftM Right . foldl' mplus mzero . map return----- | Variant of 'executeWith' for the 'LastException' inhibition monoid.------ * Depends: current instant.------ * Inhibits: when the action throws an exception.--execute ::- (MonadBaseControl IO m)- => Wire LastException m (m a) a-execute = executeWith (Last . Just)----- | Variant of 'executeWith_' for the 'LastException' inhibition monoid.------ * Depends: current instant, if the given function is strict.------ * Inhibits: when the action throws an exception.--execute_ ::- (MonadBaseControl IO m)- => (a -> m b)- -> Wire LastException m a b-execute_ = executeWith_ (Last . Just)----- | Perform the input monadic action at every instant.------ * Depends: current instant.------ * Inhibits: when the action throws an exception.--executeWith ::- (MonadBaseControl IO m)- => (SomeException -> e) -- ^ Turns an exception into an inhibition value.- -> Wire e m (m a) a-executeWith fromEx = mkFixM $ \_ c -> liftM (Bi.first fromEx) (try c)----- | Perform the given monadic action at every instant.------ * Depends: current instant, if the given function is strict.------ * Inhibits: when the action throws an exception.--executeWith_ ::- (MonadBaseControl IO m)- => (SomeException -> e) -- ^ Turns an exception into an inhibition value.- -> (a -> m b) -- ^ Action to perform.- -> Wire e m a b-executeWith_ fromEx c = mkFixM $ \_ -> liftM (Bi.first fromEx) . try . c----- | Perform the input monadic action in a wire.------ * Depends: current instant.--perform :: (Monad m) => Wire e m (m b) b-perform = mkFixM . const $ liftM Right----- | Quits the current branch using 'mzero'.--quit :: (MonadPlus m) => Wire e m a b-quit = mkFixM $ \_ _ -> mzero----- | Acts like identity in the first instant, then quits the current--- branch using 'mzero'.------ * Depends: first instant.--quitWith :: (MonadPlus m) => Wire e m a a-quitWith = mkPure $ \_ x -> (Right x, quit)
− Control/Wire/Prefab/Event.hs
@@ -1,307 +0,0 @@--- |--- Module: Control.Wire.Prefab.Event--- Copyright: (c) 2012 Ertugrul Soeylemez--- License: BSD3--- Maintainer: Ertugrul Soeylemez <es@ertes.de>------ Event wires.--module Control.Wire.Prefab.Event- ( -- * Instants- afterI,- eventsI,- forI,- notYet,- once,- periodicallyI,-- -- * Signal analysis- changed,- inject,- -- ** Predicate-based- asSoonAs,- edge,- forbid,- require,- unless,- until,- when,- while,-- -- * Time- after,- events,- for,- periodically,-- -- * Utilities- inhibit- )- where--import Control.Category-import Control.Wire.Classes-import Control.Wire.Types-import Control.Wire.Wire-import Data.Monoid-import Prelude hiding ((.), id, until)----- | Produce after the given amount of time.------ * Depends: current instant when producing, time.------ * Inhibits: until the given amount of time has passed.--after :: (Monoid e) => Time -> Event e m a-after t- | t <= 0 = identity- | otherwise = mkPure $ \dt _ -> (Left mempty, after (t - dt))----- | Produce after the given number of instants.------ * Depends: current instant when producing.------ * Inhibits: until the given number of instants has passed.--afterI :: (Monoid e) => Int -> Event e m a-afterI t- | t <= 0 = identity- | otherwise = mkPure $ \_ _ -> (Left mempty, afterI (t - 1))----- | Inhibit until the given predicate holds for the input signal. Then--- produce forever.------ * Depends: current instant, if the predicate is strict. Once true,--- on current instant forever.------ * Inhibits: until the predicate becomes true.--asSoonAs :: (Monoid e) => (a -> Bool) -> Event e m a-asSoonAs p =- mkPure $ \_ x ->- if p x- then (Right x, identity)- else (Left mempty, asSoonAs p)----- | Produce when the signal has changed and at the first instant.------ * Depends: current instant.------ * Inhibits: after the first instant when the input has changed.--changed :: (Eq a, Monoid e) => Event e m a-changed = mkPure $ \_ x0 -> (Right x0, changed' x0)- where- changed' x' =- mkPure $ \_ x ->- (if x' == x then Left mempty else Right x,- changed' x)----- | Produces once whenever the given predicate switches from 'False' to--- 'True'.------ * Depends: current instant.------ * Inhibits: when the predicate has not just switched from 'False' to--- 'True'.--edge :: (Monoid e) => (a -> Bool) -> Event e m a-edge p = off- where- off = mkPure $ \_ x -> if p x then (Right x, on) else (Left mempty, off)- on = mkPure $ \_ x -> (Left mempty, if p x then on else off)----- | Produce once periodically. The production periods are given by the--- argument list. When it's @[1,2,3]@ it produces after one second,--- then after two more seconds and finally after three more seconds.--- When the list is exhausted, it never produces again.------ * Depends: current instant when producing, time.------ * Inhibits: between the given intervals.--events :: (Monoid e) => [Time] -> Event e m a-events [] = never-events (t':ts) =- mkPure $ \dt x ->- let t = t' - dt in- if t <= 0- then (Right x, events (mapHead (+ t) ts))- else (Left mempty, events (t:ts))-- where- mapHead :: (a -> a) -> [a] -> [a]- mapHead _ [] = []- mapHead f (x:xs) = f x : xs----- | Variant of 'periodically' in number of instants instead of amount--- of time.------ * Depends: current instant when producing.------ * Inhibits: between the given intervals.--eventsI :: (Monoid e) => [Int] -> Event e m a-eventsI [] = never-eventsI (0:ts) = mkPure $ \_ x -> (Right x, eventsI ts)-eventsI (t:ts) = mkPure $ \_ _ -> (Left mempty, eventsI (t - 1 : ts))----- | Produce for the given amount of time.------ * Depends: current instant when producing, time.------ * Inhibits: after the given amount of time has passed.--for :: (Monoid e) => Time -> Event e m a-for t- | t <= 0 = never- | otherwise = mkPure $ \dt x -> (Right x, for (t - dt))----- | Same as 'unless'.--forbid :: (Monoid e) => (a -> Bool) -> Event e m a-forbid = unless----- | Produce for the given number of instants.------ * Depends: current instant when producing.------ * Inhibits: after the given number of instants has passed.--forI :: (Monoid e) => Int -> Event e m a-forI t- | t <= 0 = never- | otherwise = mkPure $ \_ x -> (Right x, forI (t - 1))----- | Inhibit with the given value. You may want to use a combination of--- 'Control.Applicative.empty' and 'Control.Wire.Trans.Event.<!>'--- instead.------ * Inhibits: always.--inhibit :: e -> Wire e m a b-inhibit ex = mkFix (\_ _ -> Left ex)----- | Inject the input signal. Please keep in mind that in application--- code it is almost always wrong to use this wire. It should only be--- used to interact with other frameworks/abstractions, and even then--- it's probably just a last resort.------ When you want to write your own wires, consider using 'mkPure' or the--- various variants of it.------ * Depends: current instant.------ * Inhibits: depending on input signal (see 'Injectable').--inject :: (Injectable e f) => Wire e m (f b) b-inject = mkFix (const toSignal)----- | Inhibit once.------ * Depends: current instant after the first instant.------ * Inhibits: in the first instant.--notYet :: (Monoid e) => Event e m a-notYet = mkPure $ \_ _ -> (Left mempty, identity)----- | Produce once.------ * Depends: current instant in the first instant.------ * Inhibits: after the first instant.--once :: (Monoid e) => Event e m a-once = mkPure $ \_ x -> (Right x, never)----- | Produce once periodically with the given time interval.------ * Depends: current instant when producing, time.------ * Inhibits: between the intervals.--periodically :: (Monoid e) => Time -> Event e m a-periodically = events . repeat----- | Produce once periodically with the given number of instants as the--- interval.------ * Depends: current instant when producing.------ * Inhibits: between the intervals.--periodicallyI :: (Monoid e) => Int -> Event e m a-periodicallyI = eventsI . repeat----- | Same as 'when'.--require :: (Monoid e) => (a -> Bool) -> Event e m a-require = when----- | Produce when the given predicate on the input signal does not hold.------ * Depends: current instant if the predicate is strict.------ * Inhibits: When the predicate is true.--unless :: (Monoid e) => (a -> Bool) -> Event e m a-unless p =- mkFix $ \_ x ->- if p x then Left mempty else Right x----- | Produce until the given predicate on the input signal holds, then--- inhibit forever.------ * Depends: current instant, if the predicate is strict.------ * Inhibits: forever as soon as the predicate becomes true.--until :: (Monoid e) => (a -> Bool) -> Event e m a-until p = while (not . p)----- | Produce when the given predicate on the input signal holds.------ * Depends: current instant if the predicate is strict.------ * Inhibits: When the predicate is false.--when :: (Monoid e) => (a -> Bool) -> Event e m a-when p =- mkFix $ \_ x ->- if p x then Right x else Left mempty----- | Produce while the given predicate on the input signal holds, then--- inhibit forever.------ * Depends: current instant, if the predicate is strict.------ * Inhibits: forever as soon as the predicate becomes false.--while :: (Monoid e) => (a -> Bool) -> Event e m a-while p =- mkPure $ \_ x ->- if p x- then (Right x, while p)- else (Left mempty, never)
− Control/Wire/Prefab/List.hs
@@ -1,36 +0,0 @@--- |--- Module: Control.Wire.Prefab.List--- Copyright: (c) 2012 Ertugrul Soeylemez--- License: BSD3--- Maintainer: Ertugrul Soeylemez <es@ertes.de>------ Wires from lists.--module Control.Wire.Prefab.List- ( -- * Wires from lists- cycleW,- list- )- where--import Control.Applicative-import Control.Monad.Fix-import Control.Wire.Wire-import Data.Monoid----- | Produce the values in the given list cycling forever.------ * Inhibits: when the argument list is empty.--cycleW :: (Monad m, Monoid e) => [b] -> Wire e m a b-cycleW [] = empty-cycleW xs = fix (\again -> foldr cons again xs)----- | Produce the values in the given list and then inhibit forever.------ * Inhibits: when the list is exhausted.--list :: (Monad m, Monoid e) => [b] -> Wire e m a b-list = foldr cons empty
− Control/Wire/Prefab/Move.hs
@@ -1,247 +0,0 @@--- |--- Module: Control.Wire.Prefab.Move--- Copyright: (c) 2012 Ertugrul Soeylemez--- License: BSD3--- Maintainer: Ertugrul Soeylemez <es@ertes.de>------ This module provides the wires for various kinds of moving objects.--- In particular this includes various calculus wires like integrals and--- differentials.--module Control.Wire.Prefab.Move- ( -- * Calculus- -- ** Integrals- integral,- integral_,- integralLim,- integralLim_,- integral1,- integral1_,- integralLim1,- integralLim1_,- -- ** Differentials- derivative,- derivative_,-- -- * Simulations/games- object,- object_,- ObjectState(..),- ObjectDiff(..)- )- where--import Control.Applicative-import Control.Arrow-import Control.Category-import Control.Wire.Prefab.Accum-import Control.Wire.Prefab.Time-import Control.Wire.Wire-import Data.Data-import Data.VectorSpace-import Prelude hiding ((.), id)----- | Object state. This includes the position and velocity.--data ObjectState a =- ObjectState {- objPosition :: a, -- ^ Position.- objVelocity :: a -- ^ Velocity.- }- deriving (Data, Eq, Ord, Read, Show, Typeable)----- | Differential for objects.--data ObjectDiff a- -- | Accelerate (units per second).- = Accelerate a-- -- | Teleport to the given position instantly (velocity will be- -- unchanged).- | Position a-- -- | Specify velocity (units per second).- | Velocity a- deriving (Data, Eq, Ord, Read, Show, Typeable)----- | Derivative. Receives @x@ and @dt@ and calculates the change rate--- @dx/dt@. Note that @dt@ despite its name does not have to be time.------ The exception handler function is called when @dt@ is zero. That--- function's result is the wire's output for those instants. If you--- don't want to handle exceptional cases specially, just pass @(^/)@ as--- the handler function.------ * Depends: current instant.--derivative ::- (Eq dt, Fractional dt, VectorSpace b, Scalar b ~ dt)- => (b -> dt -> b) -- ^ Handle exceptional change rates (receives dx and dt).- -> b -- ^ Initial position.- -> Wire e m (b, dt) b-derivative catch x0 =- mkPure $ \_ (x1, dt) ->- let dx = x1 ^-^ x0- d | dt == 0 = catch dx dt- | otherwise = dx ^/ dt- in (Right d, derivative catch x1)----- | Same as 'derivative', but with respect to time.------ * Depends: current instant.--derivative_ ::- (Monad m, VectorSpace b, Scalar b ~ Time)- => (b -> Time -> b) -- ^ Handle exceptional cases.- -> b -- ^ Initial position.- -> Wire e m b b-derivative_ catch x0 = derivative catch x0 . (id &&& dtime)----- | Integral wire. Produces position from velocity in the sense of the--- given vector space.------ * Depends: previous instant.--integral ::- (VectorSpace b)- => b- -> Wire e m (b, Scalar b) b-integral = accum (\x (dx, dt) -> x ^+^ dt *^ dx)----- | Non-delaying variant of 'integral'.------ * Depends: current instant.--integral1 ::- (VectorSpace b)- => b- -> Wire e m (b, Scalar b) b-integral1 = accum1 (\x (dx, dt) -> x ^+^ dt *^ dx)----- | Same as 'integral', but with respect to time.------ * Depends: previous instant.--integral_ ::- (VectorSpace b, Scalar b ~ Time)- => b- -> Wire e m b b-integral_ = accumT (\dt x dx -> x ^+^ dt *^ dx)----- | Non-delaying variant of 'integral_'.------ * Depends: current instant.--integral1_ ::- (Monad m, VectorSpace b, Scalar b ~ Time)- => b- -> Wire e m b b-integral1_ = accumT1 (\dt x dx -> x ^+^ dt *^ dx)----- | Variant of 'integral', where you can specify a post-update--- function, which receives the previous position as well as the current--- (in that order). This is useful for limiting the output (think of--- robot arms that can't be moved freely).------ * Depends: current instant if the post-update function is strict in--- its first argument, previous instant if not.--integralLim ::- (VectorSpace b)- => (w -> b -> b -> b) -- ^ Post-update function.- -> b -- ^ Initial value.- -> Wire e m ((b, w), Scalar b) b-integralLim uf = accum (\x ((dx, w), dt) -> uf w x (x ^+^ dt *^ dx))----- | Non-delaying variant of 'integralLim'.------ * Depends: current instant.--integralLim1 ::- (VectorSpace b)- => (w -> b -> b -> b) -- ^ Post-update function.- -> b -- ^ Initial value.- -> Wire e m ((b, w), Scalar b) b-integralLim1 uf = accum1 (\x ((dx, w), dt) -> uf w x (x ^+^ dt *^ dx))----- | Same as 'integralLim', but with respect to time.------ * Depends: previous instant.--integralLim_ ::- (VectorSpace b, Scalar b ~ Time)- => (w -> b -> b -> b)- -> b- -> Wire e m (b, w) b-integralLim_ uf = accumT (\dt x (dx, w) -> uf w x (x ^+^ dt *^ dx))----- | Non-delaying variant of 'integralLim_'.------ * Depends: current instant.--integralLim1_ ::- (VectorSpace b, Scalar b ~ Time)- => (w -> b -> b -> b)- -> b- -> Wire e m (b, w) b-integralLim1_ uf = accumT1 (\dt x (dx, w) -> uf w x (x ^+^ dt *^ dx))----- | Objects are generalized integrals. They are controlled through--- velocity and/or acceleration and can be collision-checked as well as--- instantly teleported.------ The post-move update function receives the world state and the--- current object state. It is applied just before the wire produces--- its output. You can use it to perform collision-checks or to limit--- the velocity.------ Note that teleportation doesn't change the velocity.------ * Depends: current instant.--object ::- forall b m dt e w.- (VectorSpace b, Scalar b ~ dt)- => (w -> ObjectState b -> ObjectState b) -- ^ Post-move update function.- -> ObjectState b -- ^ Initial state.- -> Wire e m (ObjectDiff b, w, dt) (ObjectState b)-object uf = loop- where- applyDiff :: dt -> ObjectDiff b -> ObjectState b -> ObjectState b- applyDiff dt (Accelerate dv) (ObjectState x' v') = ObjectState x v- where- v = v' ^+^ dt *^ dv- x = x' ^+^ dt *^ v- applyDiff _ (Position x) (ObjectState _ v) = ObjectState x v- applyDiff dt (Velocity v) (ObjectState x' _) = ObjectState (x' ^+^ dt *^ v) v-- loop :: ObjectState b -> Wire e m (ObjectDiff b, w, dt) (ObjectState b)- loop os' =- mkPure $ \_ (dos, w, dt) ->- let os = uf w . applyDiff dt dos $ os'- in (Right os, loop os)----- | Same as 'object', but with respect to time.------ * Depends: current instant.--object_ ::- (Monad m, VectorSpace b, Scalar b ~ Time)- => (w -> ObjectState b -> ObjectState b) -- ^ Post-move update function.- -> ObjectState b -- ^ Initial state.- -> Wire e m (ObjectDiff b, w) (ObjectState b)-object_ uf x0 = object uf x0 . liftA2 (\(dx, w) dt -> (dx, w, dt)) id dtime
− Control/Wire/Prefab/Noise.hs
@@ -1,96 +0,0 @@--- |--- Module: Control.Wire.Prefab.Noise--- Copyright: (c) 2012 Ertugrul Soeylemez--- License: BSD3--- Maintainer: Ertugrul Soeylemez <es@ertes.de>------ Various noise generators.--module Control.Wire.Prefab.Noise- ( -- * Pure random noise- noise,- noiseR,- wackelkontakt,-- -- * Effectful random noise- noiseM,- noiseRM,- wackelkontaktM- )- where--import Control.Monad-import Control.Wire.Classes-import Control.Wire.Prefab.Accum-import Control.Wire.Types-import Control.Wire.Wire-import Data.Monoid-import System.Random----- | Pure noise generator.--noise ::- (Random b, RandomGen g)- => g -- ^ Initial random number generator.- -> Wire e m a b-noise = unfold (\g' _ -> random g')----- | Noise generator.--noiseM ::- (MonadRandom m, Random b)- => Wire e m a b-noiseM =- mkFixM $ \_ _ -> liftM (Right $!) getRandom----- | Ranged noise generator.------ * Depends: current instant.--noiseRM ::- (MonadRandom m, Random b)- => Wire e m (b, b) b-noiseRM = mkFixM $ \_ -> liftM (Right $!) . getRandomR----- | Pure ranged noise generator.------ * Depends: current instant.--noiseR ::- (Random b, RandomGen g)- => g -- ^ Initial random number generator.- -> Wire e m (b, b) b-noiseR = unfold (\g' r -> randomR r g')----- | Event: Occurs randomly with the given probability.------ * Inhibits: @wackelkontaktM p@ inhibits with probability @1 - p@.--wackelkontakt ::- (Monoid e, RandomGen g)- => Double -- ^ Occurrence probability.- -> g -- ^ Initial random number generator.- -> Event e m a-wackelkontakt p g' =- mkPure $ \_ x ->- let (e, g) = random g' in- (if (e < p) then Right x else Left mempty, wackelkontakt p g)----- | Event: Occurs randomly with the given probability.------ * Inhibits: @wackelkontaktM p@ inhibits with probability @1 - p@.--wackelkontaktM ::- (MonadRandom m, Monoid e)- => Double -- ^ Occurrence probability.- -> Event e m a-wackelkontaktM p =- mkFixM $ \_ x -> do- e <- getRandom- return (if (e < p) then Right x else Left mempty)
− Control/Wire/Prefab/Queue.hs
@@ -1,88 +0,0 @@--- |--- Module: Control.Wire.Prefab.Queue--- Copyright: (c) 2012 Ertugrul Soeylemez--- License: BSD3--- Maintainer: Ertugrul Soeylemez <es@ertes.de>------ Wires acting as queues.--module Control.Wire.Prefab.Queue- ( -- * Queues- bag,- fifo,- lifo- )- where--import qualified Data.Set as S-import qualified Data.Sequence as Seq-import Control.Wire.Wire-import Data.Monoid-import Data.Set (Set)-import Data.Sequence (ViewL(..), (><), viewl)----- | Incoming values are placed in a set, which is discharged element by--- element. Lower values are served first. Duplicate values are served--- once.------ Note: Incorrect usage can lead to congestion.------ * Complexity: O(n) space wrt bag size.------ * Depends: current instant.------ * Inhibits: when the bag is empty.--bag :: (Monoid e, Ord b) => Wire e m (Set b) b-bag = bag' S.empty- where- bag' s' =- mkPure $ \_ xs ->- case S.minView (S.union s' xs) of- Nothing -> (Left mempty, bag' S.empty)- Just (x, s) -> (Right x, bag' s)----- | First in, first out. The input list is placed on the right end of--- a queue at every instant, giving earlier elements a higher priority.--- The queue is discharged item by item from the left.------ Note: Incorrect usage can lead to congestion.------ * Complexity: O(n) space wrt queue size.------ * Depends: current instant.------ * Inhibits: when the queue is currently empty.--fifo :: (Monoid e) => Wire e m [b] b-fifo = fifo' Seq.empty- where- fifo' queue' =- mkPure $ \_ xs ->- case viewl (queue' >< Seq.fromList xs) of- EmptyL -> (Left mempty, fifo' Seq.empty)- x :< queue -> (Right x, fifo' queue)----- | Last in, first out. The input list is placed on a stack at every--- instant, giving earlier elements a higher priority. The stack is--- discharged item by item from the top.------ Note: Incorrect usage can lead to congestion.------ * Complexity: O(n) space wrt stack size.------ * Depends: current instant.------ * Inhibits: when the stack is currently empty.--lifo :: (Monoid e) => Wire e m [b] b-lifo = lifo' Seq.empty- where- lifo' queue' =- mkPure $ \_ xs ->- case viewl (Seq.fromList xs >< queue') of- EmptyL -> (Left mempty, lifo' Seq.empty)- x :< queue -> (Right x, lifo' queue)
− Control/Wire/Prefab/Sample.hs
@@ -1,85 +0,0 @@--- |--- Module: Control.Wire.Prefab.Sample--- Copyright: (c) 2012 Ertugrul Soeylemez--- License: BSD3--- Maintainer: Ertugrul Soeylemez <es@ertes.de>------ Signal sampling wires.--module Control.Wire.Prefab.Sample- ( -- * Sampling- --history,- keep,- sample,- window,- windowList- )- where--import qualified Data.Foldable as F-import qualified Data.Sequence as Seq-import Control.Wire.Wire-import Data.Sequence (Seq, (|>))----- | Produce the most recent inputs in the given time window. The left--- input signal is the sample, the right input signal is the time--- window.------ * Complexity: O(n), where n the number of samples in the time window.------ * Depends: current instant.----history :: (Reactive cat) => Wire e cat (a, Time) (Seq (a, Time))---history = undefined----- | Keep the input signal of the first instant forever.------ Depends: first instant.--keep :: Wire e m a a-keep = mkPure (\_ x -> (Right x, constant x))----- | Sample the left signal at discrete intervals given by the right--- signal.------ * Depends: instant of the last sample.--sample :: Wire e m (a, Time) a-sample = mkPure $ \dt (x, _) -> (Right x, sample' dt x)- where- sample' t0' x' =- mkPure $ \dt (x, t1) ->- let t0 = t0' + dt in- if t0 >= t1- then (Right x, sample' (t0 - t1) x)- else (Right x', sample' t0 x')----- | Produce up to the given number of most recent inputs.------ * Complexity: O(n), where n is the given argument.------ * Depends: current instant.--window :: Int -> Wire e m a (Seq a)-window = collect' Seq.empty- where- collect' s' 0 = window' s'- collect' s' n =- mkPure $ \_ x ->- let s = s' |> x in- s `seq` (Right s, collect' s (n - 1))-- window' s' =- mkPure $ \_ x ->- let s = Seq.drop 1 (s' |> x) in- s `seq` (Right s, window' s)----- | Same as @fmap toList . window@.--windowList :: (Monad m) => Int -> Wire e m a [a]-windowList = fmap F.toList . window
− Control/Wire/Prefab/Simple.hs
@@ -1,67 +0,0 @@--- |--- Module: Control.Wire.Prefab.Simple--- Copyright: (c) 2012 Ertugrul Soeylemez--- License: BSD3--- Maintainer: Ertugrul Soeylemez <es@ertes.de>------ Basic wires.--module Control.Wire.Prefab.Simple- ( -- * Basic signal manipulation- append,- delay,- prepend,-- -- * Forcing evaluation- force,- forceNF- )- where--import Control.Arrow-import Control.Category-import Control.DeepSeq (NFData, ($!!))-import Control.Wire.Wire-import Prelude hiding ((.), id)----- | Convenience function to add another signal.------ * Depends: current instant.--append :: (Monad m) => Wire e m a b -> Wire e m a (a, b)-append = (id &&&)----- | One-instant delay.------ * Depends: previous instant.--delay :: a -> Wire e m a a-delay x' = mkPure (\_ x -> x' `seq` (Right x', delay x))----- | Acts like the identity wire, but forces evaluation of the signal to--- WHNF.------ * Depends: current instant.--force :: Wire e m a a-force = mkFix (\_ -> (Right $!))----- | Acts like the identity wire, but forces evaluation of the signal to--- NF.------ * Depends: current instant.--forceNF :: (NFData a) => Wire e m a a-forceNF = mkFix (\_ -> (Right $!!))----- | Convenience function to add another signal.------ * Depends: current instant.--prepend :: (Monad m) => Wire e m a b -> Wire e m a (b, a)-prepend = (&&& id)
− Control/Wire/Prefab/Time.hs
@@ -1,45 +0,0 @@--- |--- Module: Control.Wire.Prefab.Time--- Copyright: (c) 2012 Ertugrul Soeylemez--- License: BSD3--- Maintainer: Ertugrul Soeylemez <es@ertes.de>------ Time wires.--module Control.Wire.Prefab.Time- ( -- * Time- dtime,- time,- timeFrom- )- where--import Control.Wire.Wire----- | Outputs the time delta to the last instant.------ * Depends: time.--dtime :: Wire e m a Time-dtime = mkFix (\dt _ -> Right dt)----- | Outputs the current local time passed since the first instant.------ * Depends: time.--time :: Wire e m a Time-time = timeFrom 0----- | Outputs the current local time passed since the first instant with--- the given offset.------ * Depends: time.--timeFrom :: Time -> Wire e m a Time-timeFrom t' =- mkPure $ \dt _ ->- let t = t' + dt in- t `seq` (Right t, timeFrom t)
+ Control/Wire/Run.hs view
@@ -0,0 +1,63 @@+-- |+-- Module: Control.Wire.Run+-- Copyright: (c) 2013 Ertugrul Soeylemez+-- License: BSD3+-- Maintainer: Ertugrul Soeylemez <es@ertes.de>++module Control.Wire.Run+ ( -- * Testing wires+ testWire,+ testWireM+ )+ where++import Control.Monad.IO.Class+import Control.Wire.Core+import Control.Wire.Session+import Data.Functor.Identity+import System.IO+++-- | This function runs the given wire using the given state delta+-- generator. It constantly shows the output of the wire on one line on+-- stdout. Press Ctrl-C to abort.++testWire ::+ (MonadIO m, Show b, Show e)+ => Session m s+ -> (forall a. Wire s e Identity a b)+ -> m c+testWire s0 w0 = loop s0 w0+ where+ loop s' w' = do+ (ds, s) <- stepSession s'+ let Identity (mx, w) = stepWire w' ds (Right ())+ liftIO $ do+ putChar '\r'+ putStr (either (\ex -> "I: " ++ show ex) show mx)+ putStr "\027[K"+ hFlush stdout+ loop s w+++-- | This function runs the given wire using the given state delta+-- generator. It constantly shows the output of the wire on one line on+-- stdout. Press Ctrl-C to abort.++testWireM ::+ (Monad m', MonadIO m, Show b, Show e)+ => (forall a. m' a -> m a)+ -> Session m s+ -> (forall a. Wire s e m' a b)+ -> m c+testWireM run s0 w0 = loop s0 w0+ where+ loop s' w' = do+ (ds, s) <- stepSession s'+ (mx, w) <- run (stepWire w' ds (Right ()))+ liftIO $ do+ putChar '\r'+ putStr (either (\ex -> "I: " ++ show ex) show mx)+ putStr "\027[K"+ hFlush stdout+ loop s w
Control/Wire/Session.hs view
@@ -1,239 +1,111 @@ -- | -- Module: Control.Wire.Session--- Copyright: (c) 2012 Ertugrul Soeylemez+-- Copyright: (c) 2013 Ertugrul Soeylemez -- License: BSD3 -- Maintainer: Ertugrul Soeylemez <es@ertes.de>------ Wire sessions. module Control.Wire.Session- ( -- * Performing instants- stepSession,- stepSession_,- stepSessionP,- stepSessionP_,-- -- * Testing wires- testWire,- testWireP,- -- ** Helper functions- testPrint,-- -- * Sessions+ ( -- * State delta types+ HasTime(..), Session(..),- -- ** Generic sessions- genSession,- -- ** Specific session types++ -- ** Wires with time+ Timed(..), clockSession,- counterSession,- frozenSession+ clockSession_,+ countSession,+ countSession_ ) where -import Control.Concurrent-import Control.Exception-import Control.Monad-import Control.Monad.Identity-import Control.Monad.Trans-import Control.Wire.Types-import Control.Wire.Wire+import Control.Applicative+import Control.Monad.IO.Class+import Data.Data+import Data.Foldable (Foldable) import Data.Monoid import Data.Time.Clock-import System.IO----- | A session value contains time-related information.--newtype Session m =- Session {- sessionUpdate :: m (Time, Session m)- }----- | Construct a session using real time. This session type uses--- 'getCurrentTime'. If you have a faster time source, you may want to--- use 'genSession' instead and construct your own clock.--clockSession :: (MonadIO m) => Session m-clockSession =- Session $ do- t0 <- liftIO getCurrentTime- return (0, loop t0)-- where- loop t' =- Session $ do- t <- liftIO getCurrentTime- let dt = realToFrac (diffUTCTime t t')- return (dt, loop t)----- | Construct a simple counter session. The time delta is the given--- argument at every instant.--counterSession ::- (Monad m)- => Time -- ^ Time delta for every instant.- -> Session m-counterSession dt =- let s = Session (return (dt, s)) in s+import Data.Traversable (Traversable) --- | Construct a frozen session. Same as @'counterSession' 0@.+-- | State delta types with time deltas. -frozenSession :: (Monad m) => Session m-frozenSession = counterSession 0+class (Monoid s, Real t) => HasTime t s | s -> t where+ -- | Extract the current time delta.+ dtime :: s -> t --- | Construct a generic session from the given initial session value--- and the update function. You can use this function to implement your--- own clock.------ If you just want to use real time, you may want to use--- 'clockSession'.--genSession ::- (Monad m)- => a- -> (a -> m (Time, a))- -> Session m-genSession s' f =- Session $ do- (t, s) <- f s'- return (t, genSession s f)+-- | State delta generators as required for wire sessions, most notably+-- to generate time deltas. These are mini-wires with the sole purpose+-- of generating these deltas. +newtype Session m s =+ Session {+ stepSession :: m (s, Session m s)+ }+ deriving (Functor) --- | Perform an instant of the given wire as part of a wire session.------ This is a convenience function. You can also construct time deltas--- yourself entirely circumventing 'Session'. This can be useful, if--- there is really no need for an effectful monad.+instance (Applicative m) => Applicative (Session m) where+ pure x = let s = Session (pure (x, s)) in s -stepSession ::- (MonadIO m)- => Wire e m a b -- ^ Wire to step.- -> Session m -- ^ Current session state.- -> a -- ^ Input value.- -> m (Either e b, Wire e m a b, Session m)-stepSession w' (Session update) x' = do- (dt, s) <- update- (mx, w) <- stepWire w' dt x'- mx `seq` return (mx, w, s)+ Session ff <*> Session fx =+ Session $ liftA2 (\(f, sf) (x, sx) -> (f x, sf <*> sx)) ff fx --- | Like 'stepSession', but throws an exception instead of returning an--- 'Either' value.--stepSession_ ::- (MonadIO m)- => WireM m a b -- ^ Wire to step.- -> Session m -- ^ Current session state.- -> a -- ^ Input value.- -> m (b, WireM m a b, Session m)-stepSession_ w' s' x' = do- (mx, w, s) <- stepSession w' s' x'-- let throwM = liftIO . throwIO- emptyErr = toException (userError "empty inhibition signal")- x <- either (throwM . maybe emptyErr id . getLast) return mx+-- | This state delta type denotes time deltas. This is necessary for+-- most FRP applications. - return (x, w, s)+data Timed t s = Timed t s+ deriving (Data, Eq, Foldable, Functor,+ Ord, Read, Show, Traversable, Typeable) +instance (Monoid s, Real t) => HasTime t (Timed t s) where+ dtime (Timed dt _) = dt --- | Like 'stepSession', but for pure wires.+instance (Monoid s, Num t) => Monoid (Timed t s) where+ mempty = Timed 0 mempty -stepSessionP ::- (Monad m)- => Wire e Identity a b -- ^ Wire to step.- -> Session m -- ^ Current session state.- -> a -- ^ Input value.- -> m (Either e b, Wire e Identity a b, Session m)-stepSessionP w' (Session update) !x' = do- (dt, s) <- update- let (mx, w) = stepWireP w' dt x'- mx `seq` return (mx, w, s)+ mappend (Timed dt1 ds1) (Timed dt2 ds2) =+ let dt = dt1 + dt2+ ds = ds1 <> ds2+ in dt `seq` ds `seq` Timed dt ds --- | Like 'stepSessionP', but throws an exception instead of returning an--- 'Either' value.--stepSessionP_ ::- (MonadIO m)- => WireP a b -- ^ Wire to step.- -> Session m -- ^ Current session state.- -> a -- ^ Input value.- -> m (b, WireP a b, Session m)-stepSessionP_ w' s' !x' = do- (mx, w, s) <- stepSessionP w' s' x'+-- | State delta generator for a real time clock. - let throwM = liftIO . throwIO- emptyErr = toException (userError "empty inhibition signal")- x <- either (throwM . maybe emptyErr id . getLast) return mx+clockSession :: (MonadIO m) => Session m (s -> Timed NominalDiffTime s)+clockSession =+ Session $ do+ t0 <- liftIO getCurrentTime+ return (Timed 0, loop t0) - return (x, w, s)+ where+ loop t' =+ Session $ do+ t <- liftIO getCurrentTime+ let dt = diffUTCTime t t'+ dt `seq` return (Timed dt, loop t) --- | @testPrint n int mx@ prints a formatted version of @mx@ to stderr,--- if @n@ is zero. It returns @mod (succ n) int@. Requires @n >= 0@ to--- work properly.------ This function is used to implement the /printing interval/ used in--- 'testWire' and 'testWireM'.+-- | Non-extending version of 'clockSession'. -testPrint :: (Show e) => Int -> Int -> Either e String -> IO Int-testPrint n' int mx = do- let n = let nn = n' + 1 in- if nn >= int then 0 else nn- when (n' == 0) $ do- hPutStr stderr "\r\027[K"- hPutStr stderr (either (("(I) " ++) . show) id mx)- hFlush stderr- n `seq` return n+clockSession_ :: (Applicative m, MonadIO m) => Session m (Timed NominalDiffTime ())+clockSession_ = clockSession <*> pure () --- | Runs the given wire continuously and prints its result to stderr.--- Runs forever until an exception is raised.------ The /printing interval/ sets the instants/printing ratio. The higher--- this value, the less often the output is printed. Examples: 1000--- means to print at every 1000-th instant, 1 means to print at every--- instant.+-- | State delta generator for a simple counting clock. Denotes a fixed+-- framerate. This is likely more useful than 'clockSession' for+-- simulations and real-time games. -testWire ::- forall a b e m. (MonadIO m, Show e)- => Int -- ^ Printing interval.- -> Int -- ^ 'threadDelay' between instants.- -> m a -- ^ Input generator.- -> Session m -- ^ Initial session value.- -> Wire e m a String -- ^ Wire to test.- -> m b-testWire int delay getInput = loop 0- where- loop :: Int -> Session m -> Wire e m a String -> m b- loop n' s' w' = do- x' <- getInput- (mx, w, s) <- stepSession w' s' x'- n <- mx `seq` liftIO (testPrint n' int mx)- when (delay > 0) (liftIO (threadDelay delay))- loop n s w+countSession ::+ (Applicative m)+ => t -- ^ Increment size.+ -> Session m (s -> Timed t s)+countSession dt =+ let loop = Session (pure (Timed dt, loop))+ in loop --- | Like 'testWire', but for pure wires.+-- | Non-extending version of 'countSession'. -testWireP ::- forall a b e m. (MonadIO m, Show e)- => Int -- ^ Printing interval.- -> Int -- ^ 'threadDelay' between instants.- -> m a -- ^ Input generator.- -> Session m -- ^ Initial session value.- -> Wire e Identity a String -- ^ Wire to test.- -> m b-testWireP int delay getInput = loop 0- where- loop :: Int -> Session m -> Wire e Identity a String -> m b- loop n' s' w' = do- x' <- getInput- (mx, w, s) <- stepSessionP w' s' x'- n <- mx `seq` liftIO (testPrint n' int mx)- when (delay > 0) (liftIO (threadDelay delay))- loop n s w+countSession_ :: (Applicative m, MonadIO m) => t -> Session m (Timed t ())+countSession_ dt = countSession dt <*> pure ()
+ Control/Wire/Switch.hs view
@@ -0,0 +1,250 @@+-- |+-- Module: Control.Wire.Switch+-- Copyright: (c) 2013 Ertugrul Soeylemez+-- License: BSD3+-- Maintainer: Ertugrul Soeylemez <es@ertes.de>++module Control.Wire.Switch+ ( -- * Simple switching+ (-->),++ -- * Context switching+ modes,++ -- * Event-based switching+ -- ** Intrinsic+ switch,+ dSwitch,+ -- ** Intrinsic continuable+ kSwitch,+ dkSwitch,+ -- ** Extrinsic+ rSwitch,+ drSwitch,+ -- ** Extrinsic continuable+ krSwitch,+ dkrSwitch+ )+ where++import qualified Data.Map as M+import Control.Applicative+import Control.Arrow+import Control.Monad+import Control.Wire.Core+import Control.Wire.Event+import Control.Wire.Unsafe.Event+import Data.Monoid+++-- | Acts like the first wire until it inhibits, then switches to the+-- second wire. Infixr 1.+--+-- * Depends: like current wire.+--+-- * Inhibits: after switching like the second wire.+--+-- * Switch: now.++(-->) :: (Monad m) => Wire s e m a b -> Wire s e m a b -> Wire s e m a b+w1' --> w2' =+ WGen $ \ds mx' -> do+ (mx, w1) <- stepWire w1' ds mx'+ case mx of+ Left _ | Right _ <- mx' -> stepWire w2' ds mx'+ _ -> mx `seq` return (mx, w1 --> w2')++infixr 1 -->+++-- | Intrinsic continuable switch: Delayed version of 'kSwitch'.+--+-- * Inhibits: like the first argument wire, like the new wire after+-- switch. Inhibition of the second argument wire is ignored.+--+-- * Switch: once, after now, restart state.++dkSwitch ::+ (Monad m)+ => Wire s e m a b+ -> Wire s e m (a, b) (Event (Wire s e m a b -> Wire s e m a b))+ -> Wire s e m a b+dkSwitch w1' w2' =+ WGen $ \ds mx' -> do+ (mx, w1) <- stepWire w1' ds mx'+ (mev, w2) <- stepWire w2' ds (liftA2 (,) mx' mx)+ let w | Right (Event sw) <- mev = sw w1+ | otherwise = dkSwitch w1 w2+ return (mx, w)+++-- | Extrinsic switch: Delayed version of 'rSwitch'.+--+-- * Inhibits: like the current wire.+--+-- * Switch: recurrent, after now, restart state.++drSwitch ::+ (Monad m)+ => Wire s e m a b+ -> Wire s e m (a, Event (Wire s e m a b)) b+drSwitch w' =+ WGen $ \ds mx' ->+ let nw w | Right (_, Event w1) <- mx' = w1+ | otherwise = w+ in liftM (second (drSwitch . nw)) (stepWire w' ds (fmap fst mx'))+++-- | Intrinsic switch: Delayed version of 'switch'.+--+-- * Inhibits: like argument wire until switch, then like the new wire.+--+-- * Switch: once, after now, restart state.++dSwitch ::+ (Monad m)+ => Wire s e m a (b, Event (Wire s e m a b))+ -> Wire s e m a b+dSwitch w' =+ WGen $ \ds mx' -> do+ (mx, w) <- stepWire w' ds mx'+ let nw | Right (_, Event w1) <- mx = w1+ | otherwise = dSwitch w+ return (fmap fst mx, nw)+++-- | Extrinsic continuable switch. Delayed version of 'krSwitch'.+--+-- * Inhibits: like the current wire.+--+-- * Switch: recurrent, after now, restart state.++dkrSwitch ::+ (Monad m)+ => Wire s e m a b+ -> Wire s e m (a, Event (Wire s e m a b -> Wire s e m a b)) b+dkrSwitch w' =+ WGen $ \ds mx' ->+ let nw w | Right (_, Event f) <- mx' = f w+ | otherwise = w+ in liftM (second (dkrSwitch . nw)) (stepWire w' ds (fmap fst mx'))+++-- | Intrinsic continuable switch: @kSwitch w1 w2@ starts with @w1@.+-- Its signal is received by @w2@, which may choose to switch to a new+-- wire. Passes the wire we are switching away from to the new wire,+-- such that it may be reused in it.+--+-- * Inhibits: like the first argument wire, like the new wire after+-- switch. Inhibition of the second argument wire is ignored.+--+-- * Switch: once, now, restart state.++kSwitch ::+ (Monad m, Monoid s)+ => Wire s e m a b+ -> Wire s e m (a, b) (Event (Wire s e m a b -> Wire s e m a b))+ -> Wire s e m a b+kSwitch w1' w2' =+ WGen $ \ds mx' -> do+ (mx, w1) <- stepWire w1' ds mx'+ (mev, w2) <- stepWire w2' ds (liftA2 (,) mx' mx)+ case mev of+ Right (Event sw) -> stepWire (sw w1) mempty mx'+ _ -> return (mx, kSwitch w1 w2)+++-- | Extrinsic continuable switch. This switch works like 'rSwitch',+-- except that it passes the wire we are switching away from to the new+-- wire.+--+-- * Inhibits: like the current wire.+--+-- * Switch: recurrent, now, restart state.++krSwitch ::+ (Monad m)+ => Wire s e m a b+ -> Wire s e m (a, Event (Wire s e m a b -> Wire s e m a b)) b+krSwitch w'' =+ WGen $ \ds mx' ->+ let w' | Right (_, Event f) <- mx' = f w''+ | otherwise = w''+ in liftM (second krSwitch) (stepWire w' ds (fmap fst mx'))+++-- | Route the left input signal based on the current mode. The right+-- input signal can be used to change the current mode. When switching+-- away from a mode and then switching back to it, it will be resumed.+-- Freezes time during inactivity.+--+-- * Complexity: O(n * log n) space, O(log n) lookup time on switch wrt+-- number of started, inactive modes.+--+-- * Depends: like currently active wire (left), now (right).+--+-- * Inhibits: when active wire inhibits.+--+-- * Switch: now on mode change.++modes ::+ (Monad m, Ord k)+ => k -- ^ Initial mode.+ -> (k -> Wire s e m a b) -- ^ Select wire for given mode.+ -> Wire s e m (a, Event k) b+modes m0 select = loop M.empty m0 (select m0)+ where+ loop ms' m' w'' =+ WGen $ \ds mxev' ->+ case mxev' of+ Left _ -> do+ (mx, w) <- stepWire w'' ds (fmap fst mxev')+ return (mx, loop ms' m' w)+ Right (x', ev) -> do+ let (ms, m, w') = switch ms' m' w'' ev+ (mx, w) <- stepWire w' ds (Right x')+ return (mx, loop ms m w)++ switch ms' m' w' NoEvent = (ms', m', w')+ switch ms' m' w' (Event m) =+ let ms = M.insert m' w' ms' in+ case M.lookup m ms of+ Nothing -> (ms, m, select m)+ Just w -> (M.delete m ms, m, w)+++-- | Extrinsic switch: Start with the given wire. Each time the input+-- event occurs, switch to the wire it carries.+--+-- * Inhibits: like the current wire.+--+-- * Switch: recurrent, now, restart state.++rSwitch ::+ (Monad m)+ => Wire s e m a b+ -> Wire s e m (a, Event (Wire s e m a b)) b+rSwitch w'' =+ WGen $ \ds mx' ->+ let w' | Right (_, Event w1) <- mx' = w1+ | otherwise = w''+ in liftM (second rSwitch) (stepWire w' ds (fmap fst mx'))+++-- | Intrinsic switch: Start with the given wire. As soon as its event+-- occurs, switch to the wire in the event's value.+--+-- * Inhibits: like argument wire until switch, then like the new wire.+--+-- * Switch: once, now, restart state.++switch ::+ (Monad m, Monoid s)+ => Wire s e m a (b, Event (Wire s e m a b))+ -> Wire s e m a b+switch w' =+ WGen $ \ds mx' -> do+ (mx, w) <- stepWire w' ds mx'+ case mx of+ Right (_, Event w1) -> stepWire w1 mempty mx'+ _ -> return (fmap fst mx, switch w)
+ Control/Wire/Time.hs view
@@ -0,0 +1,38 @@+-- |+-- Module: Control.Wire.Time+-- Copyright: (c) 2013 Ertugrul Soeylemez+-- License: BSD3+-- Maintainer: Ertugrul Soeylemez <es@ertes.de>++module Control.Wire.Time+ ( -- * Time wires+ time,+ timeF,+ timeFrom+ )+ where++import Control.Wire.Core+import Control.Wire.Session+++-- | Local time starting from zero.++time :: (HasTime t s) => Wire s e m a t+time = timeFrom 0+++-- | Local time starting from zero, converted to your favorite+-- fractional type.++timeF :: (Fractional b, HasTime t s, Monad m) => Wire s e m a b+timeF = fmap realToFrac time+++-- | Local time starting from the given value.++timeFrom :: (HasTime t s) => t -> Wire s e m a t+timeFrom t' =+ mkSF $ \ds _ ->+ let t = t' + dtime ds+ in t `seq` (t, timeFrom t)
− Control/Wire/TimedMap.hs
@@ -1,118 +0,0 @@--- |--- Module: Control.Wire.TimedMap--- Copyright: (c) 2012 Ertugrul Soeylemez--- License: BSD3--- Maintainer: Ertugrul Soeylemez <es@ertes.de>------ Timed maps for efficient cleanups in the context wires.--module Control.Wire.TimedMap- ( -- * Timed maps- TimedMap,- -- * Queries- findWithDefault,- lookup,- -- * Construction- empty,- -- * Insertion- insert,- -- * Deletion- cleanup,- cut,- delete- )- where--import qualified Data.Map as M-import qualified Data.Set as S-import Control.Monad-import Data.Data-import Data.Map (Map)-import Data.Set (Set)-import Prelude hiding (lookup)----- | A timed map is a map with an additional index based on time.--data TimedMap t k a =- TimedMap !(Map k (a, t)) !(Map t (Set k))- deriving (Data, Show, Typeable)----- | Remove all elements older than the given time.--cleanup :: (Ord k, Ord t) => t -> TimedMap t k a -> TimedMap t k a-cleanup t0 (TimedMap mk' mt') = TimedMap mk mt- where- (older', middle, mt) = M.splitLookup t0 mt'- older =- M.fromDistinctAscList .- map (, ()) .- S.toList .- M.foldl' S.union S.empty .- maybe id (M.insert t0) middle $ older'- mk = mk' M.\\ older----- | Remove all but the given number of latest elements.--cut :: (Ord k, Ord t) => Int -> TimedMap t k a -> TimedMap t k a-cut n !tm@(TimedMap mk mt)- | M.size mk > n =- let k = S.findMin . snd . M.findMin $ mt in- cut n (delete k tm)- | otherwise = tm----- | Deletes the given key from the timed map.--delete :: (Ord k, Ord t) => k -> TimedMap t k a -> TimedMap t k a-delete k (TimedMap mk' mt') = TimedMap mk mt- where- mk = M.delete k mk'- mt = case M.lookup k mk' of- Nothing -> mt'- Just (_, t') ->- let alter Nothing = Nothing- alter (Just s') = do- let s = S.delete k s'- guard (not (S.null s))- return s- in M.alter alter t' mt'----- | Like 'lookup', but with a default value, if the key is not in the--- map.--findWithDefault :: (Ord k) => (a, t) -> k -> TimedMap t k a -> (a, t)-findWithDefault def k = maybe def id . lookup k----- | Empty timed map.--empty :: TimedMap t k a-empty = TimedMap M.empty M.empty----- | Insert into the timed map.--insert :: (Ord k, Ord t) => t -> k -> a -> TimedMap t k a -> TimedMap t k a-insert t k x (TimedMap mk' mt') = TimedMap mk mt- where- mk = M.insert k (x, t) mk'- mt = case M.lookup k mk' of- Nothing -> M.insertWith S.union t (S.singleton k) mt'- Just (_, t') ->- let alter Nothing = Nothing- alter (Just s') = do- let s = S.delete k s'- guard (not (S.null s))- return s- in M.insertWith S.union t (S.singleton k) .- M.alter alter t' $ mt'----- | Look up the given key in the timed map.--lookup :: (Ord k) => k -> TimedMap t k a -> Maybe (a, t)-lookup k (TimedMap mk _) = M.lookup k mk
− Control/Wire/Trans.hs
@@ -1,25 +0,0 @@--- |--- Module: Control.Wire.Trans--- Copyright: (c) 2012 Ertugrul Soeylemez--- License: BSD3--- Maintainer: Ertugrul Soeylemez <es@ertes.de>------ Proxy module to all wire combinator modules.--module Control.Wire.Trans- ( -- * Reexports- module Control.Wire.Trans.Combine,- module Control.Wire.Trans.Embed,- module Control.Wire.Trans.Event,- module Control.Wire.Trans.Simple,- module Control.Wire.Trans.Switch,- module Control.Wire.Trans.Time- )- where--import Control.Wire.Trans.Combine-import Control.Wire.Trans.Embed-import Control.Wire.Trans.Event-import Control.Wire.Trans.Simple-import Control.Wire.Trans.Switch-import Control.Wire.Trans.Time
− Control/Wire/Trans/Combine.hs
@@ -1,110 +0,0 @@--- |--- Module: Control.Wire.Trans.Combine--- Copyright: (c) 2012 Ertugrul Soeylemez--- License: BSD3--- Maintainer: Ertugrul Soeylemez <es@ertes.de>------ Wire combinators to manage sets of wires.--module Control.Wire.Trans.Combine- ( -- * Multiplexing- context,- contextLatest,- contextLimit,-- -- * Multicast- multicast- )- where--import qualified Control.Wire.TimedMap as Tm-import qualified Data.Map as M-import qualified Data.Traversable as T-import Control.Wire.TimedMap (TimedMap)-import Control.Wire.Wire-import Data.Map (Map)----- | The argument function turns the input signal into a context. For--- each context the given base wire evolves individually.------ Note: Incorrect usage can lead to a memory leak. Consider using--- 'contextLimit' instead.------ * Complexity: O(n) space, O(log n) time wrt to number of stored--- contexts.------ * Depends: current instant.------ * Inhibits: when the context wire inhibits.--context ::- forall a b m e k. (Monad m, Ord k)- => (a -> k) -- ^ Function to turn the signal into a context.- -> Wire e m a b -- ^ Base wire.- -> Wire e m a b-context key w0 = context' M.empty 0- where- context' :: Map k (Wire e m a b, Time) -> Time -> Wire e m a b- context' !ctxs t' =- mkGen $ \dt' x' -> do- let ctx = key x'- (w', t0) = M.findWithDefault (w0, t') ctx ctxs- t = t' + dt'- dt = t - t0- (mx, w) <- dt `seq` stepWire w' dt x'- return (mx, context' (M.insert ctx (w, t) ctxs) t)----- | Same as 'context', but keeps only the latest given number of--- contexts.--contextLatest ::- (Monad m, Ord k)- => (a -> k) -- ^ Signal to context.- -> Int -- ^ Maximum number of latest wires.- -> Wire e m a b -- ^ Base wire.- -> Wire e m a b-contextLatest key maxWires = contextLimit key (\_ _ -> Tm.cut maxWires)----- | Same as 'context', but applies the given cleanup function to the--- context map at every instant. This can be used to drop older wires.--contextLimit ::- forall a b m e k. (Monad m, Ord k)- => (a -> k) -- ^ Function to turn the signal into a context.- -> (forall w. Int -> Time -> TimedMap Time k w -> TimedMap Time k w)- -- ^ Cleanup function. Receives the current instant number,- -- the current local time and the current map.- -> Wire e m a b -- ^ Base wire.- -> Wire e m a b-contextLimit key uf w0 = context' 0 Tm.empty 0- where- context' :: Int -> TimedMap Time k (Wire e m a b) -> Time -> Wire e m a b- context' !n !ctxs t' =- mkGen $ \dt' x' -> do- let ctx = key x'- (w', t0) = Tm.findWithDefault (w0, t') ctx ctxs- t = t' + dt'- dt = t - t0- (mx, w) <- dt `seq` stepWire w' dt x'- return (mx, context' (n + 1) (uf n t (Tm.insert t ctx w ctxs)) t)----- | Broadcast the input signal to all of the given wires collecting--- their results. Each of the given subwires is evolved individually.------ * Depends: like the most dependent subwire.------ * Inhibits: when any of the subwires inhibits.--multicast ::- (Monad m, T.Traversable f)- => f (Wire e m a b)- -> Wire e m a (f b)-multicast ws' =- mkGen $ \dt x' -> do- res <- T.mapM (\w -> stepWire w dt x') ws'- let resx = T.sequence . fmap (\(mx, w) -> fmap (, w) mx) $ res- return (fmap (fmap fst) resx, multicast (fmap snd res))
− Control/Wire/Trans/Embed.hs
@@ -1,47 +0,0 @@--- |--- Module: Control.Wire.Trans.Embed--- Copyright: (c) 2012 Ertugrul Soeylemez--- License: BSD3--- Maintainer: Ertugrul Soeylemez <es@ertes.de>------ Combinators for embedding wires.--module Control.Wire.Trans.Embed- ( -- * Embedding wires- embed- )- where--import Control.Wire.Wire----- | Performs the argument wire with the input time delta. It is--- stepped often enough to catch up with the main wire. The individual--- results are combined as given by the fold (second and third--- argument).------ * Complexity: O(n) time wrt stepping the subwire, where n is the--- number of times the subwire is stepped.------ * Depends: like argument wire, if stepped.------ * Inhibits: When the fold results in a 'Left'.--embed ::- (Monad m)- => (a -> Time) -- ^ Time delta for the subwire.- -> (Either e c -> Either e b -> Either e c) -- ^ Folding function.- -> Either e c -- ^ Fold base value.- -> Wire e m a b -- ^ Subwire to step.- -> Wire e m a c-embed delta fold z = embed' 0- where- embed' rdt w0 =- mkGen $ \dt x' ->- let idt = delta x'- loop odt r w'- | odt >= idt = do- (mx, w) <- stepWire w' idt x'- loop (odt - idt) (fold r mx) w- | otherwise = return (r, embed' odt w')- in loop (rdt + dt) z w0
− Control/Wire/Trans/Event.hs
@@ -1,211 +0,0 @@--- |--- Module: Control.Wire.Trans.Event--- Copyright: (c) 2012 Ertugrul Soeylemez--- License: BSD3--- Maintainer: Ertugrul Soeylemez <es@ertes.de>------ Event-related wire combinators.--module Control.Wire.Trans.Event- ( -- * Combinators- eitherE,- (<||>),-- -- * Holding events- hold,- hold_,- holdFor,- holdForI,-- -- * Inhibition- (<!>),- event,- exhibit,- gotEvent,- notE- )- where--import Control.Arrow-import Control.Category-import Control.Wire.Types-import Control.Wire.Wire-import Data.Monoid hiding ((<>))-import Data.Semigroup-import Prelude hiding ((.), id)----- | Try both wires combining their results with the given functions.------ * Like argument wires.------ * Inhibits: when both wires inhibit.--eitherE ::- (Monad m, Monoid e)- => (b1 -> b) -- ^ Only left.- -> (b2 -> b) -- ^ Only right.- -> (b1 -> b2 -> b) -- ^ Both.- -> Wire e m a b1 -- ^ First wire.- -> Wire e m a b2 -- ^ Second wire.- -> Wire e m a b-eitherE left right both = eitherE'- where- eitherE' w1' w2' =- mkGen $ \dt x' -> do- (mx1, w1) <- stepWire w1' dt x'- (mx2, w2) <- stepWire w2' dt x'- let res =- case (mx1, mx2) of- (Left ex1, Left ex2) -> Left (mappend ex1 ex2)- (Right x1, Right x2) -> Right (both x1 x2)- (Right x1, _) -> Right (left x1)- (_, Right x2) -> Right (right x2)- return (res, eitherE' w1 w2)----- | Semigroup version of 'eitherE'.--(<||>) ::- (Monad m, Monoid e, Semigroup b)- => Wire e m a b- -> Wire e m a b- -> Wire e m a b-(<||>) = eitherE id id (<>)----- | If the argument wire inhibits, inhibit with the given exception--- instead.------ * Depends: like argument wire.------ * Inhibits: like argument wire.--(<!>) :: (Monad m) => Wire e m a b -> e -> Wire e m a b-w <!> ex = mapOutput (either (Left . const ex) Right) w----- | Prevent a wire from inhibiting. Instead produce a signal wrapped--- in 'Maybe'.------ Note: You probably shouldn't use this function.------ * Depends: like argument wire.--event :: (Monad m) => Wire e m a b -> Wire e m a (Maybe b)-event = mapOutput (Right . either (const Nothing) Just)----- | Prevent a wire from inhibiting. Instead produce the inhibition--- value.------ Note: You probably shouldn't use this function.------ * Depends: like argument wire.--exhibit :: (Monad m) => Wire e m a b -> Wire e m a (Either e b)-exhibit = mapOutput Right----- | Prevent a wire from inhibiting. Instead produce 'False', if the--- wire inhibited.------ Note: You probably shouldn't use this function.------ * Depends: like argument wire.--gotEvent :: (Monad m) => Wire e m a b -> Wire e m a Bool-gotEvent = mapOutput (Right . either (const False) (const True))----- | Hold the latest event. Produces the last produced value starting--- with the given one.------ * Depends: like argument wire.--hold :: (Monad m) => b -> Wire e m a b -> Wire e m a b-hold x0 w' =- mkGen $ \dt x' -> do- (mx, w) <- stepWire w' dt x'- case mx of- Left _ -> return (Right x0, hold x0 w)- Right x -> return (Right x, hold x w)----- | Hold the event. Once the argument wire produces the produced value--- is held until the argument wire produces again.------ * Depends: like argument wire.------ * Inhibits: until the argument wire produces for the first time.--hold_ :: (Monad m) => Wire e m a b -> Wire e m a b-hold_ w' =- mkGen $ \dt x' -> do- (mx, w) <- stepWire w' dt x'- return (mx, either (const hold_) hold mx w)----- | Hold the event for the given amount of time. When the argument--- wire produces, the produced value is kept for the given amount of--- time. If the wire produces again while another value is kept, the--- new value takes precedence.------ * Depends: like argument wire.------ * Inhibits: as described.--holdFor :: (Monad m) => Time -> Wire e m a b -> Wire e m a b-holdFor t0 w = hold' . exhibit w- where- hold' =- mkPure $ \_ mx ->- case mx of- Left _ -> (mx, hold')- Right x -> (mx, hold'' t0 x)-- hold'' t' x' =- mkPure $ \dt mx ->- let t = t' - dt in- case mx of- Left _- | t > 0 -> (Right x', hold'' t x')- | otherwise -> (mx, hold')- Right x -> (mx, hold'' t0 x)----- | Hold the event for the given number of instances. When the--- argument wire produces, the produced value is kept for the given--- number of instances. If the wire produces again while another value--- is kept, the new value takes precedence.------ * Depends: like argument wire.------ * Inhibits: as described.--holdForI :: (Monad m) => Int -> Wire e m a b -> Wire e m a b-holdForI t0 w = hold' . exhibit w- where- hold' = mkPure $ \_ -> id &&& either (const hold') (hold'' t0)-- hold'' t x'- | t <= 0 = hold'- | otherwise =- mkPure $ \_ mx ->- case mx of- Left _ -> (Right x', hold'' (t - 1) x')- Right x -> (mx, hold'' t0 x)----- | Act like the identity wire, if the argument wire inhibits.--- Inhibit, if the argument wire produces.------ * Depends: like argument wire.------ * Inhibits: when argument wire produces.--notE :: (Monad m, Monoid e) => Event e m a -> Event e m a-notE w' =- mkGen $ \dt x' -> do- (mx, w) <- stepWire w' dt x'- return (either (const $ Right x') (const $ Left mempty) mx, notE w)
− Control/Wire/Trans/Simple.hs
@@ -1,49 +0,0 @@--- |--- Module: Control.Wire.Trans.Simple--- Copyright: (c) 2012 Ertugrul Soeylemez--- License: BSD3--- Maintainer: Ertugrul Soeylemez <es@ertes.de>------ Basic wire combinators.--module Control.Wire.Trans.Simple- ( -- * Predicate-based- ifW- )- where--import Control.Arrow-import Control.Monad-import Control.Wire.Wire-import Data.Monoid-import Prelude hiding ((.), id)----- | The wire @ifW p x y@ acts like @x@, when the predicate @p@ is true,--- otherwise @y@.------ * Complexity: like the predicate and the chosen wire.------ * Depends: like the predicate and the chosen wire.------ * Inhibits: when the predicate or the chosen wire inhibits.--ifW ::- (Monad m, Monoid e)- => Wire e m a Bool -- ^ Predicate.- -> Wire e m a b -- ^ If true.- -> Wire e m a b -- ^ If false.- -> Wire e m a b-ifW = ifW' 0 0- where- ifW' !tx !ty wp' wx' wy' =- mkGen $ \dt x' -> do- (mb, wp) <- stepWire wp' dt x'- case mb of- Left ex -> return (Left ex, ifW' (tx + dt) (ty + dt) wp wx' wy')- Right b ->- if b- then liftM (second (\wx -> ifW' 0 (ty + dt) wp wx wy')) $- stepWire wx' (tx + dt) x'- else liftM (second (ifW' (tx + dt) 0 wp wx')) $- stepWire wy' (ty + dt) x'
− Control/Wire/Trans/Switch.hs
@@ -1,101 +0,0 @@--- |--- Module: Control.Wire.Trans.Switch--- Copyright: (c) 2012 Ertugrul Soeylemez--- License: BSD3--- Maintainer: Ertugrul Soeylemez <es@ertes.de>------ Switching combinators. Notice that these combinators restart time--- when switching.--module Control.Wire.Trans.Switch- ( -- * Simple switching- andThen,- switch,- switchBy,- (-->)- )- where--import Control.Arrow-import Control.Monad-import Control.Wire.Wire----- | Infix variant of 'andThen'.------ This operator is right-associative with precedence 1.--(-->) :: (Monad m) => Wire e m a b -> Wire e m a b -> Wire e m a b-(-->) = andThen--infixr 1 -->----- | Behaves like the first wire until it inhibits. Switches to the--- second wire as soon as the first one inhibits.------ The @`andThen`@ operator is right-associative with precedence 1.------ * Depends: like currently active wire.------ * Inhibits: when switched to second wire and that one inhibits.------ * Time: switching restarts time.--andThen ::- (Monad m)- => Wire e m a b -- ^ Wire to start with.- -> Wire e m a b -- ^ Wire to switch into.- -> Wire e m a b-andThen w1' w2' =- mkGen $ \dt x' -> do- (mx, w1) <- stepWire w1' dt x'- case mx of- Left _ -> stepWire w2' dt x'- Right _ -> return (mx, andThen w1 w2')--infixr 1 `andThen`----- | If the first argument wire produces a wire, switch to it--- immediately. If not, evolve the current wire. The second argument--- wire is the initial wire.------ * Depends: like event wire and the currently active wire.------ * Inhibits: when the currently active wire inhibits.------ * Time: switching restarts time.--switch ::- (Monad m)- => Wire e m a (Wire e m a b) -- ^ Produces a wire to switch into.- -> Wire e m a b -- ^ Initial wire.- -> Wire e m a b-switch wnew' w0 =- mkGen $ \dt x' -> do- (w', wnew) <- liftM (first (either (const w0) id)) (stepWire wnew' dt x')- (mx, w) <- stepWire w' dt x'- return (mx, switch wnew w)----- | Whenever the given wire inhibits, a new wire is constructed using--- the given function.------ * Depends: like currently active wire.------ * Time: switching restarts time.--switchBy ::- (Monad m)- => (e' -> Wire e' m a b) -- ^ Wire selection function.- -> Wire e' m a b -- ^ Initial wire.- -> Wire e m a b-switchBy new w0 =- mkGen $ \dt x' ->- let select w' = do- (mx, w) <- stepWire w' dt x'- case mx of- Left ex -> select (new ex)- Right x -> return (Right x, switchBy new w)- in select w0
− Control/Wire/Trans/Time.hs
@@ -1,31 +0,0 @@--- |--- Module: Control.Wire.Trans.Time--- Copyright: (c) 2012 Ertugrul Soeylemez--- License: BSD3--- Maintainer: Ertugrul Soeylemez <es@ertes.de>------ Time-related wire combinators.--module Control.Wire.Trans.Time- ( -- * Local time- mapTime- )- where--import Control.Arrow-import Control.Monad-import Control.Wire.Wire----- | Maps the given function over the time deltas for the given wire.------ * Complexity: like argument wire.------ * Depends: like argument wire.------ * Inhibits: like argument wire.--mapTime :: (Monad m) => (Time -> Time) -> Wire e m a b -> Wire e m a b-mapTime f w' =- mkGen $ \dt ->- liftM (second (mapTime f)) . stepWire w' (f dt)
− Control/Wire/Types.hs
@@ -1,161 +0,0 @@--- |--- Module: Control.Wire.Types--- Copyright: (c) 2012 Ertugrul Soeylemez--- License: BSD3--- Maintainer: Ertugrul Soeylemez <es@ertes.de>------ Types used in Netwire. Most notably this module implements the--- instances for the various reactive classes.--module Control.Wire.Types- ( -- * Convenient type aliases- LastException,- -- ** Events- Event,- EventM,- EventP,- -- ** Wires- WireM,- WireP,-- -- * Type-related utilities- as,- inAs,- inLike,- like,- outAs,- outLike,- -- ** Predefined proxies- pDouble,- pFloat,- pInt,- pInteger,- pString- )- where--import Control.Category-import Control.Exception (SomeException)-import Control.Monad-import Control.Monad.Identity-import Control.Wire.Wire-import Data.Monoid-import Data.Proxy-import Prelude hiding ((.), id)----- | Event wires are wires that act like identity wires, but may inhibit--- depending on whether a certain event has occurred.--type Event e m a = Wire e m a a----- | 'WireP' equivalent of 'Event'.--type EventP a = WireP a a----- | 'WireM' equivalent of 'Event'.--type EventM m a = WireM m a a----- | Monoid for the last occurred exception.--type LastException = Last SomeException----- | Monadic wires using 'LastException' as the inhibition monoid.--type WireM = Wire LastException----- | Pure wires using 'LastException' as the inhibition monoid.--type WireP = WireM Identity----- | Type-restricted identity wire. This is useful to specify the type--- of a signal.------ * Depends: current instant.--as :: (Monad m) => Proxy a -> Wire e m a a-as _ = id----- | Utility to specify the input type of a wire. The argument is--- ignored. For types with defaulting you might prefer 'inLike'.------ > inAs (Proxy :: Proxy Double) highPeak--inAs :: Proxy a -> w a b -> w a b-inAs = const id----- | Utility to specify the input type of a wire. The first argument is--- ignored. This is useful to make use of defaulting or when writing a--- dummy value is actually shorter.------ > inLike (0 :: Double) highPeak--inLike :: a -> w a b -> w a b-inLike = const id----- | Type-restricted identity wire. This is useful to specify the type--- of a signal. The argument is ignored.------ * Depends: current instant.--like :: (Monad m) => a -> Wire e m a a-like = const id----- | Utility to specify the output type of a wire. The argument is--- ignored. For types with defaulting you might prefer 'outLike'.------ > outAs (Proxy :: Proxy Double) noiseM--outAs :: Proxy b -> w a b -> w a b-outAs = const id----- | Utility to specify the output type of a wire. The first argument--- is ignored. This is useful to make use of defaulting or when writing--- a dummy value is actually shorter.------ > outLike (0 :: Double) noiseM--outLike :: b -> w a b -> w a b-outLike = const id----- | 'Double' proxy for use with 'inAs' or 'outAs'.--pDouble :: Proxy Double-pDouble = Proxy----- | 'Float' proxy for use with 'inAs' or 'outAs'.--pFloat :: Proxy Float-pFloat = Proxy----- | 'Int' proxy for use with 'inAs' or 'outAs'.--pInt :: Proxy Int-pInt = Proxy----- | 'Integer' proxy for use with 'inAs' or 'outAs'.--pInteger :: Proxy Integer-pInteger = Proxy----- | 'String' proxy for use with 'inAs' or 'outAs'.--pString :: Proxy String-pString = Proxy
+ Control/Wire/Unsafe/Event.hs view
@@ -0,0 +1,78 @@+-- |+-- Module: Control.Wire.Unsafe.Event+-- Copyright: (c) 2013 Ertugrul Soeylemez+-- License: BSD3+-- Maintainer: Ertugrul Soeylemez <es@ertes.de>++module Control.Wire.Unsafe.Event+ ( -- * Events+ Event(..),++ -- * Helper functions+ event,+ merge,+ occurred,+ onEventM+ )+ where++import Control.DeepSeq+import Control.Monad+import Control.Wire.Core+import Data.Semigroup+import Data.Typeable+++-- | Denotes a stream of values, each together with time of occurrence.+-- Since 'Event' is commonly used for functional reactive programming it+-- does not define most of the usual instances to protect continuous+-- time and discrete event occurrence semantics.++data Event a = Event a | NoEvent deriving (Typeable)++instance Functor Event where+ fmap f = event NoEvent (Event . f)++instance (Semigroup a) => Monoid (Event a) where+ mempty = NoEvent+ mappend = (<>)++instance (NFData a) => NFData (Event a) where+ rnf (Event x) = rnf x+ rnf NoEvent = ()++instance (Semigroup a) => Semigroup (Event a) where+ (<>) = merge (<>)+++-- | Fold the given event.++event :: b -> (a -> b) -> Event a -> b+event _ j (Event x) = j x+event n _ NoEvent = n+++-- | Merge two events using the given function when both occur at the+-- same time.++merge :: (a -> a -> a) -> Event a -> Event a -> Event a+merge _ NoEvent NoEvent = NoEvent+merge _ (Event x) NoEvent = Event x+merge _ NoEvent (Event y) = Event y+merge f (Event x) (Event y) = Event (f x y)+++-- | Did the given event occur?++occurred :: Event a -> Bool+occurred = event False (const True)+++-- | Each time the given event occurs, perform the given action with the+-- value the event carries. The resulting event carries the result of+-- the action.+--+-- * Depends: now.++onEventM :: (Monad m) => (a -> m b) -> Wire s e m (Event a) (Event b)+onEventM c = mkGen_ $ liftM Right . event (return NoEvent) (liftM Event . c)
− Control/Wire/Wire.hs
@@ -1,376 +0,0 @@--- |--- Module: Control.Wire.Wire--- Copyright: (c) 2012 Ertugrul Soeylemez--- License: BSD3--- Maintainer: Ertugrul Soeylemez <es@ertes.de>------ This is the core module implementing the 'Wire' type.--module Control.Wire.Wire- ( -- * Wires- Wire(..),- Time,- -- ** Constructing wires- mkFix,- mkFixM,- mkGen,- mkPure,- mkState,- mkStateM,- -- ** Simple predefined wires- constant,- identity,- never,- -- ** Simple predefined combinators- cons,- fixW,- mapOutput,-- -- * Stepping- stepWire,- stepWireP- )- where--import qualified Data.Bifunctor as Bi-import Control.Applicative-import Control.Arrow-import Control.Category-import Control.Monad-import Control.Monad.Fix-import Control.Monad.Identity-import Data.AdditiveGroup-import Data.AffineSpace-import Data.Cross-import Data.Monoid-import Data.Profunctor-import Data.String-import Data.VectorSpace-import Prelude hiding ((.), id)----- | Time.--type Time = Double----- | A wire is a signal function from an input value of type @a@ that--- either /produces/ an output value of type @b@ or /inhibits/ with a--- value of type @e@. The underlying monad is @m@.--data Wire e m a b- = WGen (Time -> a -> m (Either e b, Wire e m a b))- | WPure (Time -> a -> (Either e b, Wire e m a b))--instance (AdditiveGroup b, Monad m) => AdditiveGroup (Wire e m a b) where- zeroV = pure zeroV- (^+^) = liftA2 (^+^)- negateV = fmap negateV--instance (AdditiveGroup (Diff b), AffineSpace b, Monad m) => AffineSpace (Wire e m a b) where- type Diff (Wire e m a b) = Wire e m a (Diff b)- (.-.) = liftA2 (.-.)- (.+^) = liftA2 (.+^)--instance (Monad m, Monoid e) => Alternative (Wire e m a) where- empty = mkFix (const . const $ Left mempty)-- (<|>) = loop 0- where- loop !t2 (WPure f1) w2'@(WPure f2) =- mkPure $ \dt x' ->- let (mx1, w1) = f1 dt x' in- case mx1 of- Left ex1 ->- let (mx2, w2) = f2 (t2 + dt) x' in- (Bi.first (mappend ex1) mx2, loop 0 w1 w2)- Right _ -> (mx1, loop (t2 + dt) w1 w2')-- loop !t2 w1' w2' =- mkGen $ \dt x' -> do- (mx1, w1) <- stepWire w1' dt x'- case mx1 of- Left ex1 -> do- (mx2, w2) <- stepWire w2' (t2 + dt) x'- return (Bi.first (mappend ex1) mx2, loop 0 w1 w2)- Right _ -> return (mx1, loop (t2 + dt) w1 w2')--instance (Monad m) => Applicative (Wire e m a) where- pure = constant-- (<*>) = loop 0- where- loop !tx (WPure ff) wx'@(WPure fx) =- mkPure $ \dt x' ->- let (mf, wf) = ff dt x' in- case mf of- Right f ->- let (mx, wx) = fx (tx + dt) x' in- (fmap f mx, loop 0 wf wx)- Left ex -> (Left ex, loop (tx + dt) wf wx')-- loop !tx wf' wx' =- mkGen $ \dt x' -> do- (mf, wf) <- stepWire wf' dt x'- case mf of- Right f -> do- (mx, wx) <- stepWire wx' (tx + dt) x'- return (fmap f mx, loop 0 wf wx)- Left ex -> return (Left ex, loop (tx + dt) wf wx')--instance (Monad m) => Arrow (Wire e m) where- arr f = mkFix (const $ Right . f)- first w = liftA2 (,) (lmap fst w) (arr snd)- second w = liftA2 (,) (arr fst) (lmap snd w)- (&&&) = liftA2 (,)- w1 *** w2 = liftA2 (,) (lmap fst w1) (lmap snd w2)--instance (Monad m) => ArrowChoice (Wire e m) where- (|||) = loop 0 0- where- loop !tl !tr wl' wr' =- mkGen $ \dt ->- either (\x' -> do- (mx, wl) <- stepWire wl' (tl + dt) x'- return (mx, loop 0 (tr + dt) wl wr'))- (\x' -> do- (mx, wr) <- stepWire wr' (tr + dt) x'- return (mx, loop (tl + dt) 0 wl' wr))-- w1 +++ w2 = fmap Left w1 ||| fmap Right w2-- left = loop 0- where- loop !tl wl' =- mkGen $ \dt ->- either (liftM (fmap Left *** loop 0) . stepWire wl' (tl + dt))- (\x -> return (Right (Right x), loop (tl + dt) wl'))-- right = loop 0- where- loop !tr wr' =- mkGen $ \dt ->- either (\x -> return (Right (Left x), loop (tr + dt) wr'))- (liftM (fmap Right *** loop 0) . stepWire wr' (tr + dt))--instance (MonadFix m) => ArrowLoop (Wire e m) where- loop w' =- mkGen $ \dt x' ->- liftM (fmap fst *** loop) .- mfix $ \ ~(mx, _) ->- let feedbackErr = error "Feedback loop broken by inhibition" in- stepWire w' dt (x', either (const feedbackErr) snd mx)--instance (Monad m, Monoid e) => ArrowPlus (Wire e m) where- (<+>) = (<|>)--instance (Monad m, Monoid e) => ArrowZero (Wire e m) where- zeroArrow = empty--instance (Monad m) => Category (Wire e m) where- id = identity-- (.) = loop 0- where- loop !t2 w2'@(WPure f2) (WPure f1) =- mkPure $ \dt x'' ->- let (mx', w1) = f1 dt x'' in- case mx' of- Right x' ->- let (mx, w2) = f2 (t2 + dt) x' in- (mx, loop 0 w2 w1)- Left ex -> (Left ex, loop (t2 + dt) w2' w1)-- loop !t2 w2' w1' =- mkGen $ \dt x'' -> do- (mx', w1) <- stepWire w1' dt x''- case mx' of- Right x' -> do- (mx, w2) <- stepWire w2' (t2 + dt) x'- return (mx, loop 0 w2 w1)- Left ex -> return (Left ex, loop (t2 + dt) w2' w1)--instance (Floating b, Monad m) => Floating (Wire e m a b) where- pi = pure pi- sqrt = fmap sqrt-- (**) = liftA2 (**)- exp = fmap exp- log = fmap log- logBase = liftA2 logBase-- cos = fmap cos; sin = fmap sin; tan = fmap tan- acos = fmap acos; asin = fmap asin; atan = fmap atan-- cosh = fmap cosh; sinh = fmap sinh; tanh = fmap tanh- acosh = fmap acosh; asinh = fmap asinh; atanh = fmap atanh--instance (Fractional b, Monad m) => Fractional (Wire e m a b) where- (/) = liftA2 (/)- fromRational = pure . fromRational- recip = fmap recip--instance (Monad m) => Functor (Wire e m a) where- fmap = mapOutput . fmap--instance (HasCross2 b, Monad m) => HasCross2 (Wire e m a b) where- cross2 = fmap cross2--instance (HasCross3 b, Monad m) => HasCross3 (Wire e m a b) where- cross3 = liftA2 cross3--instance (HasNormal b, Monad m) => HasNormal (Wire e m a b) where- normalVec = fmap normalVec--instance (InnerSpace b, Monad m) => InnerSpace (Wire e m a b) where- (<.>) = liftA2 (<.>)--instance (Monad m, Num b) => Num (Wire e m a b) where- (+) = liftA2 (+)- (-) = liftA2 (-)- (*) = liftA2 (*)-- abs = fmap abs- signum = fmap signum- fromInteger = pure . fromInteger--instance (IsString b, Monad m) => IsString (Wire e m a b) where- fromString = pure . fromString--instance (Monad m, Monoid b) => Monoid (Wire e m a b) where- mempty = pure mempty- mappend = liftA2 mappend--instance (Monad m) => Profunctor (Wire e m) where- lmap f (WPure g) = WPure (\dt -> second (lmap f) . g dt . f)- lmap f (WGen g) = WGen (\dt -> liftM (second (lmap f)) . g dt . f)-- rmap = fmap--instance (Monad m, Read b) => Read (Wire e m a b) where- readsPrec n = map (first pure) . readsPrec n--instance (Monad m, VectorSpace b) => VectorSpace (Wire e m a b) where- type Scalar (Wire e m a b) = Wire e m a (Scalar b)- (*^) = liftA2 (*^)----- | Wire cons. Prepend the given value to the wire's output stream.--- This function is infixr like (':').------ * Depends: like argument wire after the first instant.------ * Inhibits: like argument wire after the first instant.--cons :: b -> Wire e m a b -> Wire e m a b-cons x xs = mkPure (\_ _ -> (Right x, xs))--infixr 5 `cons`----- | Variant of 'pure' without the 'Monad' constraint. Using 'pure' is--- preferable.--constant :: b -> Wire e m a b-constant = mkFix . const . const . Right----- | Convenience combinator for the common case of feedback where you--- ignore the input and produce the feedback value itself with 'loop'.------ * Depends: like looped argument wire.------ * Inhibits: when argument wire inhibits.--fixW :: (MonadFix m) => Wire e m b b -> Wire e m a b-fixW = loop . fmap (\x -> (x, x)) . lmap snd----- | Variant of 'id' without the 'Monad' constraint. Using 'id' is--- preferable.------ * Depends: current instant.--identity :: Wire e m a a-identity = WPure (\_ x -> (Right x, identity))----- | Map the given function over the raw wire output.--mapOutput :: (Monad m) => (Either e b' -> Either e b) -> Wire e m a b' -> Wire e m a b-mapOutput f (WGen g) = WGen (\dt -> liftM (f *** mapOutput f) . g dt)-mapOutput f (WPure g) = WPure (\dt -> (f *** mapOutput f) . g dt)----- | Construct a pure stateless wire from the given function.--mkFix :: (Time -> a -> Either e b) -> Wire e m a b-mkFix f = let w = mkPure (\dt -> (, w) . f dt) in w----- | Construct a stateless effectful wire from the given function.--mkFixM :: (Monad m) => (Time -> a -> m (Either e b)) -> Wire e m a b-mkFixM f = let w = mkGen (\dt -> liftM (, w) . f dt) in w----- | Construct an effectful wire from the given function.--mkGen :: (Time -> a -> m (Either e b, Wire e m a b)) -> Wire e m a b-mkGen = WGen----- | Construct a pure wire from the given function.--mkPure :: (Time -> a -> (Either e b, Wire e m a b)) -> Wire e m a b-mkPure = WPure----- | Construct a pure wire from the given local state transision--- function.--mkState ::- s- -> (Time -> (a, s) -> (Either e b, s))- -> Wire e m a b-mkState s0 f = loop s0- where- loop s' =- mkPure $ \dt x' ->- let (mx, s) = f dt (x', s') in- (mx, loop s)----- | Construct a monadic wire from the given local state transision--- function.--mkStateM ::- (Monad m)- => s- -> (Time -> (a, s) -> m (Either e b, s))- -> Wire e m a b-mkStateM s0 f = loop s0- where- loop s' =- mkGen $ \dt x' -> liftM (second loop) (f dt (x', s'))----- | Variant of 'empty' without the 'Monad' constraint. Using 'empty'--- is preferable.--never :: (Monoid e) => Wire e m a b-never = mkFix . const . const $ Left mempty----- | Perform an instant of the given wire.--stepWire :: (Monad m) => Wire e m a b -> Time -> a -> m (Either e b, Wire e m a b)-stepWire (WGen f) dt = f dt-stepWire (WPure f) dt = return . f dt----- | Perform an instant of the given pure wire.--stepWireP :: Wire e Identity a b -> Time -> a -> (Either e b, Wire e Identity a b)-stepWireP (WGen f) dt = runIdentity . f dt-stepWireP (WPure f) dt = f dt
+ FRP/Netwire.hs view
@@ -0,0 +1,46 @@+-- |+-- Module: FRP.Netwire+-- Copyright: (c) 2013 Ertugrul Soeylemez+-- License: BSD3+-- Maintainer: Ertugrul Soeylemez <es@ertes.de>++module FRP.Netwire+ ( -- * Netwire reexports+ Wire,+ WireP,+ SimpleWire,+ delay, evalWith, force, forceNF,+ module Control.Wire.Event,+ module Control.Wire.Interval,+ module Control.Wire.Run,+ module Control.Wire.Session,+ module Control.Wire.Switch,+ module Control.Wire.Time,++ -- * Additional wires+ module FRP.Netwire.Analyze,+ module FRP.Netwire.Move,+ module FRP.Netwire.Noise,++ -- * External+ module Control.Applicative,+ module Control.Arrow,+ module Control.Category,+ module Data.Semigroup+ )+ where++import Control.Applicative+import Control.Arrow+import Control.Category+import Control.Wire+import Control.Wire.Event+import Control.Wire.Interval+import Control.Wire.Run+import Control.Wire.Session+import Control.Wire.Switch+import Control.Wire.Time+import Data.Semigroup+import FRP.Netwire.Analyze+import FRP.Netwire.Move+import FRP.Netwire.Noise
+ FRP/Netwire/Analyze.hs view
@@ -0,0 +1,310 @@+-- |+-- Module: FRP.Netwire.Analyze+-- Copyright: (c) 2013 Ertugrul Soeylemez+-- License: BSD3+-- Maintainer: Ertugrul Soeylemez <es@ertes.de>++module FRP.Netwire.Analyze+ ( -- * Linear graphs+ lAvg,+ lGraph,+ lGraphN,++ -- * Staircase graphs+ sAvg,+ sGraph,+ sGraphN,++ -- * Peaks+ highPeak,+ highPeakBy,+ lowPeak,+ lowPeakBy,++ -- * Debug+ avgFps,+ framerate+ )+ where++import qualified FRP.Netwire.Utils.Timeline as Tl+import qualified Data.Foldable as F+import qualified Data.Sequence as Seq+import Control.Wire+import Prelude hiding ((.), id)+++-- | Average framerate over the last given number of samples. One+-- important thing to note is that the value of this wire will generally+-- disagree with 'sAvg' composed with 'framerate'. This is expected,+-- because this wire simply calculates the arithmetic mean, whereas+-- 'sAvg' will actually integrate the framerate graph.+--+-- Note: This wire is for debugging purposes only, because it exposes+-- discrete time. Do not taint your application with discrete time.+--+-- * Complexity: O(n) time and space wrt number of samples.++avgFps ::+ (RealFloat b, HasTime t s)+ => Int -- ^ Number of samples.+ -> Wire s e m a b+avgFps int | int < 1 = error "avgFps: Non-positive number of samples"+avgFps int = loop Seq.empty+ where+ intf = fromIntegral int+ afps = (/ intf) . F.foldl' (+) 0++ loop ss' =+ mkSF $ \ds _ ->+ let fps = recip . realToFrac . dtime $ ds+ ss = Seq.take int (fps Seq.<| ss')+ in if isInfinite fps+ then (afps ss', loop ss')+ else ss `seq` (afps ss, loop ss)+++-- | Current framerate.+--+-- Note: This wire is for debugging purposes only, because it exposes+-- discrete time. Do not taint your application with discrete time.+--+-- * Inhibits: when the clock stopped ticking.++framerate ::+ (Eq b, Fractional b, HasTime t s, Monoid e)+ => Wire s e m a b+framerate =+ mkPure $ \ds _ ->+ let dt = realToFrac (dtime ds)+ in (if dt == 0 then Left mempty else Right (recip dt), framerate)+++-- | High peak.+--+-- * Depends: now.++highPeak :: (Ord a) => Wire s e m a a+highPeak = highPeakBy compare+++-- | High peak with respect to the given comparison function.+--+-- * Depends: now.++highPeakBy :: (a -> a -> Ordering) -> Wire s e m a a+highPeakBy = peakBy GT+++-- | Calculate the average of the signal over the given interval (from+-- now). This is done by calculating the integral of the corresponding+-- linearly interpolated graph and dividing it by the interval length.+-- See 'Tl.linAvg' for details.+--+-- Linear interpolation can be slow. If you don't need it, you can use+-- the staircase variant 'sAvg'.+--+-- Example: @lAvg 2@+--+-- * Complexity: O(s) space, O(s) time wrt number of samples in the+-- interval.+--+-- * Depends: now.++lAvg ::+ (Fractional a, Fractional t, HasTime t s)+ => t -- ^ Interval size.+ -> Wire s e m a a+lAvg int =+ mkSF $ \ds x ->+ let t = dtime ds in+ (x, loop t (Tl.singleton t x))++ where+ loop t' tl' =+ mkSF $ \ds x ->+ let t = t' + dtime ds+ t0 = t - int+ tl = Tl.linCutL t0 (Tl.insert t x tl')+ a = Tl.linAvg t0 t tl+ in (a, loop t tl)+++-- | Produce a linearly interpolated graph for the given points in time,+-- where the magnitudes of the points are distances from /now/.+--+-- Linear interpolation can be slow. If you don't need it, you can use+-- the faster staircase variant 'sGraph'.+--+-- Example: @lGraph [0, 1, 2]@ will output the interpolated inputs at+-- /now/, one second before now and two seconds before now.+--+-- * Complexity: O(s) space, O(n * log s) time, where s = number of+-- samples in the interval, n = number of requested data points.+--+-- * Depends: now.++lGraph ::+ (Fractional a, Fractional t, HasTime t s)+ => [t] -- ^ Data points to produce.+ -> Wire s e m a [a]+lGraph qts =+ mkSF $ \ds x ->+ let t = dtime ds in+ (x <$ qts, loop t (Tl.singleton t x))++ where+ earliest = maximum (map abs qts)++ loop t' tl' =+ mkSF $ \ds x ->+ let t = t' + dtime ds+ tl = Tl.linCutL (t - earliest) (Tl.insert t x tl')+ ps = map (\qt -> Tl.linLookup (t - abs qt) tl) qts+ in (ps, loop t tl)+++-- | Graph the given interval from now with the given number of evenly+-- distributed points in time. Convenience interface to 'lGraph'.+--+-- Linear interpolation can be slow. If you don't need it, you can use+-- the faster staircase variant 'sGraphN'.+--+-- * Complexity: O(s) space, O(n * log s) time, where s = number of+-- samples in the interval, n = number of requested data points.+--+-- * Depends: now.++lGraphN ::+ (Fractional a, Fractional t, HasTime t s)+ => t -- ^ Interval to graph from now.+ -> Int -- ^ Number of data points to produce.+ -> Wire s e m a [a]+lGraphN int n+ | int <= 0 = error "lGraphN: Non-positive interval"+ | n <= 0 = error "lGraphN: Non-positive number of data points"+lGraphN int n =+ let n1 = n - 1+ f qt = realToFrac int * fromIntegral qt / fromIntegral n1+ in lGraph (map f [0..n1])+++-- | Low peak.+--+-- * Depends: now.++lowPeak :: (Ord a) => Wire s e m a a+lowPeak = lowPeakBy compare+++-- | Low peak with respect to the given comparison function.+--+-- * Depends: now.++lowPeakBy :: (a -> a -> Ordering) -> Wire s e m a a+lowPeakBy = peakBy LT+++-- | Given peak with respect to the given comparison function.++peakBy ::+ (Eq o)+ => o -- ^ This ordering means the first argument is larger.+ -> (a -> a -> o) -- ^ Compare two elements.+ -> Wire s e m a a+peakBy o comp = mkSFN $ \x -> (x, loop x)+ where+ loop x' =+ mkSFN $ \x ->+ id &&& loop $+ if comp x x' == o then x else x'+++-- | Calculate the average of the signal over the given interval (from+-- now). This is done by calculating the integral of the corresponding+-- staircase graph and dividing it by the interval length. See+-- 'Tl.scAvg' for details.+--+-- See also 'lAvg'.+--+-- Example: @sAvg 2@+--+-- * Complexity: O(s) space, O(s) time wrt number of samples in the+-- interval.+--+-- * Depends: now.++sAvg ::+ (Fractional a, Fractional t, HasTime t s)+ => t -- ^ Interval size.+ -> Wire s e m a a+sAvg int =+ mkSF $ \ds x ->+ let t = dtime ds in+ (x, loop t (Tl.singleton t x))++ where+ loop t' tl' =+ mkSF $ \ds x ->+ let t = t' + dtime ds+ t0 = t - int+ tl = Tl.scCutL t0 (Tl.insert t x tl')+ a = Tl.scAvg t0 t tl+ in (a, loop t tl)+++-- | Produce a staircase graph for the given points in time, where the+-- magnitudes of the points are distances from /now/.+--+-- See also 'lGraph'.+--+-- Example: @sGraph [0, 1, 2]@ will output the inputs at /now/, one+-- second before now and two seconds before now.+--+-- * Complexity: O(s) space, O(n * log s) time, where s = number of+-- samples in the interval, n = number of requested data points.+--+-- * Depends: now.++sGraph ::+ (Fractional t, HasTime t s)+ => [t] -- ^ Data points to produce.+ -> Wire s e m a [a]+sGraph qts =+ mkSF $ \ds x ->+ let t = dtime ds in+ (x <$ qts, loop t (Tl.singleton t x))++ where+ earliest = maximum (map abs qts)++ loop t' tl' =+ mkSF $ \ds x ->+ let t = t' + dtime ds+ tl = Tl.scCutL (t - earliest) (Tl.insert t x tl')+ ps = map (\qt -> Tl.scLookup (t - abs qt) tl) qts+ in (ps, loop t tl)+++-- | Graph the given interval from now with the given number of evenly+-- distributed points in time. Convenience interface to 'sGraph'.+--+-- See also 'lGraphN'.+--+-- * Complexity: O(s) space, O(n * log s) time, where s = number of+-- samples in the interval, n = number of requested data points.+--+-- * Depends: now.++sGraphN ::+ (Fractional t, HasTime t s)+ => t -- ^ Interval to graph from now.+ -> Int -- ^ Number of data points to produce.+ -> Wire s e m a [a]+sGraphN int n+ | int <= 0 = error "sGraphN: Non-positive interval"+ | n <= 0 = error "sGraphN: Non-positive number of data points"+sGraphN int n =+ let n1 = n - 1+ f qt = realToFrac int * fromIntegral qt / fromIntegral n1+ in sGraph (map f [0..n1])
+ FRP/Netwire/Move.hs view
@@ -0,0 +1,77 @@+-- |+-- Module: FRP.Netwire.Move+-- Copyright: (c) 2013 Ertugrul Soeylemez+-- License: BSD3+-- Maintainer: Ertugrul Soeylemez <es@ertes.de>++module FRP.Netwire.Move+ ( -- * Calculus+ derivative,+ integral,+ integralWith+ )+ where++import Control.Wire+++-- | Time derivative of the input signal.+--+-- * Depends: now.+--+-- * Inhibits: at singularities.++derivative ::+ (RealFloat a, HasTime t s, Monoid e)+ => Wire s e m a a+derivative = mkPure $ \_ x -> (Left mempty, loop x)+ where+ loop x' =+ mkPure $ \ds x ->+ let dt = realToFrac (dtime ds)+ dx = (x - x') / dt+ mdx | isNaN dx = Right 0+ | isInfinite dx = Left mempty+ | otherwise = Right dx+ in mdx `seq` (mdx, loop x)+++-- | Integrate the input signal over time.+--+-- * Depends: before now.++integral ::+ (Fractional a, HasTime t s)+ => a -- ^ Integration constant (aka start value).+ -> Wire s e m a a+integral x' =+ mkPure $ \ds dx ->+ let dt = realToFrac (dtime ds)+ in x' `seq` (Right x', integral (x' + dt*dx))+++-- | Integrate the left input signal over time, but apply the given+-- correction function to it. This can be used to implement collision+-- detection/reaction.+--+-- The right signal of type @w@ is the /world value/. It is just passed+-- to the correction function for reference and is not used otherwise.+--+-- The correction function must be idempotent with respect to the world+-- value: @f w (f w x) = f w x@. This is necessary and sufficient to+-- protect time continuity.+--+-- * Depends: before now.++integralWith ::+ (Fractional a, HasTime t s)+ => (w -> a -> a) -- ^ Correction function.+ -> a -- ^ Integration constant (aka start value).+ -> Wire s e m (a, w) a+integralWith correct = loop+ where+ loop x' =+ mkPure $ \ds (dx, w) ->+ let dt = realToFrac (dtime ds)+ x = correct w (x' + dt*dx)+ in x' `seq` (Right x', loop x)
+ FRP/Netwire/Noise.hs view
@@ -0,0 +1,98 @@+-- |+-- Module: FRP.Netwire.Noise+-- Copyright: (c) 2013 Ertugrul Soeylemez+-- License: BSD3+-- Maintainer: Ertugrul Soeylemez <es@ertes.de>++module FRP.Netwire.Noise+ ( -- * Noise generators+ noise,+ noiseR,+ wackelkontakt,++ -- * Convenience+ stdNoise,+ stdNoiseR,+ stdWackelkontakt+ )+ where++import Control.Wire+import Prelude hiding ((.), id)+import System.Random+++-- | Noise events with the given distance between events. Use 'hold' or+-- 'holdFor' to generate a staircase.++noise ::+ (HasTime t s, Random b, RandomGen g)+ => t -- ^ Time period.+ -> g -- ^ Random number generator.+ -> Wire s e m a (Event b)+noise int | int <= 0 = error "noise: Non-positive interval"+noise int = periodicList int . randoms+++-- | Noise events with the given distance between events. Noise will be+-- in the given range. Use 'hold' or 'holdFor' to generate a staircase.++noiseR ::+ (HasTime t s, Random b, RandomGen g)+ => t -- ^ Step duration.+ -> (b, b) -- ^ Noise range.+ -> g -- ^ Random number generator.+ -> Wire s e m a (Event b)+noiseR int _ | int <= 0 = error "noiseR: Non-positive interval"+noiseR int r = periodicList int . randomRs r+++-- | Convenience interface to 'noise' for 'StdGen'.++stdNoise ::+ (HasTime t s, Random b)+ => t -- ^ Step duration.+ -> Int -- ^ 'StdGen' seed.+ -> Wire s e m a (Event b)+stdNoise int = noise int . mkStdGen+++-- | Convenience interface to 'noiseR' for 'StdGen'.++stdNoiseR ::+ (HasTime t s, Monad m, Random b)+ => t -- ^ Step duration.+ -> (b, b) -- ^ Noise range.+ -> Int -- ^ 'StdGen' seed.+ -> Wire s e m a (Event b)+stdNoiseR int r = noiseR int r . mkStdGen+++-- | Convenience interface to 'wackelkontakt' for 'StdGen'.++stdWackelkontakt ::+ (HasTime t s, Monad m, Monoid e)+ => t -- ^ Step duration.+ -> Double -- ^ Probability to produce.+ -> Int -- ^ 'StdGen' seed.+ -> Wire s e m a a+stdWackelkontakt int p = wackelkontakt int p . mkStdGen+++-- | Randomly produce or inhibit with the given probability, each time+-- for the given duration.+--+-- The name /Wackelkontakt/ (German for /slack joint/) is a Netwire+-- running gag. It makes sure that you revisit the documentation from+-- time to time. =)+--+-- * Depends: now.++wackelkontakt ::+ (HasTime t s, Monad m, Monoid e, RandomGen g)+ => t -- ^ Duration.+ -> Double -- ^ Probability to produce.+ -> g -- ^ Random number generator.+ -> Wire s e m a a+wackelkontakt int _ _ | int <= 0 = error "wackelkontakt: Non-positive duration"+wackelkontakt int p g = fmap snd $ when (< p) . hold . noise int g &&& id
+ FRP/Netwire/Utils/Timeline.hs view
@@ -0,0 +1,176 @@+-- |+-- Module: FRP.Netwire.Utils.Timeline+-- Copyright: (c) 2013 Ertugrul Soeylemez+-- License: BSD3+-- Maintainer: Ertugrul Soeylemez <es@ertes.de>++module FRP.Netwire.Utils.Timeline+ ( -- * Time lines for statistics wires+ Timeline,++ -- * Constructing time lines+ insert,+ singleton,+ union,++ -- * Linear sampling+ linAvg,+ linCutL,+ linCutR,+ linLookup,++ -- * Staircase sampling+ scAvg,+ scCutL,+ scCutR,+ scLookup+ )+ where++import qualified Data.Map.Strict as M+import Control.Applicative+import Data.Data+import Data.Map.Strict (Map)+++-- | A time line is a non-empty set of samples together with time+-- information.++newtype Timeline t a =+ Timeline {+ timeline :: Map t a+ }+ deriving (Data, Eq, Ord, Read, Show, Typeable)++instance Functor (Timeline t) where+ fmap f (Timeline m) = Timeline (M.map f m)+++-- | Insert the given data point.++insert :: (Ord t) => t -> a -> Timeline t a -> Timeline t a+insert t x (Timeline m) = Timeline (M.insert t x m)+++-- | Linearly interpolate the points in the time line, integrate the+-- given time interval of the graph, divide by the interval length.++linAvg ::+ (Fractional a, Fractional t, Real t)+ => t -> t -> Timeline t a -> a+linAvg t0 t1+ | t0 > t1 = const (error "linAvg: Invalid interval")+ | t0 == t1 = linLookup t0+linAvg t0 t1 = avg 0 . M.assocs . timeline . linCutR t1 . linCutL t0+ where+ avg a' ((t', y1) : xs@((t, y2) : _)) =+ let dt = realToFrac (t - t')+ a = a' + dt*(y1 + y2)/2+ in a `seq` avg a xs+ avg a' _ = a' / realToFrac (t1 - t0)+++-- | Cut the timeline at the given point in time @t@, such that all+-- samples up to but not including @t@ are forgotten. The most recent+-- sample before @t@ is moved and interpolated accordingly.++linCutL ::+ (Fractional a, Fractional t, Real t)+ => t -> Timeline t a -> Timeline t a+linCutL t tl@(Timeline m) =+ Timeline $+ case M.splitLookup t m of+ (_, Just x, mr) -> M.insert t x mr+ (_, _, mr) -> M.insert t (linLookup t tl) mr+++-- | Cut the timeline at the given point in time @t@, such that all+-- samples later than @t@ are forgotten. The most recent sample after+-- @t@ is moved and interpolated accordingly.++linCutR ::+ (Fractional a, Fractional t, Real t)+ => t -> Timeline t a -> Timeline t a+linCutR t tl@(Timeline m) =+ Timeline $+ case M.splitLookup t m of+ (ml, Just x, _) -> M.insert t x ml+ (ml, _, _) -> M.insert t (linLookup t tl) ml+++-- | Look up with linear sampling.++linLookup :: (Fractional a, Fractional t, Real t) => t -> Timeline t a -> a+linLookup t (Timeline m) =+ case M.splitLookup t m of+ (_, Just x, _) -> x+ (ml, _, mr) ->+ case (fst <$> M.maxViewWithKey ml, fst <$> M.minViewWithKey mr) of+ (Just (t1, x1), Just (t2, x2)) ->+ let f = realToFrac ((t - t1) / (t2 - t1))+ in x1*(1 - f) + x2*f+ (Just (_, x), _) -> x+ (_, Just (_, x)) -> x+ _ -> error "linLookup: BUG: querying empty Timeline"+++-- | Integrate the given time interval of the staircase, divide by the+-- interval length.++scAvg :: (Fractional a, Real t) => t -> t -> Timeline t a -> a+scAvg t0 t1+ | t0 > t1 = const (error "scAvg: Invalid interval")+ | t0 == t1 = scLookup t0+scAvg t0 t1 = avg 0 . M.assocs . timeline . scCutR t1 . scCutL t0+ where+ avg a' ((t', y) : xs@((t, _) : _)) =+ let dt = realToFrac (t - t')+ a = a' + dt*y+ in a `seq` avg a xs+ avg a' _ = a' / realToFrac (t1 - t0)+++-- | Cut the timeline at the given point in time @t@, such that all+-- samples up to but not including @t@ are forgotten. The most recent+-- sample before @t@ is moved accordingly.++scCutL :: (Ord t) => t -> Timeline t a -> Timeline t a+scCutL t tl@(Timeline m) =+ Timeline $+ case M.splitLookup t m of+ (_, Just x, mr) -> M.insert t x mr+ (_, _, mr) -> M.insert t (scLookup t tl) mr+++-- | Cut the timeline at the given point in time @t@, such that all+-- samples later than @t@ are forgotten. The earliest sample after @t@+-- is moved accordingly.++scCutR :: (Ord t) => t -> Timeline t a -> Timeline t a+scCutR t tl@(Timeline m) =+ Timeline $+ case M.splitLookup t m of+ (ml, Just x, _) -> M.insert t x ml+ (ml, _, _) -> M.insert t (scLookup t tl) ml+++-- | Look up on staircase.++scLookup :: (Ord t) => t -> Timeline t a -> a+scLookup t (Timeline m) =+ case (M.lookupLE t m, M.lookupGE t m) of+ (Just (_, x), _) -> x+ (_, Just (_, x)) -> x+ _ -> error "linLookup: BUG: querying empty Timeline"+++-- | Singleton timeline with the given point.++singleton :: t -> a -> Timeline t a+singleton t = Timeline . M.singleton t+++-- | Union of two time lines. Right-biased.++union :: (Ord t) => Timeline t a -> Timeline t a -> Timeline t a+union (Timeline m1) (Timeline m2) = Timeline (M.union m2 m1)
LICENSE view
@@ -1,5 +1,5 @@-Netwire license-Copyright (c) 2012, Ertugrul Soeylemez+netwire license+Copyright (c) 2013, Ertugrul Soeylemez All rights reserved.
+ README.md view
@@ -0,0 +1,437 @@+Netwire+=======++Netwire is a functional reactive programming (FRP) library with signal+inhibition. It implements three related concepts, *wires*, *intervals*+and *events*, the most important of which is the *wire*. To work with+wires we will need a few imports:++ import FRP.Netwire+ import Prelude hiding ((.), id)++The `FRP.Netwire` module exports the basic types and helper functions.+It also has some convenience reexports you will pretty much always need+when working with wires, including `Control.Category`. This is why we+need the explicit `Prelude` import.++In general wires are generalized automaton arrows, so you can express+many design patterns using them. The `FRP.Netwire` module provides a+proper FRP framework based on them, which strictly respects continuous+time and discrete event semantics. When developing a framework based on+Netwire, e.g. a GUI library or a game engine, you may want to import+`Control.Wire` instead.+++Introduction+------------++The following type is central to the entire library:++ data Wire s e m a b++Don't worry about the large number of type arguments. They all have+very simple meanings, which will be explained below.++A value of this type is called a *wire* and represents a *reactive*+value of type $b$, that is a value that may change over time. It may+depend on a reactive value of type $a$. In a sense a wire is a function+from a reactive value of type $a$ to a reactive value of type $b$, so+whenever you see something of type `Wire s e m a b` your mind should+draw an arrow from $a$ to $b$. In FRP terminology a reactive value is+called a *behavior*.++A constant reactive value can be constructed using `pure`:++ pure 15++This wire is the reactive value 15. It does not depend on other+reactive values and does not change over time. This suggests that there+is an applicative interface to wires, which is indeed the case:++ liftA2 (+) (pure 15) (pure 17)++This reactive value is the sum of two reactive values, each of which is+just a constant, 15 and 17 respectively. So this is the constant+reactive value 32. Let's spell out its type:++ myWire :: (Monad m, Num b) => Wire s e m a b+ myWire = liftA2 (+) (pure 15) (pure 17)++This indicates that $m$ is some kind of underlying monad. As an+application developer you don't have to concern yourself much about it.+Framework developers can use it to allow wires to access environment+values through a reader monad or to produce something (like a GUI)+through a writer monad.++The wires we have seen so far are rather boring. Let's look at a more+interesting one:++ time :: (HasTime t s) => Wire s e m a t++This wire represents the current local time, which starts at zero when+execution begins. It does not make any assumptions about the time type+other than that it is a numeric type with a `Real` instance. This is+enforced implicitly by the `HasTime` constraint.++The type of this wire gives some insight into the $s$ parameter. Wires+are generally pure and do not have access to the system clock or other+run-time information. The timing information has to come from outside+and is passed to the wire through a value of type $s$, called the *state+delta*. We will learn more about this in the next section about+executing wires.++Since there is an applicative interface you can also apply `fmap` to a+wire to apply a function to its value:++ fmap (2*) time++This reactive value is a clock that is twice as fast as the regular+local time clock. If you use system time as your clock, then the time+type $t$ will most likely be `NominalDiffTime` from `Data.Time.Clock`.+However, you will usually want to have time of type `Double` or some+other floating point type. There is a predefined wire for this:++ timeF :: (Fractional b, HasTime t s, Monad m) => Wire s e m a b+ timeF = fmap realToFrac time++If you think of reactive values as graphs with the horizontal axis+representing time, then the `time` wire is just a straight diagonal line+and constant wires (constructed by `pure`) are just horizontal lines.+You can use the applicative interface to perform arithmetic on them:++ liftA2 (\t c -> c - 2*t) time (pure 60)++This gives you a countdown clock that starts at 60 and runs twice as+fast as the regular clock. So it after two seconds its value will be+56, decreasing by 2 each second.+++Testing wires+-------------++Enough theory, we wanna see some performance now! Let's write a simple+program to test a constant (`pure`) wire:++ import Control.Wire+ import Prelude hiding ((.), id)++ wire :: (Monad m) => Wire s () m a Integer+ wire = pure 15++ main :: IO ()+ main = testWire (pure ()) wire++This should just display the value 15. Abort the program by pressing+Ctrl-C. The `testWire` function is a convenience to examine wires. It+just executes the wire and continuously prints its value to stdout:++ testWire ::+ (MonadIO m, Show b, Show e)+ => Session m s+ -> (forall a. Wire s e Identity a b)+ -> m c++The type signatures in Netwire are known to be scary. =) But like most+of the library the underlying meaning is actually very simple.+Conceptually the wire is run continuously step by step, at each step+increasing its local time slightly. This process is traditionally+called *stepping*.++As an FRP developer you assume a continuous time model, so you don't+observe this stepping process from the point of view of your reactive+application, but it can be useful to know that wire execution is+actually a discrete process.++The first argument of `testWire` needs some explanation. It is a recipe+for state deltas. In the above example we have just used `pure ()`,+meaning that we don't use anything stateful from the outside world,+particularly we don't use a clock. From the type signature it is also+clear that this sets `s = ()`.++The second argument is the wire to run. The input type is quantified+meaning that it needs to be polymorphic in its input type. In other+words it means that the wire does not depend on any other reactive+value. The underlying monad is `Identity` with the obvious meaning that+this wire cannot have any monadic effects.++The following application just displays the number of seconds passed+since program start (with some subsecond precision):++ wire :: (HasTime t s) => Wire s () m a t+ wire = time++ main :: IO ()+ main = testWire clockSession_ wire++Since this time the wire actually needs a clock we use `clockSession_`+as the second argument:++ clockSession_ ::+ (Applicative m, MonadIO m)+ => Session m (Timed NominalDiffTime ())++It will instantiate $s$ to be `Timed NominalDiffTime ()`. This type+indeed has a `HasTime` instance with $t$ being `NominalDiffTime`. In+simpler words it provides a clock to the wire. At first it may seem+weird to use `NominalDiffTime` instead of something like `UTCTime`, but+this is reasonable, because time is relative to the wire's start time.+Also later in the section about switching we will see that a wire does+not necessarily start when the program starts.+++Constructing wires+------------------++Now that we know how to test wires we can start constructing more+complicated wires. First of all it is handy that there are many+convenience instances, including `Num`. Instead of `pure 15` we can+simply write `15`. Also instead of++ liftA2 (+) time (pure 17)++we can simply write:++ time + 17++This clock starts at 17 instead of zero. Let's make it run twice as+fast:++ 2*time + 17++If you have trouble wrapping your head around such an expression it may+help to read `a*b + c` mathematically as $a(t) b(t) + c(t)$ and read+`time` as simply $t$.++So far we have seen wires that ignore their input. The following wire+uses its input:++ integral 5++It literally integrates its input value with respect to time. Its+argument is the integration constant, i.e. the start value. To supply+an input simply compose it:++ integral 5 . 3++Remember that `3` really means `pure 3`, a constant wire. The integral+of the constant 3 is $3 t + c$ and here $c = 5$. Here is another+example:++ integral 5 . time++Since `time` denotes $t$ the integral will be $\frac{1}{2} t^2 + c$,+again with $c = 5$. This may sound like a complicated, sophisticated+wire, but it's really not. Surprisingly there is no crazy algebra or+complicated numerical algorithm going on under the hood. Integrating+over time requires one addition and one division each frame. So there+is nothing wrong with using it extensively to animate a scene or to move+objects in a game.++Sometimes categorical composition and the applicative interface can be+inconvenient, in which case you may choose to use the arrow interface.+The above integration can be expressed the following way:++ proc _ -> do+ t <- time -< ()+ integral 5 -< t++Since `time` ignores its input signal, we just give it a constant signal+with value `()`. We name time's value $t$ and pass it as the input+signal to `integral`.+++Intervals+---------++Wires may choose to produce a signal only for a limited amount of time.+We refer to those wires as intervals. When a wire does not produce,+then it *inhibits*. Example:++ for 3++This wire acts like the identity wire in that it passes its input signal+through unchanged:++ for 3 . "yes"++The signal of this wire will be "yes", but after three seconds it will+stop to act like the identity wire and will inhibit forever.++When you use `testWire` inhibition will be displayed as "I:" followed by+a value, the *inhibition value*. This is what the $e$ parameter to+`Wire` is. It's called the *inhibition monoid*:++ for :: (HasTime t s, Monoid e) => t -> Wire s e m a a++As you can see the input and output types are the same and fully+polymorphic, hinting at the identity-like behavior. All predefined+intervals inhibit with the `mempty` value. When the wire inhibits, you+don't get a signal of type $a$, but rather an inhibition value of type+$e$. Netwire does not interpret this value in any way and in most cases+you would simply use `e = ()`.++Intervals give you a very elegant way to combine wires:++ for 3 . "yes" <|> "no"++This wire produces "yes" for three seconds. Then the wire to the left+of `<|>` will stop producing, so `<|>` will use the wire to its right+instead. You can read the operator as a left-biased "or". The signal+of the wire `w1 <|> w2` will be the signal of the leftmost component+wire that actually produced a signal. There are a number of predefined+interval wires. The above signal can be written equivalently as:++ after 3 . "no" <|> "yes"++The left wire will inhibit for the first three seconds, so during that+interval the right wire is chosen. After that, as suggested by its+name, the `after` wire starts acting like the identity wire, so the left+side takes precedence. Once the time period has passed the `after` wire+will produce forever, leaving the "yes" wire never to be reached again.+However, you can easily combine intervals:++ after 5 . for 6 . "Blip!" <|> "Look at me..."++The left wire will produce after five seconds from the beginning for six+seconds from the beginning, so effectively it will produce for one+second. When you animate this wire, you will see the string "Look at+me..." for five seconds, then you will see "Blip!" for one second, then+finally it will go back to "Look at me..." and display that one forever.+++Events+------++Events are things that happen at certain points in time. Examples+include button presses, network packets or even just reaching a certain+point in time. As such they can be thought of as lists of values+together with their occurrence times. Events are actually first class+signals of the `Event` type:++ data Event a++For example the predefined `never` event is the event that never occurs:++ never :: Wire s e m a (Event b)++As suggested by the type events contain a value. Netwire does not+export the constructors of the `Event` type by default. If you are a+framework developer you can import the `Control.Wire.Unsafe.Event`+module to implement your own events. A game engine may include events+for key presses or certain things happening in the scene. However, as+an application developer you should view this type as being opaque.+This is necessary in order to protect continuous time semantics. You+cannot access event values directly.++There are a number of ways to respond to an event. The primary way to+do this in Netwire is to turn events into intervals. There are a number+of predefined wires for that purpose, for example `asSoonAs`:++ asSoonAs :: (Monoid e) => Wire s e m (Event a) a++This wire takes an event signal as its input. Initially it inhibits,+but as soon as the event occurs for the first time, it produces the+event's last value forever. The `at` event will occur only once after+the given time period has passed:++ at :: (HasTime t s) => t -> Wire s e m a (Event a)++Example:++ at 3 . "blubb"++This event will occur after three seconds, and the event's value will be+"blubb". Using `asSoonAs` we can turn this into an interval:++ asSoonAs . at 3 . "blubb"++This wire will inhibit for three seconds and then start producing. It+will produce the value "blubb" forever. That's the event's last value+after three seconds, and it will never change, because the event does+not occur ever again. Here is an example that may be more+representative of that property:++ asSoonAs . at 3 . time++This wire inhibits for three seconds, then it produces the value 3 (or a+value close to it) forever. Notice that this is not a clock. It does+not produce the current time, but the `time` at the point in time when+the event occurred.++To combine multiple events there are a number of options. In principle+you should think of event values to form a semigroup (of your choice),+because events can occur simultaneously. However, in many cases the+actual value of the event is not that interesting, so there is an easy+way to get a left- or right-biased combination:++ (at 2 <& at 3) . time++This event occurs two times, namely once after two seconds and once+after three seconds. In each case the event value will be the+occurrence time. Here is an interesting case:++ at 2 . "blah" <& at 2 . "blubb"++These events will occur simultaneously. The value will be "blah",+because `<&` means left-biased combination. There is also `&>` for+right-biased combination. If event values actually form a semigroup,+then you can just use monoidal composition:++ at 2 . "blah" <> at 2 . "blubb"++Again these events occur at the same time, but this time the event value+will be "blahblubb". Note that you are using two Monoid instances and+one Semigroup instance here. If the signals of two wires form a monoid,+then wires themselves form a monoid:++ w1 <> w2 = liftA2 (<>) w1 w2++There are many predefined event-wires and many combinators for+manipulating events in the `Control.Wire.Event` module. A common events+is the `now` event:++ now :: Wire s e m a (Event a)++This event occurs once at the beginning.+++Switching+---------++We still lack a meaningful way to respond to events. This is where+*switching* comes in, sometimes also called *dynamic switching*. The+most important combinator for switching is `-->`:++ w1 --> w2++The idea is really straightforward: This wire acts like `w1` as long as+it produces. As soon as it stops producing it is discarded and `w2`+takes its place. Example:++ for 3 . "yes" --> "no"++In this case the behavior will be the same as in the *intervals*+section, but with two major differences: Firstly when the first+interval ends, it is completely discarded and garbage-collected, never+to be seen again. Secondly and more importantly the point in time of+switching will be the beginning for the new wire. Example:++ for 3 . time --> time++This wire will show a clock counting to three seconds, then it will+start over from zero. This is why we usually refer to time as *local+time*.++Recursion is fully supported. Here is a fun example:++ netwireIsCool =+ for 2 . "Once upon a time..." -->+ for 3 . "... games were completely imperative..." -->+ for 2 . "... but then..." -->+ for 10 . ("Netwire 5! " <> anim) -->+ netwireIsCool++ where+ anim =+ holdFor 0.5 . periodic 1 . "Hoo..." <|>+ "...ray!"
Setup.lhs view
@@ -1,5 +1,5 @@-Netwire setup script-Copyright (C) 2012, Ertugrul Soeylemez+netwire setup script+Copyright (C) 2013, Ertugrul Soeylemez Please see the LICENSE file for terms and conditions of use, modification and distribution of this package, including this file.
+ default.nix view
@@ -0,0 +1,30 @@+# -*-conf-*-++{ }:+with import <nixpkgs> { };++let+ hs = haskellPackages;+ inherit (hs) cabal;+in rec {++ netwire =+ cabal.mkDerivation (self : rec {+ pname = "netwire";+ version = "5.0.0";+ isLibrary = true;+ isExecutable = false;++ src = ./dist/netwire- + "${version}.tar.gz";++ buildDepends = [+ hs.parallel+ hs.QuickCheck+ hs.semigroups+ hs.testFramework+ hs.testFrameworkQuickcheck2+ hs.testFrameworkTh+ ];+ });++}
netwire.cabal view
@@ -1,90 +1,99 @@-Name: netwire-Version: 4.0.7-Category: Control, FRP-Synopsis: Flexible wire arrows for FRP-Maintainer: Ertugrul Söylemez <es@ertes.de>-Author: Ertugrul Söylemez <es@ertes.de>-Copyright: (c) 2012 Ertugrul Söylemez-License: BSD3-License-file: LICENSE-Build-type: Simple-Stability: experimental-Cabal-version: >= 1.10-Description:- Efficient and flexible wire arrows for functional reactive programming- and other forms of locally stateful programming.+name: netwire+version: 5.0.0+category: FRP+synopsis: Functional reactive programming library+maintainer: Ertugrul Söylemez <es@ertes.de>+author: Ertugrul Söylemez <es@ertes.de>+copyright: (c) 2013 Ertugrul Söylemez+license: BSD3+license-file: LICENSE+build-type: Simple+cabal-version: >= 1.10+extra-source-files: README.md default.nix+description:+ This library provides interfaces for and implements wire arrows+ useful both for functional reactive programming (FRP) and locally+ stateful programming (LSP). +flag TestProgram+ default: False+ description: Build the test program+ manual: True+ Source-repository head type: darcs- location: http://darcs.ertes.de/netwire/+ location: http://hub.darcs.net/ertes/netwire -Library- Build-depends:- base >= 4.0 && < 5,- bifunctors >= 0.1 && < 4,- containers >= 0.4 && < 1,- deepseq >= 1.3 && < 2,- lifted-base >= 0.1 && < 1,- monad-control >= 0.3 && < 1,- mtl >= 2.0 && < 3,- profunctors >= 0.1 && < 4,- random >= 1.0 && < 2,- semigroups >= 0.8 && < 1,- tagged >= 0.4 && < 1,- time >= 1.4 && < 2,- vector-space >= 0.8 && < 1- Default-language: Haskell2010- Default-extensions:- BangPatterns+library+ build-depends:+ base >= 4.5 && < 5,+ containers >= 0.5 && < 1,+ deepseq >= 1.3 && < 2,+ parallel >= 3.2 && < 4,+ random >= 1.0 && < 2,+ semigroups >= 0.9 && < 1,+ transformers >= 0.3 && < 1,+ time >= 1.4 && < 2+ default-language: Haskell2010+ default-extensions: DeriveDataTypeable- FlexibleContexts+ DeriveFoldable+ DeriveFunctor+ DeriveTraversable FlexibleInstances+ FunctionalDependencies+ GADTs MultiParamTypeClasses RankNTypes- ScopedTypeVariables TupleSections- TypeFamilies- GHC-Options: -W- Exposed-modules:+ ghc-options: -W+ exposed-modules: Control.Wire- Control.Wire.Classes- Control.Wire.Prefab- Control.Wire.Prefab.Accum- Control.Wire.Prefab.Analyze- Control.Wire.Prefab.Effect- Control.Wire.Prefab.Event- Control.Wire.Prefab.List- Control.Wire.Prefab.Move- Control.Wire.Prefab.Noise- Control.Wire.Prefab.Queue- Control.Wire.Prefab.Sample- Control.Wire.Prefab.Simple- Control.Wire.Prefab.Time+ Control.Wire.Core+ Control.Wire.Event+ Control.Wire.Interval+ Control.Wire.Run Control.Wire.Session- Control.Wire.TimedMap- Control.Wire.Trans- Control.Wire.Trans.Combine- Control.Wire.Trans.Embed- Control.Wire.Trans.Event- Control.Wire.Trans.Simple- Control.Wire.Trans.Switch- Control.Wire.Trans.Time- Control.Wire.Types- Control.Wire.Wire+ Control.Wire.Switch+ Control.Wire.Time+ Control.Wire.Unsafe.Event+ FRP.Netwire+ FRP.Netwire.Analyze+ FRP.Netwire.Move+ FRP.Netwire.Noise+ FRP.Netwire.Utils.Timeline --- Executable netwire-test--- Build-depends:--- base >= 4 && < 5,--- containers,+executable netwire-test+ build-depends:+ base >= 4.5 && < 5,+ containers,+ netwire+ default-language: Haskell2010+ default-extensions:+ Arrows+ OverloadedStrings+ RecursiveDo+ ghc-options: -threaded -rtsopts+ hs-source-dirs: test+ main-is: Test.hs+ if flag(testprogram)+ buildable: True+ else+ buildable: False++-- test-suite tests+-- type: exitcode-stdio-1.0+-- build-depends:+-- base >= 4.5 && < 5, -- netwire,--- random,--- time--- Default-language: Haskell2010--- Default-extensions:--- Arrows--- OverloadedStrings--- RecursiveDo--- TupleSections--- Hs-source-dirs: test--- Main-is: Main.hs--- GHC-Options: -threaded -rtsopts+-- QuickCheck,+-- test-framework,+-- test-framework-quickcheck2,+-- test-framework-th,+-- vty+-- default-language: Haskell2010+-- default-extensions:+-- TemplateHaskell+-- ghc-options: -W -threaded -rtsopts -with-rtsopts=-N+-- hs-source-dirs: test+-- main-is: Props.hs
+ test/Test.hs view
@@ -0,0 +1,20 @@+-- |+-- Module: Main+-- Copyright: (c) 2013 Ertugrul Soeylemez+-- License: BSD3+-- Maintainer: Ertugrul Soeylemez <es@ertes.de>++module Main where++import Control.Monad.Fix+import Control.Wire+import Prelude hiding ((.), id)+++wire :: SimpleWire a String+wire =+ holdFor 0.5 . periodicList 1 (cycle ["a", "b", "c"]) <|> "---"+++main :: IO ()+main = testWire clockSession_ wire