netwire (empty) → 1.0.0
raw patch · 14 files changed
+1688/−0 lines, 14 filesdep +basedep +containersdep +deepseqsetup-changed
Dependencies added: base, containers, deepseq, mersenne-random, stm, time, vector, vector-space
Files
- FRP/NetWire.hs +45/−0
- FRP/NetWire/Analyze.hs +134/−0
- FRP/NetWire/Calculus.hs +45/−0
- FRP/NetWire/Event.hs +355/−0
- FRP/NetWire/IO.hs +67/−0
- FRP/NetWire/Random.hs +60/−0
- FRP/NetWire/Request.hs +30/−0
- FRP/NetWire/Session.hs +121/−0
- FRP/NetWire/Switch.hs +184/−0
- FRP/NetWire/Tools.hs +277/−0
- FRP/NetWire/Wire.hs +264/−0
- LICENSE +32/−0
- Setup.lhs +12/−0
- netwire.cabal +62/−0
+ FRP/NetWire.hs view
@@ -0,0 +1,45 @@+-- |+-- Module: FRP.NetWire+-- Copyright: (c) 2011 Ertugrul Soeylemez+-- License: BSD3+-- Maintainer: Ertugrul Soeylemez <es@ertes.de>+--+-- Arrowized FRP implementation for networking applications. The aim of+-- this library is to provide a convenient FRP implementation, which+-- should enable you to write entirely pure network sessions.++module FRP.NetWire+ ( -- * Wires+ Wire, Time, DTime, Event,++ -- * Reactive sessions+ Session,+ stepWire,+ stepWireDelta,+ stepWireTime,+ withWire,++ -- * Reexports+ module FRP.NetWire.Analyze,+ module FRP.NetWire.Calculus,+ -- module FRP.NetWire.Concurrent,+ module FRP.NetWire.Event,+ module FRP.NetWire.IO,+ module FRP.NetWire.Random,+ module FRP.NetWire.Request,+ module FRP.NetWire.Switch,+ module FRP.NetWire.Tools+ )+ where++import FRP.NetWire.Analyze+import FRP.NetWire.Calculus+-- import FRP.NetWire.Concurrent+import FRP.NetWire.Event+import FRP.NetWire.IO+import FRP.NetWire.Random+import FRP.NetWire.Request+import FRP.NetWire.Session+import FRP.NetWire.Switch+import FRP.NetWire.Tools+import FRP.NetWire.Wire
+ FRP/NetWire/Analyze.hs view
@@ -0,0 +1,134 @@+-- |+-- Module: FRP.NetWire.Analyze+-- Copyright: (c) 2011 Ertugrul Soeylemez+-- License: BSD3+-- Maintainer: Ertugrul Soeylemez <es@ertes.de>+--+-- Signal analysis.++module FRP.NetWire.Analyze+ ( -- * Changes+ diff,++ -- * Statistics+ -- ** Average+ avg,+ avgAll,+ avgFps,++ -- ** Peak+ highPeak,+ lowPeak,+ peakBy,+ )+ where++import qualified Data.Vector.Unboxed.Mutable as V+import Control.DeepSeq+import Data.Vector.Unboxed.Mutable (IOVector, Unbox)+import FRP.NetWire.Wire+++-- | Calculate the average of the signal over the given number of last+-- samples. This wire has O(n) space complexity and O(1) time+-- complexity.+--+-- If you need an average over all samples ever produced, consider using+-- 'avgAll' instead.++avg :: forall v. (Fractional v, NFData v, Unbox v) => Int -> Wire v v+avg n =+ mkGen $ \_ x -> do+ samples <- V.replicate n (x/d)+ return (Just x, avg' samples x 0)++ where+ avg' :: IOVector v -> v -> Int -> Wire v v+ avg' samples s' cur' =+ mkGen $ \_ ((/d) -> x) -> do+ let cur = let cur = succ cur' in if cur >= n then 0 else cur+ x' <- V.read samples cur+ V.write samples cur x+ let s = s' - x' + x+ s `deepseq` return (Just s, avg' samples s cur)++ d :: v+ d = realToFrac n+++-- | Calculate the average of the signal over all samples.+--+-- Please note that somewhat surprisingly this wire runs in constant+-- space and is generally faster than 'avg', but most applications will+-- benefit from averages over only the last few samples.++avgAll :: forall v. (Fractional v, NFData v) => Wire v v+avgAll = mkGen $ \_ x -> return (Just x, avgAll' 1 x)+ where+ avgAll' :: v -> v -> Wire v v+ avgAll' n' a' =+ mkGen $ \_ x ->+ let n = n' + 1+ a = a' - a'/n + x/n in+ n `deepseq` a `deepseq` return (Just a, avgAll' n a)+++-- | Calculate the average number of frames per virtual second for the+-- last given number of frames.+--+-- Please note that this wire uses the clock, which you give the network+-- using the stepping functions in "FRP.NetWire.Session". If this clock+-- doesn't represent real time, then the output of this wire won't+-- either.++avgFps :: forall a. Int -> Wire a Double+avgFps = avgFps' . avg+ where+ avgFps' :: Wire Double Double -> Wire a Double+ avgFps' w' =+ mkGen $ \ws@(wsDTime -> dt) _ -> do+ (ma, w) <- toGen w' ws dt+ return (fmap recip ma, avgFps' w)+++-- | Emits an event, whenever the input signal changes. The event+-- contains the last input value and the time elapsed since the last+-- change.++diff :: forall a. Eq a => Wire a (Event (a, Time))+diff =+ mkGen $ \(wsDTime -> dt) x' ->+ return (Just Nothing, diff' dt x')++ where+ diff' :: Time -> a -> Wire a (Event (a, Time))+ diff' t' x' =+ mkGen $ \(wsDTime -> dt) x ->+ let t = t' + dt in+ if x' == x+ then return (Just Nothing, diff' t x')+ else return (Just (Just (x', t)), diff' 0 x)+++-- | Returh the high peak.++highPeak :: (NFData a, Ord a) => Wire a a+highPeak = peakBy compare+++-- | Return the low peak.++lowPeak :: (NFData a, Ord a) => Wire a a+lowPeak = peakBy (flip compare)+++-- | Return the high peak with the given comparison function.++peakBy :: forall a. NFData a => (a -> a -> Ordering) -> Wire a a+peakBy comp = mkGen $ \_ x -> return (Just x, peakBy' x)+ where+ peakBy' :: a -> Wire a a+ peakBy' p' =+ mkGen $ \_ x -> do+ let p = if comp x p' == GT then x else p'+ p `deepseq` return (Just p, peakBy' p)
+ FRP/NetWire/Calculus.hs view
@@ -0,0 +1,45 @@+-- |+-- Module: FRP.NetWire.Calculus+-- Copyright: (c) 2011 Ertugrul Soeylemez+-- License: BSD3+-- Maintainer: Ertugrul Soeylemez <es@ertes.de>+--+-- Calculus functions.++module FRP.NetWire.Calculus+ ( -- * Calculus over time+ derivative,+ derivativeFrom,+ integral+ )+ where++import Control.DeepSeq+import Data.VectorSpace+import FRP.NetWire.Wire+++-- | Differentiate over time. Inhibits at first instant.++derivative :: (NFData v, VectorSpace v, Scalar v ~ Double) => Wire v v+derivative = mkGen $ \_ y2 -> return (Nothing, derivativeFrom y2)+++-- | Differentiate over time. The argument is the value before the+-- first instant.++derivativeFrom :: (NFData v, VectorSpace v, Scalar v ~ Double) => v -> Wire v v+derivativeFrom y1 =+ mkGen $ \(wsDTime -> dt) y2 -> do+ let dy = (y2 ^-^ y1) ^/ dt+ dy `deepseq` return (Just dy, derivativeFrom y2)+++-- | Integrate over time. The argument is the integration constant.++integral :: (NFData v, VectorSpace v, Scalar v ~ Double) => v -> Wire v v+integral x1 =+ mkGen $ \ws dx -> do+ let dt = wsDTime ws+ x2 = x1 ^+^ dt *^ dx+ x2 `deepseq` return (Just x2, integral x2)
+ FRP/NetWire/Event.hs view
@@ -0,0 +1,355 @@+-- |+-- Module: FRP.NetWire.Event+-- Copyright: (c) 2011 Ertugrul Soeylemez+-- License: BSD3+-- Maintainer: Ertugrul Soeylemez <es@ertes.de>+--+-- Events.++module FRP.NetWire.Event+ ( -- * Producing events+ after,+ afterEach,+ edge,+ edgeBy,+ edgeJust,+ never,+ now,+ once,+ repeatedly,+ repeatedlyList,++ -- * Wire transformers+ wait,++ -- * Event transformers+ -- ** Delaying events+ dam,+ delayEvents,+ delayEventsSafe,+ -- ** Selecting events+ dropEvents,+ dropFor,+ notYet,+ takeEvents,+ takeFor,+ -- ** Manipulating events+ accum,+ -- ** Mapping to continuous signals+ hold, dHold+ )+ where++import qualified Data.Sequence as Seq+import Control.Arrow+import Control.Monad+import Data.Maybe+import Data.Sequence (Seq, (|>), ViewL((:<)))+import FRP.NetWire.Tools+import FRP.NetWire.Wire+++-- | This function corresponds to the 'iterate' function for lists.+-- Begins with an initial output value, which is not emitted. Each time+-- an input event is received, its function is applied to the current+-- accumulator and the new value is emitted.++accum :: forall a. a -> Wire (Event (a -> a)) (Event a)+accum ee' = accum'+ where+ accum' :: Wire (Event (a -> a)) (Event a)+ accum' =+ mkGen $ \_ ->+ return .+ maybe (Nothing, accum')+ (\f -> let ee = f ee' in ee `seq` (Just (Just ee), accum ee))+++-- | Produce an event once after the specified delay and never again.+-- The event's value will be the input signal at that point.++after :: forall a. DTime -> Wire a (Event a)+after t' =+ mkGen $ \(wsDTime -> dt) x ->+ let t = t' - dt in+ if t <= 0+ then return (Just (Just x), never)+ else return (Nothing, after t)+++-- | Produce an event according to the given list of time deltas and+-- event values. The time deltas are relative to each other, hence from+-- the perspective of switching in @[(1, 'a'), (2, 'b'), (3, 'c')]@+-- produces the event @'a'@ after one second, @'b'@ after three seconds+-- and @'c'@ after six seconds.++afterEach :: forall a b. [(DTime, b)] -> Wire a (Event b)+afterEach = afterEach' 0+ where+ afterEach' :: DTime -> [(DTime, b)] -> Wire a (Event b)+ afterEach' _ [] = never+ afterEach' t' d@((int, x):ds) =+ mkGen $ \(wsDTime -> dt) _ ->+ let t = t' + dt in+ if t >= int+ then let nextT = t - int+ in nextT `seq` return (Just (Just x), afterEach' (t - int) ds)+ else return (Just Nothing, afterEach' t d)+++-- | Event dam. Collects all values from the input list and emits one+-- value at each instant.+--+-- Note that this combinator can cause event congestion. If you feed+-- values faster than it can produce, it will leak memory.++dam :: forall a. Wire [a] (Event a)+dam = dam' []+ where+ dam' :: [a] -> Wire [a] (Event a)+ dam' xs =+ mkGen $ \_ ys ->+ case xs ++ ys of+ [] -> return (Just Nothing, dam' [])+ (ee:rest) -> return (Just (Just ee), dam' rest)+++-- | Delay events by the time interval in the left signal.+--+-- Note that this event transformer has to keep all delayed events in+-- memory, which can cause event congestion. If events are fed in+-- faster than they can be produced (for example when the framerate+-- starts to drop), it will leak memory. Use 'delayEventSafe' to+-- prevent this.++delayEvents :: Wire (DTime, Event a) (Event a)+delayEvents = delayEvent' Seq.empty 0+ where+ delayEvent' :: Seq (DTime, a) -> Time -> Wire (DTime, Event a) (Event a)+ delayEvent' es' t' =+ mkGen $ \(wsDTime -> dt) (int, ev) -> do+ let t = t' + dt+ es = t `seq` maybe es' (\ee -> es' |> (t + int, ee)) ev+ case Seq.viewl es of+ Seq.EmptyL -> return (Nothing, delayEvent' es 0)+ (et, ee) :< rest+ | t >= et -> return (Just (Just ee), delayEvent' rest t)+ | otherwise -> return (Just Nothing, delayEvent' es t)+++-- | Delay events by the time interval in the left signal. The event+-- queue is limited to the maximum number of events given by middle+-- signal. If the current queue grows to this size, then temporarily no+-- further events are queued.+--+-- As suggested by the type, this maximum can change over time.+-- However, if it's decreased below the number of currently queued+-- events, the events are not deleted.++delayEventsSafe :: Wire (DTime, Int, Event a) (Event a)+delayEventsSafe = delayEventSafe' Seq.empty 0+ where+ delayEventSafe' :: Seq (DTime, a) -> Time -> Wire (DTime, Int, Event a) (Event a)+ delayEventSafe' es' t' =+ mkGen $ \(wsDTime -> dt) (int, maxEvs, ev') -> do+ let t = t' + dt+ ev = guard (Seq.length es' < maxEvs) >> ev'+ es = t `seq` maybe es' (\ee -> es' |> (t + int, ee)) ev+ case Seq.viewl es of+ Seq.EmptyL -> return (Nothing, delayEventSafe' es 0)+ (et, ee) :< rest+ | t >= et -> return (Just (Just ee), delayEventSafe' rest t)+ | otherwise -> return (Just Nothing, delayEventSafe' es t)+++-- | Decoupled variant of 'hold'.++dHold :: forall a. a -> Wire (Event a) a+dHold x0 = dHold'+ where+ dHold' :: Wire (Event a) a+ dHold' =+ mkGen $ \_ ->+ return . maybe (Just x0, dHold') (\x1 -> (Just x0, dHold x1))+++-- | Drop the given number of events, before passing events through.++dropEvents :: forall a. Int -> Wire (Event a) (Event a)+dropEvents 0 = identity+dropEvents n = drop'+ where+ drop' :: Wire (Event a) (Event a)+ drop' =+ mkGen $ \_ ->+ return .+ maybe (Nothing, drop')+ (const (Nothing, dropEvents (pred n)))+++-- | Timed event gate for the right signal, which begins closed and+-- opens after the time interval in the left signal has passed.++dropFor :: forall a. Wire (DTime, Event a) (Event a)+dropFor = dropFor' 0+ where+ dropFor' :: Time -> Wire (DTime, Event a) (Event a)+ dropFor' t' =+ mkGen $ \(wsDTime -> dt) (int, ev) ->+ let t = t' + dt in+ if t >= int+ then return (Just ev, arr snd)+ else return (Just Nothing, dropFor' t)+++-- | Produce a single event with the right signal whenever the left+-- signal switches from 'False' to 'True'.++edge :: Wire (Bool, a) (Event a)+edge = edgeBy fst snd+++-- | Whenever the predicate in the first argument switches from 'False'+-- to 'True' for the input signal, produce an event carrying the value+-- given by applying the second argument function to the input signal.++edgeBy :: forall a b. (a -> Bool) -> (a -> b) -> Wire a (Event b)+edgeBy p f = edgeBy'+ where+ edgeBy' :: Wire a (Event b)+ edgeBy' =+ mkGen $ \_ subject ->+ if p subject+ then return (Just (Just (f subject)), switchBack)+ else return (Just Nothing, edgeBy')++ switchBack :: Wire a (Event b)+ switchBack =+ mkGen $ \_ subject ->+ if p subject+ then return (Just Nothing, switchBack)+ else return (Just Nothing, edgeBy')+++-- | Produce a single event carrying the value of the input signal,+-- whenever the input signal switches to 'Just'.++edgeJust :: Wire (Maybe a) (Event a)+edgeJust = edgeBy isJust fromJust+++-- | Turn discrete events into continuous signals. Initially produces+-- the argument value. Each time an event occurs, the produced value is+-- switched to the event's value.++hold :: forall a. a -> Wire (Event a) a+hold x0 = hold'+ where+ hold' :: Wire (Event a) a+ hold' =+ mkGen $ \_ ->+ return .+ maybe (Just x0, hold')+ (\x -> (Just x, hold x))+++-- | Never produce an event.++never :: Wire a (Event b)+never = constant Nothing+++-- | Suppress the first event occurence.++notYet :: Wire (Event a) (Event a)+notYet = mkGen $ \_ -> return . maybe (Just Nothing, notYet) (const (Just Nothing, identity))+++-- | Produce an event at the first instant and never again.++now :: b -> Wire a (Event b)+now x = constantAfter Nothing (Just x)+++-- | Pass the first event occurence through and suppress all future+-- events.++once :: Wire (Event a) (Event a)+once =+ mkGen $ \_ ev ->+ case ev of+ Nothing -> return (Just Nothing, once)+ Just _ -> return (Just ev, constant Nothing)+++-- | Emit the right signal event each time the left signal interval+-- passes.++repeatedly :: forall a. Wire (DTime, a) (Event a)+repeatedly = repeatedly' 0+ where+ repeatedly' :: Time -> Wire (DTime, a) (Event a)+ repeatedly' t' =+ mkGen $ \(wsDTime -> dt) (int, x) ->+ let t = t' + dt in+ if t >= int+ then let nextT = fmod t int+ in nextT `seq` return (Just (Just x), repeatedly' nextT)+ else return (Just Nothing, repeatedly' t)+++-- | Each time the signal interval passes emit the next element from the+-- given list.++repeatedlyList :: forall a. [a] -> Wire DTime (Event a)+repeatedlyList = repeatedly' 0+ where+ repeatedly' :: DTime -> [a] -> Wire DTime (Event a)+ repeatedly' _ [] = constant Nothing+ repeatedly' t' x@(x0:xs) =+ mkGen $ \(wsDTime -> dt) int ->+ let t = t' + dt in+ if t >= int+ then let nextT = fmod t int+ in nextT `seq` return (Just (Just x0), repeatedly' nextT xs)+ else return (Just Nothing, repeatedly' t x)+++-- | Pass only the first given number of events. Then suppress events+-- forever.++takeEvents :: Int -> Wire (Event a) (Event a)+takeEvents 0 = constant Nothing+takeEvents n = take'+ where+ take' :: Wire (Event a) (Event a)+ take' =+ mkGen $ \_ ev ->+ case ev of+ Nothing -> return (Just Nothing, take')+ Just _ -> return (Just ev, takeEvents (pred n))+++-- | Timed event gate for the right signal, which starts open and slams+-- shut after the left signal time interval passed.++takeFor :: Wire (DTime, Event a) (Event a)+takeFor = takeFor' 0+ where+ takeFor' :: Time -> Wire (DTime, Event a) (Event a)+ takeFor' t' =+ mkGen $ \(wsDTime -> dt) (int, ev) ->+ let t = t' + dt in+ if t >= int+ then return (Just Nothing, constant Nothing)+ else return (Just ev, takeFor' t)+++-- | Inhibit the signal, unless an event occurs.++wait :: Wire (Event a) a+wait =+ mkGen $ \_ ev ->+ case ev of+ Nothing -> return (Nothing, wait)+ Just _ -> return (ev, wait)
+ FRP/NetWire/IO.hs view
@@ -0,0 +1,67 @@+-- |+-- Module: FRP.NetWire.IO+-- Copyright: (c) 2011 Ertugrul Soeylemez+-- License: BSD3+-- Maintainer: Ertugrul Soeylemez <es@ertes.de>+--+-- Access the rest of the universe.++module FRP.NetWire.IO+ ( -- * IO Actions+ execute,+ executeEvery,+ executeOnce+ )+ where++import Control.Exception+import FRP.NetWire.Tools+import FRP.NetWire.Wire+++-- | Execute the IO action in the input signal at every instant.+--+-- Note: If the action throws an exception, then this wire inhibits the+-- signal.++execute :: Wire (IO a) a+execute =+ mkGen $ \_ c -> do+ mx <- try c+ case mx of+ Left (_ :: SomeException) -> return (Nothing, execute)+ Right x -> return (Just x, execute)+++-- | Executes the IO action in the right input signal periodically+-- keeping its most recent result value.++executeEvery :: forall a. Wire (DTime, IO a) a+executeEvery = executeEvery' True 0 Nothing+ where+ executeEvery' :: Bool -> Time -> Maybe a -> Wire (DTime, IO a) a+ executeEvery' firstRun t' mx' =+ mkGen $ \(wsDTime -> dt) (int, c) ->+ let t = t' + dt in+ if t >= int || firstRun+ then do+ let nextT = fmod t int+ mx <- nextT `seq` try c+ case mx of+ Left (_ :: SomeException) -> return (mx', executeEvery' False nextT mx')+ Right x ->+ let mx = Just x+ in mx `seq` return (mx, executeEvery' False nextT mx)+ else return (mx', executeEvery' False t mx')+++-- | Executes the IO action in the input signal and inhibits, until it+-- succeeds without an exception. Keeps the result forever.++executeOnce :: Wire (IO a) a+executeOnce =+ mkGen $ \_ c -> do+ mx <- try c+ case mx of+ Left (_ :: SomeException) -> return (Nothing, executeOnce)+ Right x -> return (Just x, constant x)
+ FRP/NetWire/Random.hs view
@@ -0,0 +1,60 @@+-- |+-- Module: FRP.NetWire.Random+-- Copyright: (c) 2011 Ertugrul Soeylemez+-- License: BSD3+-- Maintainer: Ertugrul Soeylemez <es@ertes.de>+--+-- Noise generators.++module FRP.NetWire.Random+ ( -- * Noise generators+ noise,+ noise1,+ noiseGen,+ noiseR,+ wackelkontakt+ )+ where++import FRP.NetWire.Wire+import System.Random.Mersenne+++-- | Noise between 0 (inclusive) and 1 (exclusive).++noise :: Wire a Double+noise = noiseGen+++-- | Noise between -1 and 1 exclusive.++noise1 :: Wire a Double+noise1 =+ mkGen $ \(wsRndGen -> mt) _ -> do+ x <- fmap (pred . (2*)) $ random mt+ x `seq` return (Just x, noise1)+++-- | Noise.++noiseGen :: MTRandom b => Wire a b+noiseGen =+ mkGen $ \(wsRndGen -> mt) _ -> do+ x <- random mt+ x `seq` return (Just x, noiseGen)+++-- | Noise between 0 (inclusive) and the input signal (exclusive).++noiseR :: (Real a, Integral b) => Wire a b+noiseR =+ mkGen $ \(wsRndGen -> mt) n -> do+ x' <- random mt+ let x = floor ((x' :: Double) * realToFrac n)+ x `seq` return (Just x, noiseR)+++-- | Random boolean.++wackelkontakt :: Wire a Bool+wackelkontakt = noiseGen
+ FRP/NetWire/Request.hs view
@@ -0,0 +1,30 @@+-- |+-- Module: FRP.NetWire.Request+-- Copyright: (c) 2011 Ertugrul Soeylemez+-- License: BSD3+-- Maintainer: Ertugrul Soeylemez <es@ertes.de>+--+-- Unique identifiers.++module FRP.NetWire.Request+ ( -- * Identifiers.+ identifier+ )+ where++import Control.Concurrent.STM+import FRP.NetWire.Wire+++-- | Choose a unique identifier when switching in and keep it.++identifier :: Wire a Int+identifier =+ mkGen $ \ws _ -> do+ let reqVar = wsReqVar ws+ req <- atomically $ do+ req' <- readTVar reqVar+ let req = succ req'+ req `seq` writeTVar reqVar (succ req')+ return req'+ return (Just req, WConst req)
+ FRP/NetWire/Session.hs view
@@ -0,0 +1,121 @@+-- |+-- Module: FRP.NetWire.Session+-- Copyright: (c) 2011 Ertugrul Soeylemez+-- License: BSD3+-- Maintainer: Ertugrul Soeylemez <es@ertes.de>+--+-- Wire sessions.++module FRP.NetWire.Session+ ( -- * Sessions+ Session(..),+ stepWire,+ stepWireDelta,+ stepWireTime,+ stepWireTime',+ withWire+ )+ where++import Control.Applicative+import Control.Concurrent.STM+import Control.Exception+import Data.IORef+import Data.Time.Clock+import FRP.NetWire.Wire+++-- | Reactive sessions with the given time type.++data Session a b =+ Session {+ sessFreeVar :: TVar Bool, -- ^ False, if in use.+ sessStateRef :: IORef WireState, -- ^ State of the last instant.+ sessTimeRef :: IORef UTCTime, -- ^ Time of the last instant.+ sessWireRef :: IORef (Wire a b) -- ^ Wire for the next instant.+ }+++-- | Feed the given input value into the reactive system performing the+-- next instant using real time.++stepWire :: a -> Session a b -> IO (Maybe b)+stepWire x' sess =+ withBlock sess $ do+ t <- getCurrentTime+ stepWireTime' t x' sess+++-- | Feed the given input value into the reactive system performing the+-- next instant using the given time delta.++stepWireDelta :: NominalDiffTime -> a -> Session a b -> IO (Maybe b)+stepWireDelta dt x' sess =+ withBlock sess $ do+ t' <- readIORef (sessTimeRef sess)+ let t@(UTCTime td tt) = addUTCTime dt t'+ td `seq` tt `seq` t `seq` stepWireTime' t x' sess+++-- | Feed the given input value into the reactive system performing the+-- next instant, which is at the given time. This function is+-- thread-safe.++stepWireTime :: UTCTime -> a -> Session a b -> IO (Maybe b)+stepWireTime t' x' sess = withBlock sess (stepWireTime' t' x' sess)+++-- | Feed the given input value into the reactive system performing the+-- next instant, which is at the given time. This function is *not*+-- thread-safe.++stepWireTime' :: UTCTime -> a -> Session a b -> IO (Maybe b)+stepWireTime' t x' sess = do+ let Session { sessTimeRef = tRef, sessStateRef = wsRef, sessWireRef = wRef+ } = sess++ -- Time delta.+ t' <- readIORef tRef+ let dt = realToFrac (diffUTCTime t t')+ dt `seq` writeIORef tRef t++ -- Wire state.+ ws' <- readIORef wsRef+ let ws = ws' { wsDTime = dt }+ ws `seq` writeIORef wsRef ws++ -- Wire.+ w' <- readIORef wRef+ (x, w) <- toGen w' ws x'+ w `seq` writeIORef wRef w++ return x+++-- | Perform an interlocked step function.++withBlock :: Session a b -> IO c -> IO c+withBlock (Session { sessFreeVar = freeVar }) c = do+ atomically (readTVar freeVar >>= check >> writeTVar freeVar False)+ c `finally` atomically (writeTVar freeVar True)+++-- | Initialize a reactive session and pass it to the given+-- continuation.++withWire :: Wire a b -> (Session a b -> IO c) -> IO c+withWire w k = do+ t@(UTCTime td tt) <- getCurrentTime+ ws <- initWireState++ sess <-+ td `seq` tt `seq` t `seq` ws `seq`+ Session+ <$> newTVarIO True+ <*> newIORef ws+ <*> newIORef t+ <*> newIORef w++ seq sess (k sess)+ `finally`+ (readIORef (sessStateRef sess) >>= cleanupWireState)
+ FRP/NetWire/Switch.hs view
@@ -0,0 +1,184 @@+-- |+-- Module: FRP.NetWire.Switch+-- Copyright: (c) 2011 Ertugrul Soeylemez+-- License: BSD3+-- Maintainer: Ertugrul Soeylemez <es@ertes.de>+--+-- Switching combinators. Note that 'Wire' also provides a+-- state-preserving 'Control.Arrow.ArrowApply' instance, which may be+-- more convenient than these combinators in many cases.++module FRP.NetWire.Switch+ ( -- * Basic switches+ switch, dSwitch,+ rSwitch, drSwitch,++ -- * Broadcasters+ parB,+ rpSwitchB, drpSwitchB,++ -- * Routers+ par,+ rpSwitch, drpSwitch+ )+ where++import qualified Data.Traversable as T+import Data.Traversable (Traversable)+import FRP.NetWire.Wire+++-- | Decoupled variant of 'rpSwitch'.++drpSwitch ::+ Traversable f =>+ (forall w. a -> f w -> f (b, w)) ->+ f (Wire b c) ->+ Wire (a, Event (f (Wire b c) -> f (Wire b c))) (f c)+drpSwitch route wires''' =+ WGen $ \ws (x'', ev) -> do+ let wires'' = route x'' wires'''+ r <- T.sequenceA $ fmap (\(x', w') -> toGen w' ws x') wires''+ let xs = T.sequenceA . fmap fst $ r+ wires' = fmap snd r+ wires = maybe id id ev wires'+ return (xs, rpSwitch route wires)+++-- | Decoupled variant of 'rpSwitchB'.++drpSwitchB ::+ forall a b f. Traversable f =>+ f (Wire a b) ->+ Wire (a, Event (f (Wire a b) -> f (Wire a b))) (f b)+drpSwitchB wires'' =+ WGen $ \ws (x', ev) -> do+ r <- T.sequenceA $ fmap (\w' -> toGen w' ws x') wires''+ let xs = T.sequenceA . fmap fst $ r+ wires' = fmap snd r+ wires = maybe id id ev wires'+ return (xs, rpSwitchB wires)+++-- | Decoupled variant of 'rSwitch'.++drSwitch :: Wire a b -> Wire (a, Event (Wire a b)) b+drSwitch w1' =+ WGen $ \ws (x', swEv) -> do+ (mx, w1) <- toGen w1' ws x'+ let w = maybe w1 id swEv+ w `seq` return (mx, drSwitch w)+++-- | Decoupled variant of 'switch'.++dSwitch :: Wire a (b, Event c) -> (c -> Wire a b) -> Wire a b+dSwitch w1' f =+ WGen $ \ws x' -> do+ (m, w1) <- toGen w1' ws x'+ case m of+ Nothing -> return (Nothing, dSwitch w1 f)+ Just (x, swEv) ->+ case swEv of+ Nothing -> return (Just x, dSwitch w1 f)+ Just sw -> return (Just x, f sw)+++-- | Route signal to a collection of signal functions using the supplied+-- routing function. If any of the wires inhibits, the whole network+-- inhibits.++par ::+ Traversable f =>+ (forall w. a -> f w -> f (b, w)) -> f (Wire b c) -> Wire a (f c)+par route wires'' =+ WGen $ \ws x'' -> do+ let wires' = route x'' wires''+ r <- T.sequenceA $ fmap (\(x', w') -> toGen w' ws x') wires'+ let xs = T.sequenceA . fmap fst $ r+ wires = fmap snd r+ return (xs, par route wires)+++-- | Broadcast signal to a collection of signal functions. If any of+-- the wires inhibits, then the whole parallel network inhibits.++parB :: Traversable f => f (Wire a b) -> Wire a (f b)+parB wires' =+ WGen $ \ws x' -> do+ r <- T.sequenceA $ fmap (\w' -> toGen w' ws x') wires'+ let xs = T.sequenceA . fmap fst $ r+ wires = fmap snd r+ return (xs, parB wires)+++-- | Recurrent parallel routing switch. This combinator acts like+-- 'par', but takes an additional event signal, which can transform the+-- set of wires. This is the most powerful switch.+--+-- Just like 'par' if any of the wires inhibits, the whole network+-- inhibits.++rpSwitch ::+ Traversable f =>+ (forall w. a -> f w -> f (b, w)) ->+ f (Wire b c) ->+ Wire (a, Event (f (Wire b c) -> f (Wire b c))) (f c)+rpSwitch route wires''' =+ WGen $ \ws (x'', ev) -> do+ let wires'' = maybe id id ev wires'''+ wires' = route x'' wires''+ r <- T.sequenceA $ fmap (\(x', w') -> toGen w' ws x') wires'+ let xs = T.sequenceA . fmap fst $ r+ wires = fmap snd r+ return (xs, rpSwitch route wires)+++-- | Recurrent parallel broadcast switch. This combinator acts like+-- 'parB', but takes an additional event signal, which can transform the+-- set of wires.+--+-- Just like 'parB' if any of the wires inhibits, the whole network+-- inhibits.++rpSwitchB ::+ Traversable f =>+ f (Wire a b) -> Wire (a, Event (f (Wire a b) -> f (Wire a b))) (f b)+rpSwitchB wires'' =+ WGen $ \ws (x', ev) -> do+ let wires' = maybe id id ev wires''+ r <- T.sequenceA $ fmap (\w' -> toGen w' ws x') wires'+ let xs = T.sequenceA . fmap fst $ r+ wires = fmap snd r+ return (xs, rpSwitchB wires)+++-- | Combinator for recurrent switches. The wire produced by this+-- switch takes switching events and switches to the wires contained in+-- the events. The first argument is the initial wire.++rSwitch :: Wire a b -> Wire (a, Event (Wire a b)) b+rSwitch w1 =+ WGen $ \ws (x', swEv) -> do+ let w' = maybe w1 id swEv+ (mx, w) <- toGen w' ws x'+ return (mx, rSwitch w)+++-- | This is the most basic switching combinator. It is an event-based+-- one-time switch.+--+-- The first argument is the initial wire, which may produce a switching+-- event at some point. When this event is produced, then the signal+-- path switches to the wire produced by the second argument function.++switch :: Wire a (b, Event c) -> (c -> Wire a b) -> Wire a b+switch w1' f =+ WGen $ \ws x' -> do+ (m, w1) <- toGen w1' ws x'+ case m of+ Nothing -> return (Nothing, switch w1 f)+ Just (x, swEv) ->+ case swEv of+ Nothing -> return (Just x, switch w1 f)+ Just sw -> toGen (f sw) (ws { wsDTime = 0 }) x'
+ FRP/NetWire/Tools.hs view
@@ -0,0 +1,277 @@+-- |+-- Module: FRP.NetWire.Tools+-- Copyright: (c) 2011 Ertugrul Soeylemez+-- License: BSD3+-- Maintainer: Ertugrul Soeylemez <es@ertes.de>+--+-- The usual FRP tools you'll want to work with.++module FRP.NetWire.Tools+ ( -- * Basic utilities+ constant,+ identity,++ -- * Time+ time,+ timeFrom,++ -- * Signal transformers+ discrete,+ keep,++ -- * Inhibitors+ inhibit,+ require,++ -- * Wire transformers+ exhibit,+ freeze,+ sample,+ swallow,+ (-->),+ (>--),+ (-=>),+ (>=-),++ -- * Switches+ -- ** Unconditional switches+ constantAfter,+ initially,++ -- * Arrow tools+ mapA,++ -- * Convenience functions+ dup,+ fmod,+ swap+ )+ where++import Control.Arrow+import Control.Category hiding ((.))+import FRP.NetWire.Wire+import Prelude hiding (id)+++-- | Override the output value at the first non-inhibited instant.++(-->) :: b -> Wire a b -> Wire a b+y --> w' =+ WGen $ \ws x -> do+ (mx, w) <- toGen w' ws x+ case mx of+ Nothing -> return (Nothing, y --> w)+ Just _ -> return (Just y, w)+++-- | Override the input value, until the wire starts producing.++(>--) :: a -> Wire a b -> Wire a b+x' >-- w' =+ WGen $ \ws _ -> do+ (mx, w) <- toGen w' ws x'+ return (mx, maybe (x' >-- w) (const w) mx)+++-- | Apply a function to the wire's output at the first non-inhibited+-- instant.++(-=>) :: (b -> b) -> Wire a b -> Wire a b+f -=> w' =+ WGen $ \ws x' -> do+ (mx, w) <- toGen w' ws x'+ case mx of+ Nothing -> return (Nothing, f -=> w)+ Just x -> return (Just (f x), w)+++-- | Apply a function to the wire's input, until the wire starts+-- producing.++(>=-) :: (a -> a) -> Wire a b -> Wire a b+f >=- w' =+ WGen $ \ws x' -> do+ (mx, w) <- toGen w' ws (f x')+ case mx of+ Nothing -> return (Nothing, f >=- w)+ Just x -> return (Just x, w)+++-- | The constant wire. Please use this function instead of @arr (const+-- c)@.++constant :: b -> Wire a b+constant = WConst+++-- | Produce the value of the second argument at the first instant.+-- Then produce the second value forever.++constantAfter :: b -> b -> Wire a b+constantAfter x1 x0 =+ mkGen $ \_ _ -> return (Just x0, constant x1)+++-- | Turn a continuous signal into a discrete one. This transformer+-- picks values from the right signal at intervals of the left signal.+--+-- The interval length is followed in real time. If it's zero, then+-- this wire acts like @second id@.++discrete :: forall a. Wire (DTime, a) a+discrete =+ mkGen $ \(wsDTime -> dt) (_, x0) ->+ return (Just x0, discrete' dt x0)++ where+ discrete' :: Time -> a -> Wire (DTime, a) a+ discrete' t' x' =+ mkGen $ \(wsDTime -> dt) (int, x) ->+ let t = t' + dt in+ if t >= int+ then return (Just x, discrete' (fmod t int) x)+ else return (Just x', discrete' t x')+++-- | Duplicate a value to a tuple.++dup :: a -> (a, a)+dup x = (x, x)+++-- | This function corresponds to 'try' for exceptions, allowing you to+-- observe inhibited signals.++exhibit :: Wire a b -> Wire a (Maybe b)+exhibit w' =+ WGen $ \ws x' -> do+ (mx, w) <- toGen w' ws x'+ return (Just mx, exhibit w)+++-- | Floating point modulo operation. Note that @fmod n 0@ = 0.++fmod :: Double -> Double -> Double+fmod _ 0 = 0+fmod n d = n - d * realToFrac (floor $ n/d)+++-- | Effectively prevent a wire from rewiring itself. This function+-- will turn any stateful wire into a stateless wire, rendering most+-- wires useless.+--+-- Note: This function should not be used normally. Use it only, if+-- you know exactly what you're doing.++freeze :: Wire a b -> Wire a b+freeze w =+ WGen $ \ws x' -> do+ (mx, _) <- toGen w ws x'+ return (mx, w)+++-- | Identity signal transformer. Outputs its input.++identity :: Wire a a+identity = id+++-- | Unconditional inhibition. Equivalent to 'zeroArrow'.++inhibit :: Wire a b+inhibit = zeroArrow+++-- | Produce the argument value at the first instant. Then act as the+-- identity signal transformer forever.++initially :: a -> Wire a a+initially x0 =+ mkGen $ \_ _ -> return (Just x0, identity)+++-- | Keep the value in the first instant forever.++keep :: Wire a a+keep = mkGen $ \_ x -> return (Just x, constant x)+++-- | Apply an arrow to a list of inputs.++mapA :: ArrowChoice a => a b c -> a [b] [c]+mapA a =+ proc x ->+ case x of+ [] -> returnA -< []+ (x0:xs) -> arr (uncurry (:)) <<< a *** mapA a -< (x0, xs)+++-- | Inhibit right signal, when the left signal is false.++require :: Wire (Bool, a) a+require =+ mkGen $ \_ (b, x) ->+ return (if b then Just x else Nothing, require)+++-- | Sample the given wire at specific intervals. Use this instead of+-- 'discrete', if you want to prevent the signal from passing through+-- the wire all the time.+--+-- The left signal interval is allowed to become zero, at which point+-- the signal is passed through the wire at every instant.++sample :: Wire a b -> Wire (DTime, a) b+sample w' =+ WGen $ \ws@(wsDTime -> dt) (_, x') -> do+ (mx, w) <- toGen w' ws x'+ return (mx, sample' dt mx w)++ where+ sample' :: Time -> Maybe b -> Wire a b -> Wire (DTime, a) b+ sample' t' mx' w' =+ WGen $ \ws@(wsDTime -> dt) (int, x'') ->+ let t = t' + dt in+ if t >= int || int <= 0+ then do+ (mx, w) <- toGen w' ws x''+ let nextT = fmod t int+ case mx of+ Nothing -> nextT `seq` return (mx', sample' nextT mx' w)+ Just _ -> nextT `seq` return (mx, sample' nextT mx w)+ else+ return (mx', sample' t mx' w')+++-- | Wait for the first signal from the given wire and keep it forever.++swallow :: Wire a b -> Wire a b+swallow w' =+ WGen $ \ws x' -> do+ (mx, w) <- toGen w' ws x'+ case mx of+ Nothing -> return (Nothing, swallow w)+ Just x -> do+ return (Just x, constant x)+++-- | Swap the values in a tuple.++swap :: (a, b) -> (b, a)+swap (x, y) = (y, x)+++-- | Get the local time.++time :: Wire a Time+time = timeFrom 0+++-- | Get the local time, assuming it starts from the given value.++timeFrom :: Time -> Wire a Time+timeFrom t' =+ mkGen $ \ws _ ->+ let t = t' + wsDTime ws+ in t `seq` return (Just t, timeFrom t)
+ FRP/NetWire/Wire.hs view
@@ -0,0 +1,264 @@+-- |+-- Module: FRP.NetWire.Wire+-- Copyright: (c) 2011 Ertugrul Soeylemez+-- License: BSD3+-- Maintainer: Ertugrul Soeylemez <es@ertes.de>+--+-- The module contains the main 'Wire' type.++module FRP.NetWire.Wire+ ( -- * Wires+ Wire(..),+ WireState(..),++ -- * Auxilliary types+ DTime,+ Event,+ Time,++ -- * Utilities+ cleanupWireState,+ initWireState,+ mkGen,+ toGen+ )+ where++import Control.Applicative+import Control.Arrow+import Control.Category+import Control.Concurrent.STM+import Prelude hiding ((.), id)+import System.Random.Mersenne+++-- | Derivative of time. In English: It's the time between two+-- instants of an FRP session.++type DTime = Double+++-- | Events are signals, which can be absent. They usually denote+-- discrete occurences of certain events.++type Event = Maybe+++-- | Time.++type Time = Double+++-- | A wire is a network of signal transformers.++data Wire a b where+ WArr :: (a -> b) -> Wire a b+ WConst :: b -> Wire a b+ WGen :: (WireState -> a -> IO (Maybe b, Wire a b)) -> Wire a b+ WId :: Wire a a+++instance Alternative (Wire a) where+ empty = zeroArrow+ (<|>) = (<+>)+++instance Applicative (Wire a) where+ pure = WConst++ wf' <*> wx' =+ WGen $ \ws x' -> do+ (mf, wf) <- toGen wf' ws x'+ (mx, wx) <- toGen wx' ws x'+ return (mf <*> mx, wf <*> wx)+++instance Arrow Wire where+ arr = WArr++ first (WGen f) =+ WGen $ \ws (x', y) -> do+ (mx, w) <- f ws x'+ return (fmap (,y) mx, first w)+ first (WArr f) = WArr (first f)+ first (WConst c) = WArr (first (const c))+ first WId = WId++ second (WGen f) =+ WGen $ \ws (x, y') -> do+ (my, w) <- f ws y'+ return (fmap (x,) my, second w)+ second (WArr f) = WArr (second f)+ second (WConst c) = WArr (second (const c))+ second WId = WId++ wf *** WId = first wf+ WId *** wg = second wg+ wf' *** wg' =+ WGen $ \ws (x', y') -> do+ (mx, wf) <- toGen wf' ws x'+ (my, wg) <- toGen wg' ws y'+ return (liftA2 (,) mx my, wf *** wg)++ wf' &&& wg' =+ WGen $ \ws x' -> do+ (mx1, wf) <- toGen wf' ws x'+ (mx2, wg) <- toGen wg' ws x'+ return (liftA2 (,) mx1 mx2, wf &&& wg)+++instance ArrowChoice Wire where+ left w' = wl+ where+ wl =+ WGen $ \ws mx' ->+ case mx' of+ Left x' -> do+ (mx, w) <- toGen w' ws x'+ return (fmap Left mx, left w)+ Right x -> return (Just (Right x), wl)++ right w' = wl+ where+ wl =+ WGen $ \ws mx' ->+ case mx' of+ Right x' -> do+ (mx, w) <- toGen w' ws x'+ return (fmap Right mx, right w)+ Left x -> return (Just (Left x), wl)++ wf' +++ wg' =+ WGen $ \ws mx' ->+ case mx' of+ Left x' -> do+ (mx, wf) <- toGen wf' ws x'+ return (fmap Left mx, wf +++ wg')+ Right x' -> do+ (mx, wg) <- toGen wg' ws x'+ return (fmap Right mx, wf' +++ wg)++ wf' ||| wg' =+ WGen $ \ws mx' ->+ case mx' of+ Left x' -> do+ (mx, wf) <- toGen wf' ws x'+ return (mx, wf ||| wg')+ Right x' -> do+ (mx, wg) <- toGen wg' ws x'+ return (mx, wf' ||| wg)+++instance ArrowPlus Wire where+ WGen f <+> wg =+ WGen $ \ws x' -> do+ (mx, w1) <- f ws x'+ case mx of+ Just _ -> return (mx, w1 <+> wg)+ Nothing -> do+ (mx2, w2) <- toGen wg ws x'+ return (mx2, w1 <+> w2)++ wf <+> WGen _ = WGen (toGen wf)++ wa@(WArr _) <+> _ = wa+ wc@(WConst _) <+> _ = wc+ WId <+> _ = WId+++instance ArrowZero Wire where+ zeroArrow = mkGen $ \_ _ -> return (Nothing, zeroArrow)+++instance Category Wire where+ id = WId++ -- Combining two general wires.+ wf@(WGen f) . WGen g =+ WGen $ \ws x'' -> do+ (mx', w1) <- g ws x''+ case mx' of+ Nothing -> return (Nothing, wf . w1)+ Just x' -> do+ (mx, w2) <- f ws x'+ return (mx, w2 . w1)++ -- Combining a special wire with a general wire.+ wf@(WArr f) . WGen g =+ WGen $ \ws x' -> do+ (mx, w) <- g ws x'+ return (fmap f mx, wf . w)+ wc@(WConst c) . WGen g =+ WGen $ \ws x' -> do+ (mx, w) <- g ws x'+ return (fmap (const c) mx, wc . w)+ WGen f . wg@(WArr g) =+ WGen $ \ws x' -> do+ (mx, w) <- f ws (g x')+ return (mx, w . wg)+ WGen f . wc@(WConst c) =+ WGen $ \ws _ -> do+ (mx, w) <- f ws c+ return (mx, w . wc)++ -- Combining special wires.+ WArr f . WArr g = WArr (f . g)+ WArr f . WConst c = WArr (const (f c))++ WConst c . WArr _ = WConst c+ WConst c . WConst _ = WConst c++ WId . w2 = w2+ w1 . WId = w1+++instance Functor (Wire a) where+ fmap f (WGen w') =+ WGen $ \ws x' -> do+ (x, w) <- w' ws x'+ return (fmap f x, fmap f w)+ fmap f (WArr g) = WArr (f . g)+ fmap f (WConst c) = WConst (f c)+ fmap f WId = WArr f+++-- | The state of the wire.++data WireState =+ WireState {+ wsDTime :: Double, -- ^ Time difference for current instant.+ wsRndGen :: MTGen, -- ^ Random number generator.+ wsReqVar :: TVar Int -- ^ Request counter.+ }+++-- | Clean up wire state.++cleanupWireState :: WireState -> IO ()+cleanupWireState _ = return ()+++-- | Initialize wire state.++initWireState :: IO WireState+initWireState =+ WireState+ <$> pure 0+ <*> getStdGen+ <*> newTVarIO 0+++-- | Create a generic wire from the given function. This is a smart+-- constructor. Please use it instead of the 'WGen' constructor.++mkGen :: (WireState -> a -> IO (Maybe b, Wire a b)) -> Wire a b+mkGen = WGen+++-- | Extract the transition function of a wire.++toGen :: Wire a b -> WireState -> a -> IO (Maybe b, Wire a b)+toGen (WGen f) ws x = f ws x+toGen wf@(WArr f) _ x = return (Just (f x), wf)+toGen wc@(WConst c) _ _ = return (Just c, wc)+toGen wi@WId _ x = return (Just x, wi)
+ LICENSE view
@@ -0,0 +1,32 @@+netwire license+Copyright (c) 2011, Ertugrul Soeylemez++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are+met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ * Redistributions in binary form must reproduce the above copyright+ notice, this list of conditions and the following disclaimer in+ the documentation and/or other materials provided with the+ distribution.++ * Neither the name of the author nor the names of any contributors+ may be used to endorse or promote products derived from this+ software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS+IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER+OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.lhs view
@@ -0,0 +1,12 @@+Netwire setup script+Copyright (C) 2011, Ertugrul Soeylemez++Please see the LICENSE file for terms and conditions of use,+modification and distribution of this package, including this file.++> module Main where+>+> import Distribution.Simple+>+> main :: IO ()+> main = defaultMain
+ netwire.cabal view
@@ -0,0 +1,62 @@+Name: netwire+Version: 1.0.0+Category: FRP, Network+Synopsis: Arrowized FRP implementation+Maintainer: Ertugrul Söylemez <es@ertes.de>+Author: Ertugrul Söylemez <es@ertes.de>+Copyright: (c) 2011 Ertugrul Söylemez+License: BSD3+License-file: LICENSE+Build-type: Simple+Stability: experimental+Cabal-version: >= 1.8+Description:++ This library provides an arrowized functional reactive programming+ (FRP) implementation. It is similar to Yampa and Animas, but has a+ much simpler internal representation and a lot of new features.++Library+ Build-depends:+ base >= 4 && <= 5,+ containers >= 0.4.0,+ deepseq >= 1.1.0,+ mersenne-random >= 1.0.0,+ stm >= 2.2.0,+ time >= 1.2.0,+ vector >= 0.7.1,+ vector-space >= 0.7.3+ Extensions:+ Arrows+ GADTs+ RankNTypes+ ScopedTypeVariables+ TupleSections+ TypeFamilies+ ViewPatterns+ GHC-Options: -W+ Exposed-modules:+ FRP.NetWire+ FRP.NetWire.Analyze+ FRP.NetWire.Calculus+ -- FRP.NetWire.Concurrent+ FRP.NetWire.Event+ FRP.NetWire.IO+ FRP.NetWire.Random+ FRP.NetWire.Request+ FRP.NetWire.Session+ FRP.NetWire.Switch+ FRP.NetWire.Tools+ FRP.NetWire.Wire++-- Executable netwire-test+-- Build-depends:+-- base >= 4 && <= 5,+-- netwire,+-- time+-- Extensions:+-- Arrows,+-- ScopedTypeVariables+-- Hs-Source-Dirs: test+-- Main-is: Main.hs+-- GHC-Options: -W -threaded -rtsopts