wires (empty) → 0.1.0
raw patch · 13 files changed
+876/−0 lines, 13 filesdep +basedep +clockdep +containerssetup-changed
Dependencies added: base, clock, containers, deepseq, profunctors, semigroupoids, these, wires
Files
- CHANGELOG.md +3/−0
- Control/Wire.hs +17/−0
- Control/Wire/Controller.hs +48/−0
- Control/Wire/Core.hs +157/−0
- Control/Wire/Internal.hs +203/−0
- Control/Wire/Utils.hs +56/−0
- Control/Wire/Varying.hs +155/−0
- LICENSE +27/−0
- README.md +43/−0
- Setup.lhs +6/−0
- examples/Utils.hs +29/−0
- examples/ping-pong.hs +71/−0
- wires.cabal +61/−0
+ CHANGELOG.md view
@@ -0,0 +1,3 @@+# 0.1.0++ * Initial version.
+ Control/Wire.hs view
@@ -0,0 +1,17 @@+-- |+-- Copyright: (c) 2016 Ertugrul Söylemez+-- License: BSD3+-- Maintainer: Ertugrul Söylemez <esz@posteo.de>+-- Stability: experimental++module Control.Wire+ ( -- * Wires+ module Control.Category,+ module Control.Wire.Core,+ module Control.Wire.Utils+ )+ where++import Control.Category+import Control.Wire.Core+import Control.Wire.Utils
+ Control/Wire/Controller.hs view
@@ -0,0 +1,48 @@+-- |+-- Copyright: (c) 2016 Ertugrul Söylemez+-- License: BSD3+-- Maintainer: Ertugrul Söylemez <esz@posteo.de>+-- Stability: experimental++{-# LANGUAGE TupleSections #-}++module Control.Wire.Controller+ ( -- * Controllers+ animate,+ control,+ newEvent,+ onEvent,+ stepWire+ )+ where++import Control.Wire.Internal+++-- | Run the given action in every frame.++animate :: (Applicative m) => Wire m (m a) a+animate = let w = Wire (fmap (, w)) in w+++-- | Run the given wire until its result event occurs.++control :: (Monad m) => Wire m () (Event b) -> m b+control w' = do+ (mx, w) <- stepWire w' ()+ event (control w) pure mx+++-- | Construct an event from the given polling function.++newEvent :: (Functor m) => Wire m (m (Maybe b)) (Event b)+newEvent = Wire $ fmap (\mx -> (maybe NotNow Now mx, newEvent))+++-- | Run the given action whenever the given event occurs.++onEvent :: (Applicative m) => Wire m (Event (m a)) (Event a)+onEvent =+ Wire $+ event (pure (NotNow, onEvent))+ (fmap $ \x -> (Now x, onEvent))
+ Control/Wire/Core.hs view
@@ -0,0 +1,157 @@+-- |+-- Copyright: (c) 2016 Ertugrul Söylemez+-- License: BSD3+-- Maintainer: Ertugrul Söylemez <esz@posteo.de>+-- Stability: experimental++{-# LANGUAGE RankNTypes #-}++module Control.Wire.Core+ ( -- * Wires+ Wire,+ evalWith,+ initial,+ withM,++ -- * Events+ Event,+ catMapE,+ hold,+ hold',+ never,+ unfoldE,++ -- * Switching+ manage,+ manage',+ sequenceW,+ switch,+ switch'+ )+ where++import Control.Wire.Internal+++-- | Map and filter event occurrences using the given function.++catMapE :: (a -> Maybe b) -> Event a -> Event b+catMapE f = event NotNow (maybe NotNow Now . f)+++-- | Evaluate the input using the given strategy in every frame. Valid+-- arguments include functions like `seq`.++evalWith :: (Applicative m) => (forall b. a -> b -> b) -> Wire m a a+evalWith strat = let w = Wire (\x -> x `strat` pure (x, w)) in w+++-- | Hold the latest occurrence of the given event starting with the+-- given initial value. The value switch occurs in the next frame.++hold :: (Applicative m) => a -> Wire m (Event a) a+hold x' = delayW x' (hold' x')+++-- | Hold the latest occurrence of the given event starting with the+-- given initial value. The value switch occurs instantly.++hold' :: (Applicative m) => a -> Wire m (Event a) a+hold' x' = Wire $ (\x -> pure (x, hold' x)) . event x' id+++-- | Run the given action once at the beginning.++initial :: (Applicative m) => Wire m (m a) a+initial = Wire $ fmap (\y -> (y, pure y))+++-- | Sequence each of the given wires and collect their results. If the+-- given event occurs, the its function is applied to the current set of+-- wires. Changes are applied in the next frame.++manage+ :: (Traversable f, Applicative m)+ => f (Wire m a b)+ -> Wire m (a, Event (f (Wire m a b) -> f (Wire m a b))) (f b)+manage ws' =+ Wire $ \(x, mf) ->+ (\ys -> (fst <$> ys,+ manage (event id id mf (snd <$> ys))))+ <$> traverse (`stepWire` x) ws'+++-- | Sequence each of the given wires and collect their results. If the+-- given event occurs, the its function is applied to the current set of+-- wires. Changes are applied immediately.++manage'+ :: (Traversable f, Applicative m)+ => f (Wire m a b)+ -> Wire m (a, Event (f (Wire m a b) -> f (Wire m a b))) (f b)+manage' ws' =+ Wire $ \(x, mf) ->+ (\ys -> (fst <$> ys,+ manage (snd <$> ys)))+ <$> traverse (`stepWire` x) (event id id mf ws')+++-- | The event that never occurs.++never :: Event a+never = NotNow+++-- | Sequence each of the given wires and collect their results.++sequenceW :: (Traversable f, Applicative m) => f (Wire m a b) -> Wire m a (f b)+sequenceW ws' =+ Wire $ \x ->+ (\ys -> (fst <$> ys,+ sequenceW (snd <$> ys)))+ <$> traverse (\w' -> stepWire w' x) ws'+++-- | Acts like the given wire until its event occurs, then switches to+-- the wire the occurrence contained. The switch occurs in the next+-- frame.++switch :: (Functor m) => Wire m a (b, Event (Wire m a b)) -> Wire m a b+switch w' =+ Wire $ \x ->+ (\((y, mw), w) -> (y, event (switch w) id mw))+ <$> stepWire w' x+++-- | Acts like the given wire until its event occurs, then switches to+-- the wire the occurrence contained. The switch occurs immediately.++switch' :: (Monad m) => Wire m a (b, Event (Wire m a b)) -> Wire m a b+switch' w' =+ Wire $ \x -> do+ ((y, mw), w) <- stepWire w' x+ case mw of+ NotNow -> pure (y, switch w)+ Now nw -> stepWire nw x+++-- | Unfold the given event using the state transition functions it+-- carries.++unfoldE :: (Applicative m) => s -> Wire m (Event (s -> (a, s))) (Event a)+unfoldE s' =+ Wire $ \mf ->+ pure (case mf of+ NotNow -> (NotNow, unfoldE s')+ Now f -> let (x, s) = f s' in (Now x, unfoldE s))+++-- | Run the given action to initialise the given wire. Example:+--+-- > withM (scan f) actionFromInitialInput++withM :: (Monad m) => (s -> Wire m a b) -> (a -> m s) -> Wire m a b+withM w f =+ Wire $ \x -> do+ s0 <- f x+ stepWire (w s0) x
+ Control/Wire/Internal.hs view
@@ -0,0 +1,203 @@+-- |+-- Copyright: (c) 2016 Ertugrul Söylemez+-- License: BSD3+-- Maintainer: Ertugrul Söylemez <esz@posteo.de>+-- Stability: experimental++{-# LANGUAGE DeriveFunctor #-}++module Control.Wire.Internal+ ( -- * Wires+ Wire(..),+ delayW,++ -- * Events+ Event(..),+ event+ )+ where++import Control.Arrow+import Control.Category+import Control.DeepSeq+import Control.Monad.Fix+import Data.Align+import Data.Functor.Bind+import Data.Functor.Extend+import Data.Functor.Plus+import Data.Profunctor+import Data.These+import Prelude hiding ((.), id)+++-- | An event is a timestamped stream of occurrences with payloads of+-- the given type.++data Event a+ = NotNow -- ^ Not in this frame.+ | Now a -- ^ In this frame with the given value.+ deriving (Functor)++instance Align Event where+ nil = NotNow++ alignWith f (Now x) (Now y) = Now (f (These x y))+ alignWith f (Now x) _ = Now (f (This x))+ alignWith f _ (Now y) = Now (f (That y))+ alignWith _ _ _ = NotNow++instance Alt Event where+ (<!>) = alignWith (mergeThese const)++instance Apply Event where+ Now f <.> Now x = Now (f x)+ _ <.> _ = NotNow++instance Bind Event where+ Now x >>- f = event NotNow Now (f x)+ NotNow >>- _ = NotNow++ join (Now (Now x)) = Now x+ join _ = NotNow++instance Extend Event where+ duplicated = event NotNow (Now . Now)+ extended f = event NotNow (Now . f . Now)++instance (Monoid a) => Monoid (Event a) where+ mappend = alignWith (mergeThese mappend)+ mempty = nil++instance (NFData a) => NFData (Event a) where+ rnf NotNow = ()+ rnf (Now x) = rnf x++instance Plus Event where+ zero = nil+++-- | 'Wire' is a language for defining reactive systems. It is similar+-- to the underlying monad @m@, but runs continuously.++newtype Wire m a b =+ Wire {+ -- | Run a single frame of the given wire.+ stepWire :: a -> m (b, Wire m a b)+ }+ deriving (Functor)++instance (Applicative m) => Applicative (Wire m a) where+ pure x = let w = Wire (\_ -> pure (x, w)) in w++ wf' <*> wx' =+ Wire $ \x' ->+ (\(f, wf) (x, wx) -> (f x, wf <*> wx))+ <$> stepWire wf' x'+ <*> stepWire wx' x'++instance (Monad m) => Arrow (Wire m) where+ arr f = let w = Wire (\x -> pure (f x, w)) in w+ first = first'+ second = second'++ wx' &&& wy' =+ Wire $ \x' ->+ (\(x, wx) (y, wy) -> ((x, y), wx &&& wy))+ <$> stepWire wx' x'+ <*> stepWire wy' x'++ wx' *** wy' =+ Wire $ \(x', y') ->+ (\(x, wx) (y, wy) -> ((x, y), wx *** wy))+ <$> stepWire wx' x'+ <*> stepWire wy' y'++instance (Monad m) => ArrowChoice (Wire m) where+ left = left'+ right = right'++ wl' +++ wr' =+ Wire $+ either (\x -> (\(y, wl) -> (Left y, wl +++ wr')) <$> stepWire wl' x)+ (\x -> (\(y, wr) -> (Right y, wl' +++ wr)) <$> stepWire wr' x)++ wl' ||| wr' =+ Wire $+ either (\x -> (\(y, wl) -> (y, wl ||| wr')) <$> stepWire wl' x)+ (\x -> (\(y, wr) -> (y, wl' ||| wr)) <$> stepWire wr' x)++instance (MonadFix m) => ArrowLoop (Wire m) where+ loop = unfirst++instance (Monad m) => Category (Wire m) where+ id = let w = Wire (\x -> pure (x, w)) in w++ w2' . w1' =+ Wire $ \x0 -> do+ (x1, w1) <- stepWire w1' x0+ (x2, w2) <- stepWire w2' x1+ pure (x2, w2 . w1)++instance (Applicative m) => Choice (Wire m) where+ left' w' =+ Wire $+ either (\x -> (\(y, w) -> (Left y, left' w)) <$> stepWire w' x)+ (\x -> pure (Right x, left' w'))++ right' w' =+ Wire $+ either (\x -> pure (Left x, right' w'))+ (\x -> (\(y, w) -> (Right y, right' w)) <$> stepWire w' x)++instance (MonadFix m) => Costrong (Wire m) where+ unfirst w' =+ Wire $ \x' ->+ (\((x, _), w) -> (x, unfirst w))+ <$> mfix (\r -> stepWire w' (x', snd (fst r)))++ unsecond w' =+ Wire $ \x' ->+ (\((_, x), w) -> (x, unsecond w))+ <$> mfix (\r -> stepWire w' (fst (fst r), x'))++instance (Functor m) => Profunctor (Wire m) where+ dimap fl fr = go+ where+ go w' = Wire (fmap (\(y, w) -> (fr y, go w)) . stepWire w' . fl)++ lmap f = go+ where+ go w' = Wire (fmap (\(y, w) -> (y, go w)) . stepWire w' . f)++ rmap = fmap++instance (Functor m) => Strong (Wire m) where+ first' w' =+ Wire $ \(x', y) ->+ (\(x, w) -> ((x, y), first' w))+ <$> stepWire w' x'++ second' w' =+ Wire $ \(x, y') ->+ (\(y, w) -> ((x, y), second' w))+ <$> stepWire w' y'+++-- | Delay the result of the given wire by one frame.++delayW :: (Functor m) => b -> Wire m a b -> Wire m a b+delayW y' w' =+ Wire $ \x ->+ (\(y, w) -> (y', delayW y w))+ <$> stepWire w' x+++-- | Fold the given event.++event+ :: r -- ^ Not now.+ -> (a -> r) -- ^ Now.+ -> Event a -- ^ Event to fold.+ -> r+event k _ NotNow = k+event _ k (Now x) = k x
+ Control/Wire/Utils.hs view
@@ -0,0 +1,56 @@+-- |+-- Copyright: (c) 2016 Ertugrul Söylemez+-- License: BSD3+-- Maintainer: Ertugrul Söylemez <esz@posteo.de>+-- Stability: experimental++module Control.Wire.Utils+ ( -- * Wire utilities+ withM_,++ -- * Event utilities+ filterE,+ scan,+ scan',+ scanE+ )+ where++import Control.Category+import Control.Wire.Core+import Data.Profunctor+import Prelude hiding ((.), id)+++-- | Filter event occurrences using the given function.++filterE :: (a -> Bool) -> Event a -> Event a+filterE p = catMapE (\x -> if p x then Just x else Nothing)+++-- | Left scan and hold of the given event.++scan :: (Monad m) => a -> Wire m (Event (a -> a)) a+scan x0 = hold x0 . scanE x0+++-- | Left scan and hold of the given event. The value switch occurs+-- instantly.++scan' :: (Monad m) => a -> Wire m (Event (a -> a)) a+scan' x0 = hold' x0 . scanE x0+++-- | Left scan of the given event.++scanE :: (Applicative m) => a -> Wire m (Event (a -> a)) (Event a)+scanE = lmap (fmap $ \f x -> let y = f x in (y, y)) . unfoldE+++-- | Run the given action to initialise the given wire. Simplified+-- variant of 'withM'. Example:+--+-- > withM_ (scan f) action++withM_ :: (Monad m) => (s -> Wire m a b) -> m s -> Wire m a b+withM_ w c = withM w (const c)
+ Control/Wire/Varying.hs view
@@ -0,0 +1,155 @@+-- |+-- Copyright: (c) 2016 Ertugrul Söylemez+-- License: BSD3+-- Maintainer: Ertugrul Söylemez <esz@posteo.de>+-- Stability: experimental++{-# LANGUAGE CPP #-}+{-# LANGUAGE DeriveFoldable #-}+{-# LANGUAGE DeriveFunctor #-}++module Control.Wire.Varying+ ( -- * Time-varying values+ Varying,+ changes,+ value,++ -- * Events+ holdV,+ holdV',+ scanV,++ -- * Controllers+ animateV+ )+ where++import Control.Applicative+import Control.DeepSeq+import Control.Monad+import Control.Wire.Internal+import Data.String+#if MIN_VERSION_base(4,9,0)+import Numeric+#endif+++-- | A 'Varying' is a behaviour combined with an event that tracks when+-- the value changes.++data Varying a = Varying !Bool !a+ deriving (Foldable, Functor)++instance Applicative Varying where+ pure = Varying False+ (<*>) = ap++instance (Bounded a) => Bounded (Varying a) where+ minBound = pure minBound+ maxBound = pure maxBound++instance (NFData a) => NFData (Varying a) where+ rnf (Varying _ x) = rnf x++instance (Floating a) => Floating (Varying a) where+ (**) = liftA2 (**)+ pi = pure pi+ exp = fmap exp+ log = fmap log+ sqrt = fmap sqrt+ logBase = liftA2 logBase++ sin = fmap sin; asin = fmap asin; sinh = fmap sinh; asinh = fmap asinh+ cos = fmap cos; acos = fmap acos; cosh = fmap cosh; acosh = fmap acosh+ tan = fmap tan; atan = fmap atan; tanh = fmap tanh; atanh = fmap atanh++#if MIN_VERSION_base(4,9,0)+ log1p = fmap log1p+ expm1 = fmap expm1+ log1pexp = fmap log1pexp+ log1mexp = fmap log1mexp+#endif++instance (Fractional a) => Fractional (Varying a) where+ (/) = liftA2 (/)+ fromRational = pure . fromRational+ recip = fmap recip++instance (IsString a) => IsString (Varying a) where+ fromString = pure . fromString++instance Monad Varying where+ Varying cx x >>= f =+ let Varying cy y = f x+ in Varying (cx || cy) y++instance (Num a) => Num (Varying a) where+ (+) = liftA2 (+)+ (-) = liftA2 (-)+ (*) = liftA2 (*)+ abs = fmap abs+ fromInteger = pure . fromInteger+ negate = fmap negate+ signum = fmap signum+++-- | Run the given action each time the given time-varying value+-- changes.++animateV :: (Applicative m) => (a -> m b) -> Wire m (Varying a) (Varying b)+animateV f =+ Wire $ \(Varying cx x) -> do+ (\y -> (Varying cx y, go y))+ <$> f x++ where+ go y' =+ Wire $ \(Varying cx x) ->+ if cx+ then (\y -> (Varying True y, go y)) <$> f x+ else pure (Varying False y', go y')+++-- | The change event of the given time-varying value.++changes :: Varying a -> Event a+changes (Varying cx x) = if cx then Now x else NotNow+++-- | Hold the latest occurrence of the given event starting with the+-- given initial value. The value switch occurs in the next frame.++holdV :: (Applicative m) => a -> Wire m (Event a) (Varying a)+holdV = go . Varying False+ where+ go x' =+ Wire $ \mx ->+ pure (x', go (event (Varying False (value x')) (Varying True) mx))+++-- | Hold the latest occurrence of the given event starting with the+-- given initial value. The value switch occurs instantly.++holdV' :: (Applicative m) => a -> Wire m (Event a) (Varying a)+holdV' x' =+ Wire $ \mx ->+ pure $+ case mx of+ NotNow -> (Varying False x', holdV' x')+ Now x -> (Varying True x, holdV' x)+++-- | Left scan and hold of the given event.++scanV :: (Applicative m) => (a -> b -> b) -> b -> Wire m (Event a) (Varying b)+scanV f = go . Varying False+ where+ go x' =+ Wire $ \mdx ->+ pure (x', go (event (Varying False) (\dx -> Varying True . f dx) mdx (value x')))+++-- | The behaviour value of the given time-varying value.++value :: Varying a -> a+value (Varying _ x) = x
+ LICENSE view
@@ -0,0 +1,27 @@+Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are+met:++ (1) Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ (2) 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.++ (3)The name of the author may not be used to endorse or promote+ products derived from this software without specific prior written+ permission.++THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ README.md view
@@ -0,0 +1,43 @@+Wires+=====++This is a functional reactive programming library for interactive+applications with the following features:++ * heavy focus on real-time applications like games and simulations,++ * very small core abstraction,++ * efficient in both time and space.++Until a proper tutorial has been written, please check out the+*examples* directory. If you have questions, join #haskell-game on+irc.freenode.net. If you would like to report a bug or request a+feature, please [file an+issue](https://github.com/esoeylemez/wires/issues).+++Module overview+---------------++The library is split into two roles: *applications* and *controllers*.+An application developer models interactions and implements reactive+systems, i.e. the application logic. A controller developer implements+the glue between the application and the real world, i.e. how events and+time-varying values map to actual things on the screen or in the+network. The module structure reflects that distinction:++Module | Purpose+--------------------------|-----------------------------------------------+`Control.Wire` | Application language (basically core + utils).+`Control.Wire.Controller` | Controller language.+`Control.Wire.Core` | Core application language.+`Control.Wire.Internal` | You should never need this module.+`Control.Wire.Utils` | Extra application utilities.++Modules not listed here are highly experimental and should not be used.++If you are asking yourself whether you are an application or a+controller developer: at this early stage of development you are+probably both, which means that you will write the application as well+as connect its inputs and outputs to the real world.
+ Setup.lhs view
@@ -0,0 +1,6 @@+> module Main (main) where+>+> import Distribution.Simple+>+> main :: IO ()+> main = defaultMain
+ examples/Utils.hs view
@@ -0,0 +1,29 @@+-- |+-- Copyright: (c) 2016 Ertugrul Söylemez+-- License: BSD3+-- Maintainer: Ertugrul Söylemez <esz@posteo.de>+-- Stability: experimental++module Utils+ ( -- * Utilities+ average+ )+ where++import Control.Wire+import Data.Foldable+import Data.Profunctor+import qualified Data.Sequence as Seq+++-- | Average of the given event's payload over the last given number of+-- occurrences.++average :: (Fractional a, Monad m) => Int -> Wire m (Event a) (Event a)+average n =+ lmap (fmap go) (unfoldE Seq.empty)+ where+ go x xs' =+ let xs = Seq.take n (x Seq.<| xs')+ in (foldl' (+) 0 xs / fromIntegral (Seq.length xs),+ xs)
+ examples/ping-pong.hs view
@@ -0,0 +1,71 @@+-- |+-- Copyright: (c) 2016 Ertugrul Söylemez+-- License: BSD3+-- Maintainer: Ertugrul Söylemez <esz@posteo.de>+-- Stability: experimental++{-# LANGUAGE Arrows #-}++module Main (main) where++import Control.Monad.IO.Class+import Control.Wire+import Control.Wire.Controller+import GHC.Stats+import Prelude hiding ((.), id)+import System.Clock+import System.IO+import System.Mem+import Text.Printf+import Utils+++newCharEvent :: (MonadIO m) => Wire m a (Event Char)+newCharEvent = proc _ ->+ newEvent -< liftIO $ do+ b <- hReady stdin+ if b then Just <$> getChar else pure Nothing+++newTickEvent :: (MonadIO m) => Wire m a (Event Double)+newTickEvent = proc _ -> do+ times <- newEvent -< Just <$> getT+ withM_ unfoldE getT -< fmap (\t t' -> (secs (t - t'), t)) times++ where+ secs = (/ 1000000000) . fromInteger . toNanoSecs+ getT = liftIO (getTime Monotonic)+++myApp :: (MonadIO m) => Wire m a (Event ())+myApp = proc _ -> do+ deltas <- newTickEvent -< ()+ fps <- hold 0 . (fmap recip <$> average 25) -< deltas+ chars <- newCharEvent -< ()++ acc <- scan 1 -< negate <$ filterE (== ' ') chars+ vel <- scan 0 -< fmap (\dt -> min 2 . max (-2) . (+ acc*dt)) deltas+ pos <- scan 0 -< fmap (\dt -> min 1 . max (-1) . (+ vel*dt)) deltas++ animate -< liftIO $ do+ performGC+ mem <- (`div` 1024) . currentBytesUsed <$> getGCStats+ printf "\r%8dk %8.2f %5.2f %s %s\027[K"+ mem+ fps+ vel+ (if acc > 0 then ">" else "<")+ (map (\xI ->+ let x = -1 + 2 * fromInteger xI / 59+ in if abs (pos - x) < 0.1 then 'X' else '.')+ [0..59])+ hFlush stdout++ id -< () <$ filterE (== 'q') chars+++main :: IO ()+main = do+ hSetBuffering stdin NoBuffering+ hSetEcho stdin False+ control myApp
+ wires.cabal view
@@ -0,0 +1,61 @@+name: wires+version: 0.1.0+category: Control, FRP+synopsis: Functional reactive programming library++maintainer: Ertugrul Söylemez <esz@posteo.de>+author: Ertugrul Söylemez <esz@posteo.de>+copyright: Copyright 2016 Ertugrul Söylemez+homepage: https://github.com/esoeylemez/wires+bug-reports: https://github.com/esoeylemez/wires/issues+license: BSD3+license-file: LICENSE++description: Functional reactive programming library.++build-type: Simple+cabal-version: >= 1.10+extra-source-files: CHANGELOG.md README.md++source-repository head+ type: git+ location: https://github.com/esoeylemez/wires.git++flag Examples+ default: False+ description: Build the example programs+ manual: True+++library+ build-depends:+ base >= 4.8 && < 5,+ deepseq >= 1.4 && < 2,+ profunctors >= 5.2 && < 6,+ semigroupoids >= 5.1 && < 6,+ these >= 0.7 && < 1+ default-language: Haskell2010+ ghc-options: -W -fdefer-typed-holes+ exposed-modules:+ Control.Wire+ Control.Wire.Controller+ Control.Wire.Core+ Control.Wire.Internal+ Control.Wire.Utils+ Control.Wire.Varying++executable wires-ping-pong+ if flag(examples)+ build-depends:+ base >= 4.8 && < 5,+ clock,+ containers,+ profunctors,+ wires+ else+ buildable: False+ default-language: Haskell2010+ ghc-options: -threaded -fdefer-typed-holes+ hs-source-dirs: examples+ main-is: ping-pong.hs+ other-modules: Utils