packages feed

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 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