packages feed

dyna (empty) → 0.1.0.0

raw patch · 8 files changed

+1688/−0 lines, 8 filesdep +Booleandep +MonadRandomdep +asyncsetup-changed

Dependencies added: Boolean, MonadRandom, async, base, dyna, lifted-async, lifted-base, monad-control, mtl, random, stm, temporal-media, time, unagi-chan, unbounded-delays, vector, vector-space

Files

+ ChangeLog.md view
@@ -0,0 +1,3 @@+# Changelog for try-evt++## Unreleased changes
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright Author name here (c) 2022++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 Author name here nor the names of other+      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.
+ README.md view
@@ -0,0 +1,20 @@+# dyna - FRP library++The `dyna` is functional reactive programming (FRP) library for Haskell.+It implements classical FRP and probvides easy to use +interface to write call-back heavy interactive applications.++## Tutorial++* [Introduction](https://github.com/anton-k/dyna-frp/blob/main/dyna/tutorial/00-intro.md)+* [Event streams](https://github.com/anton-k/dyna-frp/blob/main/dyna/tutorial/01-event-streams.md)+* [Console game example](https://github.com/anton-k/dyna-frp/blob/main/dyna/tutorial/03-event-game-example.md)+* [Dynamic values](https://github.com/anton-k/dyna-frp/blob/main/dyna/tutorial/04-dynamics.md)+* [Backbone FRP monad](https://github.com/anton-k/dyna-frp/blob/main/dyna/tutorial/05-backbone-monad.md)+* [Control flow. Sharing event streams and recursion](https://github.com/anton-k/dyna-frp/blob/main/dyna/tutorial/06-control-flow.md)+* [How to make bindings to imperative libraries](https://github.com/anton-k/dyna-frp/blob/main/dyna/tutorial/07-make-bindings.md)+* [Parsers of event streams](https://github.com/anton-k/dyna-frp/blob/main/dyna/tutorial/08-parser.md)+* [Conclusion](https://github.com/anton-k/dyna-frp/blob/main/dyna/tutorial/09-conclusion.md)+* [Quick reference](https://github.com/anton-k/dyna-frp/blob/main/dyna/tutorial/101-reference.md)+* [Resources](https://github.com/anton-k/dyna-frp/blob/main/dyna/tutorial/102-resuorces.md)+
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ dyna.cabal view
@@ -0,0 +1,73 @@+cabal-version: 1.12++name:           dyna+version:        0.1.0.0+synopsis:       Minimal FRP library+description:    Defines minimal FRP library (classical FRP)+homepage:       https://github.com/anton-k/dyna-frp#readme+bug-reports:    https://github.com/anton-k/dyna-frp/issues+author:         Anton Kholomiov+maintainer:     anton.kholomiov@gmail.com+copyright:      2022 Anton Kholomiov+category:       FRP+license:        BSD3+license-file:   LICENSE+build-type:     Simple+extra-source-files:+    README.md+    ChangeLog.md++source-repository head+  type: git+  location: https://github.com/anton-k/dyna-frp/dyna++library+  exposed-modules:+      Dyna+      Dyna.Ref+  hs-source-dirs:+      src+  build-depends:+      base >=4.7 && <5+    , async+    , Boolean+    , mtl+    , stm+    , random+    , unagi-chan+    , unbounded-delays+    , lifted-base+    , lifted-async+    , monad-control+    , MonadRandom+    , vector+    , vector-space+    , temporal-media+    , time+  default-language: Haskell2010+  default-extensions:+    DeriveFunctor+    RankNTypes+    LambdaCase+    FlexibleContexts+    FlexibleInstances+    ExistentialQuantification+    TypeApplications+    TypeFamilies+    TupleSections+    GeneralizedNewtypeDeriving+    UndecidableInstances+    ImportQualifiedPost++test-suite dyna-test+  type: exitcode-stdio-1.0+  main-is: Spec.hs+  other-modules:+      Paths_dyna+  hs-source-dirs:+      test+  ghc-options: -threaded -rtsopts -with-rtsopts=-N+  build-depends:+      base >=4.7 && <5+    , dyna+  default-language: Haskell2010
+ src/Dyna.hs view
@@ -0,0 +1,1532 @@+-- | Dyna is functional reactive programming library.+-- It describes event streams that are based on callbacks.+-- The event stream can produce something useful with callback that it consumes.+-- Also we have continous signals called @Dyn@ (short for dynamic).+-- The Dyn is sort of observance process of an event stream. For any+-- event that happen on event stream we remember that value and produce it+-- at any time until the next event will happen.+--+-- # Events+--+-- The event stream is just callback consumer funtion:+--+-- > newtype Evt m a = Evt {+-- >   runEvt :: (a -> m ()) -> m ()+-- > }+--+-- So it tells us: If you give me some callback @(a -> m ())@ I will apply it to the event+-- when event will occur. But when it will occur we don't know until we run the event.+-- All events happen at the same time. Every event triggers a callback.+-- This has some special nuance to it. That can differ from other FRP libraries.+-- For example monoidal append of two event streams:+--+--  > evtA <> evtB+--+-- In many FRP libraries we choose which element will happen or should we also append the events+-- if they happen "at the same time". For this library we spawn two concurrent processes+-- on background so if two events will happen at the same time callback will be called twice.+--+-- # Dynamics+--+-- The assumption is that dynamic is a process that evolves in time.+-- And as a human beings we can only ask for current values while process happens.+-- So we assemble the dynamics with combinators an after that we can run it's process:+--+-- > ref <-runDyn dynamicValue+--+-- It produces reference to the process which we can use to sample the current value in real time:+--+-- > readDyn ref+-- >  10+-- > readDyn ref  -- 5 seconds later+-- >  10+-- > readDyn ref  -- 5 seconds later+-- >  3+--+-- This reminds us of the notion of present moment. Take for example a weather temperature.+-- We can claim to build a model of weather and have an assumption of which value will happen tomorrow+-- but the exact value for it we can only measure at the moment when it will actually happen.+--+-- So the library is based on simple assumptions:+--+-- * Event stream is a callback processor+--+-- * Event stream happen at the same time as concurrent process+--+-- * Dynamic is a process and we can only query the current value for it+--+-- * Dynamics are based on event streams. The dynamic is an observation of some underlying event streams.+--    We just remember the last event and keep producing it until the next one wil arrive.+module Dyna(+  -- * Pipe+  (|>),+  -- * Class+  Frp(..),+  -- * Events+  Evt(..),+  once,+  never,+  -- * Dynamics+  Dyn(..),+  constDyn,+  runDyn,+  DynRef(..),+  readDyn,+  cancelDyn,++  -- * Control+  newEvt,+  newDyn,+  withDyn,++  -- * API+  -- * Event API+  scan,+  scanMay,+  mapMay,+  accum,+  accumB,+  accumMay,+  filters,+  filterJust,+  whens,+  splits,+  lefts,+  rights,+  iterates,+  withIterates,++  fix1,+  fix2,+  fix3,+  fix4,+  switch,+  joins,++  delay,+  delayFork,++  sums,+  sumD,+  integrate,+  integrate2,+  products,+  count,+  withCount,+  appends,+  foldMaps,+  takes,+  drops,+  takesWhile,+  dropsWhile,+  cycles,+  listAt,+  toToggle,++  forevers,+  races,+  forks,+  -- * Render streams+  heads,+  prints,+  putStrLns,+  folds,+  foldls,+  foldls',+  foldrs,+  foldrs',+  Parser,+  runParser,+  takeP,+  cycleP,+  headP,+  maybeP,+  -- * Event/Dynamic interaction+  hold,+  unhold,+  scanD,+  scanMayD,+  switchD,+  switchDyn,+  apply,+  applyMay,+  snap,+  attach,+  attachWith,+  attachWithMay,+  (<@>),+  (<@),+  -- * Effectful API+  FunctorM(..),+  foreach,+  posteach,+  iterates',+  scan',+  scanMay',+  accum',+  accumMay',+  filters',+  mapMay',+  apply',+  applyMay',+  -- * Utilities++  -- **  Channels (interaction with the world)+  mchanEvt,+  tchanEvt,+  uchanEvt,+  UChan,+  newTriggerEvt,+  -- ** IO+  getLines,++  -- ** Clock+  clock,+  pulse,+  ticks,+  timer,+  timerD,+  -- ** Random+  toRandom,+  toRandomR,+  withRandom,+  withRandomR,+  oneOf,+  withOneOf,+  freqOf,+  withFreqOf,+  randSkip,+  randSkipBy,++  -- * Re-exports+  liftA2,+  liftA3,+  BasisArity(..),+  module X,+) where++import Prelude hiding ((<*))+import Data.IORef+import Control.Applicative (liftA2, liftA3)+import Control.Monad+import Control.Monad.IO.Class+import System.Environment+import Data.Functor+import Data.Bifunctor+import Data.Function+import Data.Maybe (fromJust)+import Data.Vector qualified as V+import Data.AdditiveGroup as X+import Data.AffineSpace as X+import Data.Basis+import Data.Cross as X+import Data.VectorSpace as X+import Data.String+import Control.Concurrent.Lifted+import Control.Concurrent.Thread.Delay qualified as D+import Control.Concurrent.Async.Lifted+import Control.Concurrent.STM+import Control.Concurrent.STM.TVar+import Control.Concurrent.STM.TChan+import Control.Concurrent.Chan.Unagi (InChan)+import Control.Concurrent.Chan.Unagi qualified as U+import Control.Concurrent.Chan qualified as M++import Control.Monad.Trans.Control+import Dyna.Ref+import Data.Time+import System.Random (Random, newStdGen, randomR, random)+import Control.Exception.Lifted+import Control.Monad.Random.Class qualified as R+import Temporal.Class as X++import Data.Boolean++infixl 4 <@>+infixl 4 <@+infixl 0  |>++{-# inline (|>) #-}+-- | Pipe operator. We often write processors of event streams+-- It makes it convenient write them from first to the last:+--+-- > evt = proc1 |> proc2 |> ... |> procN+--+-- Instead of reversed order with @($)@:+--+-- > evt = procN $ ... $ proc2 $ proc1+(|>) :: a -> (a -> b) -> b+a |> f = f a+++class (IsRef (Ref m), MonadBaseControl IO m, MonadIO m) => Frp m where+  type Ref m :: * -> *++instance Frp IO where+  type Ref IO = TVar++-- | Dynamics are step-wise constant effectful functions+-- each step transition is driven by underlying stream of events.+--+-- Meaning of the Dyn is a process that evolves in time.+-- We can start the process by running @runDyn@. It produces a reference to the+-- process that runs in background.+--+-- > runDyn :: Frp m => Dyn m a -> DynRef m a+--+-- When reference is initialized we can query current  value of it:+--+-- > readDyn :: DynRef m a -> m a+--+-- When we are done with observations we should shut down the background process with:+--+-- > cancelDyn :: DynRef m a -> m ()+--+-- It kills the background process and triggers the release function of underlying event stream.+data Dyn m a+  = forall s . Dyn+      { dyn'get     :: s -> m a   -- ^ get the value from internal state+      , dyn'evt     :: Evt m s    -- ^ stream of state updates+      , dyn'init    :: m s        -- ^ initial state+      , dyn'release :: m ()       -- ^ release resources for dynamic+      }+    -- ^ event based dynamic+  | ConstDyn a+    -- ^ Constant value++-- | Reference to running dynamic process by which we can query values (@readDyn@).+-- Also note that we no longer need the reference we should release the resources+-- by calling @cancelDyn@.+data DynRef m a+  = forall s . DynRef (s -> m a) (Ref m s) ThreadId (m ())+  | ConstRef a++-- | Runs dynamic within the scope of the function.+-- It provides a callback with dyn getter as argument and after+-- callback finishes it shutdowns the dyn process.+withDyn :: Frp m => Dyn m a -> (m a -> m b) -> m b+withDyn dyn f = bracket (runDyn dyn) cancelDyn (\ref -> f (readDyn ref))++-- | Dyn that is constructed from effectful callback.+constDyn :: Frp m => m a -> Dyn m a+constDyn act = Dyn (const act) never (pure ()) (pure ())++instance Functor m => Functor (Dyn m) where+  fmap f (ConstDyn a)                = ConstDyn (f a)+  fmap f (Dyn extract evt s release) = Dyn (fmap f . extract) evt s release++instance Frp m => Applicative (Dyn m) where+  pure a = ConstDyn a+  (ConstDyn f) <*> (ConstDyn a) = ConstDyn (f a)+  (ConstDyn f) <*> (Dyn aget aevt as release) = Dyn (\s -> f <$> aget s) aevt as release+  (Dyn fget fevt fs release) <*> (ConstDyn a) = Dyn (\s -> ($ a) <$> fget s) fevt fs release+  (Dyn fget fevt fs releaseF) <*> (Dyn aget aevt as releaseA) =+    Dyn (\(f, a) -> (fget f) <*> (aget a)) evt (liftA2 (,) fs as) (releaseF >> releaseA)+    where+      evt = Evt $ \go -> do+        tv <- proxyNewRef evt =<< liftA2 (,) fs as+        void $ fork $ runEvt joint $ \case+          Left  s -> do+            liftIO $ modifyRef tv (first $ const s)+            go =<< liftIO (readRef tv)+          Right s -> do+            liftIO $ modifyRef tv (second $ const s)+            go =<< liftIO (readRef tv)++      joint = fmap Left fevt <> fmap Right aevt++-- | Event stream. The meaning of an event is a callback consumer function.+-- If we give callback to it it will do something useful based on it.+--+-- The main function is runEvt:+--+-- > runEvt :: Evt m a -> (a -> m ()) -> m ()+-- > runEvt events callback = ...+--+-- Let's look at simple examples of the event streams:+--+-- Event that never produce anything:+--+-- > never = Evt {+-- >    runEvt _ = pure ()+-- >  }+--+-- So it just ignores the callback and returns right away.+--+-- Event that happens only once:+--+-- > once :: m a -> Evt m a+-- > once get = Evt {+-- >     runEvt go = go =<< get+-- >  }+--+-- It just gets the value right away and applies callback to it.+-- We can try it out in the interpreter:+--+-- > putStrLns $ fmap ("Your message: " <> ) $ once getLine+--+-- We have useful functions to print out the events: @putStrLns@ and @prints@.+--+-- Also we have event streams that happen periodically:+--+-- > prints $ clock 1  -- prints time every second+--+-- ## Duplication of the events.+--+-- Note that event streams are functions that do side-effects within some monad.+-- We use them as values but it means that two values with the same event stream definition+-- can produce different results. For example:+--+-- > a = toRandomR (0, 10) $ clock 1+-- > b = a+--+-- Note that a and b will each have their own copy of underlying random event stream.+-- So if you use it in the code don't expect values to be the same.+--+-- But if we want them to be the same we can copy event from it's definition with function:+--+-- > newEvt :: Evt m a -> m (Evt m a)+--+-- It starts the underying event stream process n background and sends all events+-- to the result by channel. With nice property of when we shut down the result event the+-- background process also shuts down.+--+-- > a <- newEvt toRandomR (0, 10) $ clock 1+-- > b = a+--+-- In this example event streams @a@ and @b@ will have the same events during execution.+newtype Evt m a = Evt { runEvt :: (a -> m ()) -> m () }++-- | Event that happens only once and happens right away.+once :: Frp m => m a -> Evt m a+once ask = Evt $ \go -> go =<< ask++-- | Event that never happens. Callback function is ignored.+never :: Frp m => Evt m a+never = Evt (const $ pure ())++-- | Runs the argument event stream as background process+-- and produces event stream that is fed with events over channel (unagi-channel package).+-- When result event stream shuts down the background process also shuts down.+newEvt :: Frp m => Evt m a -> m (Evt m a)+newEvt evt = do+  ch <- liftIO $ U.newChan+  tid <- fork $ runEvt evt $ liftIO . U.writeChan (fst ch)+  pure $ uchanEvtFinally tid (pure $ fst ch)++uchanEvtFinally :: (Frp m) => ThreadId -> m (InChan a) -> Evt m a+uchanEvtFinally tid mchan = Evt $ \go -> do+  chan <- liftIO . U.dupChan =<< mchan+  loop chan go `finally` (killThread tid)+  where+    loop chan go = do+      a <- liftIO $ U.readChan chan+      go a+      loop chan go++-- | Runs the dynamic process in background and returns dynamic+-- that just samples the background proces with @readDyn@.+newDyn :: Frp m => Dyn m a -> m (Dyn m a)+newDyn dyn = do+  ref <- runDyn dyn+  pure $ Dyn readDyn never (pure ref) (cancelDyn ref)++instance Functor (Evt m) where+  fmap f (Evt evt) = Evt $ \proc -> evt (proc . f)++instance Frp m => Semigroup (Evt m a) where+  (<>) (Evt a) (Evt b) = Evt $ \proc ->+    concurrently_ (a proc) (b proc)++-- | Shutdown the remaining event if one of the events close up early.+races :: Frp m => Evt m a -> Evt m a -> Evt m a+races (Evt a) (Evt b) = Evt $ \go ->+  race_ (a go) (b go)++-- | Execute each callback in separate thread+forks :: Frp m => Evt m a -> Evt m a+forks evt =+  Evt $ \go -> runEvt evt $ void . fork . go++instance Frp m => Monoid (Evt m a) where+  mempty = never++instance Frp m => Applicative (Evt m) where+  pure a = once (pure a)+  f <*> a = a >>= (\x -> fmap ( $ x) f)++instance Frp m => Monad (Evt m) where+  (>>=) a f = switch (fmap f a)++-- | Accumulate over event stream.+accum :: Frp m => (a -> s -> (b, s)) -> s -> Evt m a -> Evt m b+accum f s evt = Evt $ \go -> do+  ref <- proxyNewRef evt s+  runEvt evt $ \x -> do+    (b, s) <- f x <$> liftIO (readRef ref)+    go b+    liftIO $ writeRef ref s++-- | Accumulate over event stream.+accum' :: Frp m => (a -> s -> m (b, s)) -> s -> Evt m a -> Evt m b+accum' f s evt = Evt $ \go -> do+  ref <- proxyNewRef evt s+  runEvt evt $ \x -> do+    (b, s) <- f x =<< liftIO (readRef ref)+    go b+    liftIO $ writeRef ref s++-- | Accumulate over event stream.+accumMay :: Frp m => (a -> s -> Maybe (b, s)) -> s -> Evt m a -> Evt m b+accumMay f s evt = Evt $ \go -> do+  ref <- proxyNewRef evt s+  runEvt evt $ \x -> do+    mRes <- f x <$> liftIO (readRef ref)+    forM_ mRes $ \(b, s) -> do+      go b+      liftIO $ writeRef ref s++-- | Accumulate over event stream.+accumMay' :: Frp m => (a -> s -> m (Maybe (b, s))) -> s -> Evt m a -> Evt m b+accumMay' f s evt = Evt $ \go -> do+  ref <- proxyNewRef evt s+  runEvt evt $ \x -> do+    mRes <- f x =<< liftIO (readRef ref)+    forM_ mRes $ \(b, s) -> do+      go b+      liftIO $ writeRef ref s++-- | scan over event stream. Example:+--+-- > naturals = scan (+) 0 pulse+scan :: Frp m => (a -> b -> b) -> b -> Evt m a -> Evt m b+scan f s evt = Evt $ \go -> do+  ref <- proxyNewRef evt s+  runEvt evt $ \x -> do+    s <- f x <$> liftIO (readRef ref)+    go s+    liftIO $ writeRef ref s++-- | scan over event stream with effectful function.+scan' :: Frp m => (a -> b -> m b) -> b -> Evt m a -> Evt m b+scan' f s evt = Evt $ \go -> do+  ref <- proxyNewRef evt s+  runEvt evt $ \x -> do+    s <- f x =<< liftIO (readRef ref)+    go s+    liftIO $ writeRef ref s++-- | scan combined with filter. If accumulator function produces @Nothing@ on event then+-- that event is ignored and state is kept to previous state.+scanMay :: Frp m => (a -> b -> Maybe b) -> b -> Evt m a -> Evt m b+scanMay f s evt = Evt $ \go -> do+  ref <- proxyNewRef evt s+  runEvt evt $ \x -> do+    ms <- f x <$> liftIO (readRef ref)+    forM_ ms $ \s -> do+      go s+      liftIO $ writeRef ref s++-- | scan combined with filter for effectful function. See @scanMay@ for details.+scanMay' :: Frp m => (a -> b -> m (Maybe b)) -> b -> Evt m a -> Evt m b+scanMay' f s evt = Evt $ \go -> do+  ref <- proxyNewRef evt s+  runEvt evt $ \x -> do+    ms <- f x =<< liftIO (readRef ref)+    forM_ ms $ \s -> do+      go s+      liftIO $ writeRef ref s++-- | Iterates over event stream. It's like scan but it ignores the values of underying stream+-- and starts with initial value as first element.+iterates :: Frp m => (a -> a) -> a -> Evt m b -> Evt m a+iterates f val evt = Evt $ \go -> do+  ref <- proxyNewRef evt val+  runEvt evt $ \_ -> do+    s <- liftIO (readRef ref)+    go s+    liftIO $ writeRef ref (f s)++withIterates :: Frp m => (a -> a) -> a -> Evt m b -> Evt m (a, b)+withIterates f val evt = Evt $ \go -> do+  ref <- proxyNewRef evt val+  runEvt evt $ \x -> do+    s <- liftIO (readRef ref)+    go (s, x)+    liftIO $ writeRef ref (f s)+++-- | Effectful version for @iterates@.+iterates' :: Frp m => (a -> m a) -> a -> Evt m b -> Evt m a+iterates' f val evt = Evt $ \go -> do+  ref <- proxyNewRef evt val+  runEvt evt $ \_ -> do+    s <- liftIO (readRef ref)+    go s+    liftIO . writeRef ref =<< f s++instance (Frp m, Num a) => Num (Dyn m a) where+  fromInteger = pure . fromInteger+  (+) = liftA2 (+)+  (*) = liftA2 (*)+  (-) = liftA2 (-)+  negate = fmap negate+  abs = fmap abs+  signum = fmap signum++instance (Frp m, Fractional a) => Fractional (Dyn m a) where+  fromRational = pure . fromRational+  recip = fmap recip++instance (Frp m, Semigroup a) => Semigroup (Dyn m a) where+  (<>) = liftA2 (<>)++instance (Frp m, Monoid a) => Monoid (Dyn m a) where+  mempty = pure mempty++instance (Frp m, IsString a) => IsString (Dyn m a) where+  fromString = pure . fromString++-- | Reads current dynamic value.+readDyn :: Frp m => DynRef m a -> m a+readDyn (ConstRef val) = pure val+readDyn (DynRef extract ref _ _) = do+  s <- liftIO (readRef ref)+  extract s++-- | Shuts down the background process for dynamic and releases resulrces for+-- event stream that drives the dynamic.+cancelDyn :: Frp m => DynRef m a -> m ()+cancelDyn (ConstRef _) = pure ()+cancelDyn (DynRef _ _ tid release) = killThread tid >> release++-- | Executes dynamic for observation. The dynamic is step-wise constant+-- function that is driven by some event stream. The function runs the event stream+-- process in background and samples the updated state.+--+-- We can observe the value with @readDyn@. We need to shut down the stream when+-- we no longer need it with @cancelDyn@ function.+runDyn :: Frp m => Dyn m a -> m (DynRef m a)+runDyn (ConstDyn val) = pure (ConstRef val)+runDyn dyn@(Dyn extract evt init release) = do+  ref <- proxyNewRefDyn dyn =<< init+  tid <- fork $ runEvt evt $ \s -> do+    liftIO $ writeRef ref s+  pure (DynRef extract ref tid release)++-- | Turns event stream to dynamic. It holds the values of+-- events until the next event happen. It starts with initial value.+--+-- > hold initVal events = ...+hold :: Frp m => a -> Evt m a -> Dyn m a+hold s evt = Dyn pure evt (pure s) (pure ())++-- | Counts how many events accured so far on the stream.+count :: Frp m => Evt m a -> Evt m Int+count = scan (const succ) 0++withCount :: Frp m => Evt m a -> Evt m (Int, a)+withCount = accum (\a b -> ((b, a), succ b)) 1++-- | Turns dynamic into event stream of underlying events+-- that trigger dynamic updates.+unhold :: Frp m => Dyn m a -> Evt m a+unhold (ConstDyn val) = Evt $ \go -> go val+unhold (Dyn extract evts init release) = Evt $ \go -> do+  go =<< extract =<< init+  runEvt evts (go <=< extract) `finally` release++-- | scans over event stream and converts it to dynamic.+scanD :: Frp m => (a -> b -> b) -> b -> Evt m a -> Dyn m b+scanD f s evt = hold s (scan f s evt)++-- | Accumulates the values with event stream that produce functions.+accumB :: Frp m => a -> Evt m (a -> a) -> Dyn m a+accumB a evt = scanD ($) a evt++-- | Dynamic scan that can also filter out events. If Nothing is produced then the event is skipped.+scanMayD :: Frp m => (a -> b -> Maybe b) -> b -> Evt m a -> Dyn m b+scanMayD f s evt = hold s (scanMay f s evt)++-- | Adds some procedure to callback. Procedure is called prior to callback execution.+foreach :: Frp m => (a -> m ()) -> Evt m a -> Evt m a+foreach call evt = Evt $ \go ->+  runEvt evt $ \x -> do+    call x+    go x++-- | Adds some procedure to callback. Procedure is called after callback execution.+posteach :: Frp m => (a -> m ()) -> Evt m a -> Evt m a+posteach call evt = Evt $ \go ->+  runEvt evt $ \x -> do+    go x+    call x++--------------------------------------------------------------------------------+-- applications++-- | Applies a function to event stream value. The function is sampled+-- from dynamic process.+apply :: Frp m => Dyn m (a -> b) -> Evt m a -> Evt m b+apply dyn evt = Evt $ \go -> do+  ref <- runDyn dyn+  runEvt evt (\b -> do+      go . ($ b) =<< readDyn ref+    )+    `finally` cancelDyn ref++-- | Effectful variant of @apply@.+apply' :: Frp m => Dyn m (a -> m b) -> Evt m a -> Evt m b+apply' dyn evt = Evt $ \go -> do+  ref <- runDyn dyn+  runEvt evt (\b -> do+    (\f -> go =<< f b) =<< readDyn ref)+    `finally` cancelDyn ref++-- | Infix variant of @apply@+(<@>) :: Frp m => Dyn m (a -> b) -> Evt m a -> Evt m b+(<@>) = apply++-- | Infix variant of @snap@.+(<@) :: Frp m => Dyn m a -> Evt m b -> Evt m a+(<@) = snap++-- | Apply combined with filter.+applyMay :: Frp m => Dyn m (a -> Maybe b) -> Evt m a -> Evt m b+applyMay dyn evt = Evt $ \go -> do+  ref <- runDyn dyn+  runEvt evt (\b -> do+    mapM_ go . ($ b) =<< readDyn ref)+    `finally` cancelDyn ref++-- | Effectful @applyMay@.+applyMay' :: Frp m => Dyn m (a -> m (Maybe b)) -> Evt m a -> Evt m b+applyMay' dyn evt = Evt $ \go -> do+  ref <- runDyn dyn+  runEvt evt (\b -> do+    (\f -> mapM_ go =<< f b) =<< readDyn ref)+    `finally` cancelDyn ref++-- | Snapshot of dynamic process with event stream. All values+-- in the event stream are substituted with current value of dynamic.+snap :: Frp m => Dyn m a -> Evt m b -> Evt m a+snap dyn evt = apply (const <$> dyn) evt++-- | Attach element from dyn to event stream.+attach :: Frp m => Dyn m a-> Evt m b -> Evt m (a, b)+attach dyn evt = attachWith (,) dyn evt++-- | Kind of @zipWith@ function for dynamics and event streams.+attachWith :: Frp m => (a -> b -> c) -> Dyn m a -> Evt m b -> Evt m c+attachWith f dyn evt = apply (f <$> dyn) evt++-- | Attach with filtering. When @Nothing@ is produced event is omitted from the stream.+attachWithMay :: Frp m => (a -> b -> Maybe c) -> Dyn m a -> Evt m b -> Evt m c+attachWithMay f dyn evt = applyMay (f <$> dyn) evt++--------------------------------------------------------------------------------+-- filters++-- | Map with filtering. When @Nothing@ is produced event is omitted from the stream.+mapMay :: Frp m => (a -> Maybe b) -> Evt m a -> Evt m b+mapMay f evt = Evt $ \go -> runEvt evt (mapM_ go . f)++-- | Effectful @mapMay@+mapMay' :: Frp m => (a -> m (Maybe b)) -> Evt m a -> Evt m b+mapMay' f evt = Evt $ \go -> runEvt evt (mapM_ go <=< f)++-- | Filtering of the event strewams. Only events that produce True remain in the stream.+filters :: Frp m => (a -> Bool) -> Evt m a -> Evt m a+filters f evt = Evt $ \go -> runEvt evt (\x -> when (f x) (go x))++-- | Effectful filtering for event streams.+filters' :: Frp m => (a -> m Bool) -> Evt m a -> Evt m a+filters' f evt = Evt $ \go -> runEvt evt (\x -> (\cond -> when cond (go x)) =<< f x)++-- | Filters based on Maybe. If @Nothing@ is produced forthe event it is omitted from the stream.+filterJust :: Frp m => Evt m (Maybe a) -> Evt m a+filterJust evt = Evt $ \go -> runEvt evt (mapM_ go)++-- | Filters with dynamic. When dynamic is true events pass through and when it's false+-- events are omitted.+whens :: Frp m => Dyn m Bool -> Evt m a -> Evt m a+whens dyn evt = Evt $ \go -> do+  ref <- runDyn dyn+  runEvt evt $ \b -> do+    a <- readDyn ref+    when a (go b)++-- | Splits the either event stream.+splits :: Frp m => Evt m (Either a b) -> (Evt m a, Evt m b)+splits evt = (lefts evt, rights evt)++-- | Gets all left events from the stream+lefts :: Frp m => Evt m (Either a b) -> Evt m a+lefts evt = mapMay (either Just (const Nothing)) evt++-- | Gets all right events from the stream+rights :: Frp m => Evt m (Either a b) -> Evt m b+rights evt = mapMay (either (const Nothing) Just) evt++-- | Takes only so many events from the stream+takes :: Frp m => Int -> Evt m a -> Evt m a+takes n evt = Evt $ \go -> do+  ref <- proxyNewRef evt 0+  waitAsync $ do+    runEvt evt $ \x -> do+      cur <- liftIO (readRef ref)+      when (cur < n) $ do+        go x+        when (cur == n - 1) stopSelf+        liftIO $ writeRef ref (cur + 1)++-- | Drops first so many events from the stream+drops :: Frp m => Int -> Evt m a -> Evt m a+drops n evt = Evt $ \go -> do+  tv <- proxyNewRef evt n+  runEvt evt $ \x -> do+    cur <- liftIO (readRef tv)+    if (cur <= 0)+      then go x+      else liftIO (modifyRef tv pred)++stopSelf :: Frp m => m ()+stopSelf = killThread =<< myThreadId++waitStop :: Frp m => Async a -> m ()+waitStop x = void $ liftIO $ waitCatch x++waitAsync :: Frp m => m () -> m ()+waitAsync act = do+  tid <- async act+  waitStop tid++-- | Takes events only while predicate is true.+takesWhile :: Frp m => (a -> Bool) -> Evt m a -> Evt m a+takesWhile pred evt = Evt $ \go -> do+  waitAsync $ do+    runEvt evt $ \x -> do+      if (pred x)+        then go x+        else stopSelf++-- | Drops events while predicate is true.+dropsWhile :: Frp m => (a -> Bool) -> Evt m a -> Evt m a+dropsWhile pred evt = Evt $ \go -> do+  tv <- proxyNewRef evt True+  runEvt evt $ \x -> do+    cur <- liftIO (readRef tv)+    if cur+      then+        unless (pred x) $ do+          liftIO $ writeRef tv False+          go x+      else go x++-- | Takes elements from the list by index. If index is out of bounds the event is omitted.+listAt :: Frp m => [a] -> Evt m Int -> Evt m a+listAt vals evt = mapMay (vec V.!?) evt+  where+    vec = V.fromList vals++-- | Turns event stream to toggle stream. It produce cyclic sequence of [True, False]+toToggle :: Frp m => Evt m a -> Evt m Bool+toToggle = iterates not True++-- | Cycles the values in the list over event sream.+cycles :: Frp m => [a] -> Evt m b -> Evt m a+cycles vals evt = fmap (vec V.!) $ iterates ((`mod` len) . succ) 0 evt+  where+    vec = V.fromList vals+    len = V.length vec++-- | Sums all the elements in the event stream+sums :: (Frp m, Num a) => Evt m a -> Evt m a+sums = scan (+) 0++-- | Integrates signal of vectors with given time step+integrate :: (Frp m, VectorSpace v, Real (Scalar v), Fractional (Scalar v)) => (Scalar v) -> Dyn m v -> Dyn m v+integrate dt dyn =+  hold zeroV $ scan (^+^) zeroV (attachWith (\v k -> realToFrac k *^ v) dyn (ticks (realToFrac dt)))++-- | More accurate integration of signal of vectors with given time step+integrate2 :: (Frp m, VectorSpace v, Real (Scalar v), Fractional (Scalar v)) => (Scalar v) -> Dyn m v -> Dyn m v+integrate2 dt dyn =+  hold zeroV $ fmap snd $ scan go (Nothing, zeroV) (attach dyn (ticks (realToFrac dt)))+  where+    go (v, h) (mPrev, res) = ((Just (v, h), ) . (res ^+^ )) $ case mPrev of+      Nothing       -> realToFrac h *^ v+      Just (v0, h0) -> (realToFrac h * 0.5) *^ (v0 ^+^ v)++-- | Sums all points in the signal with given time step+sumD :: (Frp m, Num a) => NominalDiffTime -> Dyn m a -> Dyn m a+sumD dt dyn = hold 0 $ sums (snap dyn (pulse dt))+++-- | Finds the product of all elements in the event stream.+products :: (Frp m, Num a) => Evt m a -> Evt m a+products = scan (*) 1++-- | Monoidal append of all elements in the stream+appends :: (Frp m, Monoid a) => Evt m a -> Evt m a+appends = scan (flip (<>)) mempty++-- | Same as foldMap only for streams.+foldMaps :: (Frp m, Monoid b) => (a -> b) -> Evt m a -> Evt m b+foldMaps f = appends . fmap f++-- | Monoidal fold for event streams, note that stream have to be finite for+-- the function to complete+folds :: (Frp m, Monoid a) => Evt m a -> m a+folds = foldls (<>) mempty++-- | Left fold for event streams, note that stream have to be finite for+-- the function to complete+foldls :: (Frp m) => (b -> a -> b) -> b -> Evt m a -> m b+foldls f s evt = do+  ref <- proxyNewRef evt s+  runEvt evt $ \x -> liftIO $ modifyRef ref $ flip f x+  liftIO $ readRef ref++-- | Effectful left fold+foldls' :: (Frp m) => (b -> a -> m b) -> b -> Evt m a -> m b+foldls' f s evt = do+  ref <- proxyNewRef evt s+  runEvt evt $ \x -> liftIO . writeRef ref =<< flip f x =<< liftIO (readRef ref)+  liftIO $ readRef ref++-- | Right fold for event streams, note that stream have to be finite for+-- the function to complete+foldrs :: (Frp m) => (a -> b -> b) -> b -> Evt m a -> m b+foldrs f s evt = do+  ref <- proxyNewRef evt s+  runEvt evt $ \x -> liftIO $ modifyRef ref $ f x+  liftIO $ readRef ref++-- | Effectful right fold+foldrs' :: (Frp m) => (a -> b -> m b) -> b -> Evt m a -> m b+foldrs' f s evt = do+  ref <- proxyNewRef evt s+  runEvt evt $ \x -> liftIO . writeRef ref =<< f x =<< liftIO (readRef ref)+  liftIO $ readRef ref++-- | Starts event stream process and as callback prints it values.+prints :: (Frp m, Show a) => Evt m a -> m ()+prints evt = runEvt evt (liftIO . print)++-- | Starts event stream process and as callback prints it values.+putStrLns :: (Frp m) => Evt m String -> m ()+putStrLns evt = runEvt evt (liftIO . putStrLn)++-- | Stream of user inputs+getLines :: Frp m => Evt m String+getLines = once (liftIO getLine)++-- | Queries the event stream form dynamic and runs it all next event streams are ignored.+switchDyn :: Frp m => Dyn m (Evt m a) -> Evt m a+switchDyn dyn = Evt $ \go -> do+  ref <- runDyn dyn+  evt <- readDyn ref+  runEvt evt go++-- | Joins event stream of streams. If stream is started it runs until the end.+joins :: Frp m => Evt m (Evt m a) -> Evt m a+joins evt = Evt $ \go ->+  runEvt evt $ \e -> void $ fork $ runEvt e go++-- | Recursion on event streams. As event streams are functions we can not use+-- normal recursion that haskell provides. It will stuck the execution.+-- But we can use @fix1@ to create event stream that feeds back the events to itself.+--+-- Note that any sort of recursion can be implemented with @fix1@.+-- For example if we need 3-recursive event stream:+--+--  > fix3 ::+--  >      (Evt m a -> Evt m b -> Evt m c -> m (Evt m a, Evt m b, Evt m c))+--  >   -> (Evt m a, Evt m b, Evt m c)+--+-- we can use sum tpye tags+-- to join it to single stream:+--+-- > data Tag a b c = TagA a | TagB b | TagC c+--+-- > fix3 f = unwrap $ fix1 g+-- >   where+-- >      g x = wrap <$> f (unwrapA x) (unwrapB x) (unwrapC x)+-- >+-- >      wrap a b c = mconcat [TagA <$> a, TagB <$> b, TagC <$> c]+-- >      unwrap evt = (unwrapA evt, unwrapB evt, unwrapC evt)+-- >+-- >      unwrapA = flip mapMay $ \x -> case x of+-- >                                  TagA a -> Just a+-- >                                  _      -> Nothing+--+-- We can use this trck with any number of streams. There are helper functions: @fix2@, @fix3@, @fix4@+fix1 :: Frp m => (Evt m a -> m (Evt m a)) -> Evt m a+fix1 f = Evt $ \go -> do+  chan <- liftIO U.newChan+  let evt = uchanEvt (fst chan)+  evt' <- f evt+  runEvt evt' $ \x -> do+    liftIO $ U.writeChan (fst chan) x+    go x++-- | Recursion for binary functions+fix2 :: Frp m => (Evt m a -> Evt m b -> m (Evt m a, Evt m b)) -> (Evt m a, Evt m b)+fix2 f = splits $ fix1 g+  where+    g x = wrap <$> f (lefts x) (rights x)+    wrap (a, b) = (Left <$> )a <> (Right <$> b)++data Tag3 a b c = TagA3 a | TagB3 b | TagC3 c++-- | Recursion for ternary functions+fix3 :: Frp m+  => (Evt m a -> Evt m b -> Evt m c -> m (Evt m a, Evt m b, Evt m c))+  -> (Evt m a, Evt m b, Evt m c)+fix3 f = unwrap $ fix1 g+  where+    g x = wrap <$> f (unwrapA x) (unwrapB x) (unwrapC x)+    wrap (a, b, c) = (TagA3 <$> a) <> (TagB3 <$> b) <> (TagC3 <$> c)++    unwrap x = (unwrapA x, unwrapB x, unwrapC x)++    unwrapA = mapMay $ \case+                TagA3 a -> Just a+                _       -> Nothing++    unwrapB = mapMay $ \case+                TagB3 a -> Just a+                _       -> Nothing++    unwrapC = mapMay $ \case+                TagC3 a -> Just a+                _       -> Nothing+++data Tag4 a b c d = TagA4 a | TagB4 b | TagC4 c | TagD4 d++-- | Recursion for functions of four arguments+fix4 :: Frp m =>+     (Evt m a -> Evt m b -> Evt m c -> Evt m d -> m (Evt m a, Evt m b, Evt m c, Evt m d))+  -> (Evt m a, Evt m b, Evt m c, Evt m d)+fix4 f = unwrap $ fix1 g+  where+    g x = wrap <$> f (unwrapA x) (unwrapB x) (unwrapC x) (unwrapD x)+    wrap (a, b, c, d) = (TagA4 <$> a) <> (TagB4 <$> b) <> (TagC4 <$> c) <> (TagD4 <$> d)++    unwrap x = (unwrapA x, unwrapB x, unwrapC x, unwrapD x)++    unwrapA = mapMay $ \case+                TagA4 a -> Just a+                _       -> Nothing++    unwrapB = mapMay $ \case+                TagB4 a -> Just a+                _       -> Nothing++    unwrapC = mapMay $ \case+                TagC4 a -> Just a+                _       -> Nothing++    unwrapD = mapMay $ \case+                TagD4 a -> Just a+                _       -> Nothing++-- | Flattens event stream producer by switching between event streams.+-- When next event stream happens it shuts down the previous one.+switch :: Frp m => Evt m (Evt m a) -> Evt m a+switch evts = Evt $ \go -> do+  tidRef <- proxyNewRef evts Nothing+  let stop = mapM_ killThread =<< liftIO (readRef tidRef)+  lock <- newEmptyMVar  -- we use this lock to make sure that next process+                        -- does not start before we saved it's threadId for stopping.+  runEvt evts (\evt -> do+    stop+    tid <- fork (takeMVar lock >> runEvt evt go)  -- delay until threadId is saved+    liftIO $ writeRef tidRef (Just tid)           -- save tid (for stopping)+    putMVar lock ()                               -- start the event process+    ) `finally` stop++-- | Switches between dynamic producers.+switchD :: Frp m => Dyn m a -> Evt m (Dyn m a) -> Dyn m a+switchD d evts = Dyn extract resEvt init (pure ())+  where+    init    = runDyn d+    extract = readDyn++    resEvt = Evt $ \go -> do+      runEvt evts $ \dyn -> do+        ref <- runDyn dyn+        go ref `finally` cancelDyn ref++---------------------------------------------------------+-- channels++-- | Creates the event stream that listens to MVar based channel.+-- If any value is put chan the event stream fires the callback.+mchanEvt :: (Frp m) => M.Chan a -> Evt m a+mchanEvt chan = Evt $ \go -> do+  chan <- liftIO $ M.dupChan chan+  loop chan go+  where+    loop chan go = do+      a <- liftIO $ M.readChan chan+      go a+      loop chan go++-- | Creates the event stream that listens to @TChan@ based channel.+-- If any value is put chan the event stream fires the callback.+tchanEvt :: (Frp m) => TChan a -> Evt m a+tchanEvt chan = Evt $ \go -> do+  chan <- liftIO $ atomically $ dupTChan chan+  loop chan go+  where+    loop chan go = do+      a <- liftIO $ atomically $ readTChan chan+      go a+      loop chan go++-- | Creates the event stream that listens to unagi channel (package @unagi-chan@).+-- If any value is put chan the event stream fires the callback.+uchanEvt :: (Frp m) => InChan a -> Evt m a+uchanEvt chan = Evt $ \go -> do+  chan <- liftIO $ U.dupChan chan+  loop chan go+  where+    loop chan go = do+      a <- liftIO $ U.readChan chan+      go a+      loop chan go++type UChan a = (U.InChan a, U.OutChan a)++--------------------------------------------------------------------------------++proxyNewRef :: Frp m => Evt m a -> b -> m (Ref m b)+proxyNewRef _ v = liftIO $ newRef v++proxyNewRefDyn :: Frp m => Dyn m a -> b -> m (Ref m b)+proxyNewRefDyn _ v = liftIO $ newRef v++proxyFunRes :: (a -> b) -> b+proxyFunRes _ = undefined++---------------------------------------------------------------------------+-- utilities++-- | Queries current time periodically with given period in seconds.+clock :: Frp m => NominalDiffTime -> Evt m UTCTime+clock t = Evt $ \go -> periodic t $ go =<< liftIO getCurrentTime++-- | Produces pulse events with given period in seconds.+pulse :: Frp m => NominalDiffTime -> Evt m ()+pulse t = Evt $ \go -> periodic t (go ())++-- | Produces pulse events with given period in seconds+-- and also tells how many seconds exactly has passed.+-- It can be useful for simulations of models that are based on differential equations.+-- As event streams carries how much time has passed between simulation steps.+ticks :: Frp m => NominalDiffTime -> Evt m NominalDiffTime+ticks t = Evt $ \go -> do+  startRef <- liftIO $ newIORef =<< getCurrentTime+  periodic t $ do+    dt <- liftIO $ do+      cur   <- getCurrentTime+      start <- readIORef startRef+      writeIORef startRef cur+      pure $ cur `diffUTCTime` start+    go dt++-- | Timer behaves like tocks only it produces accumulated time since beginning+-- of the process. It calculates them by querying current time and suntracting start time from it.+--+-- It can be though of as:+--+-- > sums ticks+timer :: Frp m => NominalDiffTime -> Evt m NominalDiffTime+timer t = Evt $ \go -> do+  start <- liftIO getCurrentTime+  periodic t $ go =<< liftIO ((`diffUTCTime` start) <$> getCurrentTime)++-- | Timer as dynamic signal.+timerD :: Frp m => NominalDiffTime -> Dyn m NominalDiffTime+timerD t = hold 0 $ timer t++{-# NOINLINE periodic #-}+-- | Periodically triggers callback.+periodic :: MonadIO m => NominalDiffTime -> m () -> m ()+periodic dur proc = do+  startRef <- liftIO $ newIORef =<< getCurrentTime+  fix $ \next -> do+    proc+    time <- liftIO $ do+      last <- readIORef startRef+      cur  <- getCurrentTime+      let dt = max 0 $ dur - (cur `diffUTCTime` last)+      writeIORef startRef (addUTCTime dt cur)+      pure dt+    sleep time+    next++-- | Stop the thread for some time in seconds.+sleep :: MonadIO m => NominalDiffTime -> m ()+sleep dt = liftIO . D.delay $ toMicroseconds dt++-- | Convert time to microseconds+toMicroseconds :: NominalDiffTime -> Integer+toMicroseconds t = ceiling $ toRational t * 1000000++--------------------------------------------------------------------------------++-- | Substitutes values in event stream with random values.+toRandom :: forall m a b . (Frp m, Random b) => Evt m a -> Evt m b+toRandom evt = Evt $ \go -> do+  tv <- proxyNewRef evt =<< liftIO newStdGen+  runEvt evt $ \_ -> do+    (a, g) <- liftIO $ random <$> readRef tv+    go a+    liftIO $ writeRef tv g++-- | Substitutes values in event stream with random values from the given range.+toRandomR :: forall m a b . (Frp m, Random b) => (b, b) -> Evt m a -> Evt m b+toRandomR range evt = Evt $ \go -> do+  tv <- proxyNewRef evt =<< liftIO newStdGen+  runEvt evt $ \_ -> do+    (a, g) <- liftIO $ randomR range <$> readRef tv+    go a+    liftIO $ writeRef tv g++-- | Substitutes values in event stream with random values.+withRandom :: forall m a b . (Frp m, Random b) => Evt m a -> Evt m (b, a)+withRandom evt = Evt $ \go -> do+  tv <- proxyNewRef evt =<< liftIO newStdGen+  runEvt evt $ \x -> do+    (a, g) <- liftIO $ random <$> readRef tv+    go (a, x)+    liftIO $ writeRef tv g++-- | Substitutes values in event stream with random values from the given range.+withRandomR :: forall m a b . (Frp m, Random b) => (b, b) -> Evt m a -> Evt m (b, a)+withRandomR range evt = Evt $ \go -> do+  tv <- proxyNewRef evt =<< liftIO newStdGen+  runEvt evt $ \x -> do+    (a, g) <- liftIO $ randomR range <$> readRef tv+    go (a, x)+    liftIO $ writeRef tv g++-- | Picks at random one element from the list+oneOf :: Frp m => [a] -> Evt m b -> Evt m a+oneOf xs evt = listAt xs $ toRandomR (0, len - 1) evt+  where+    len = length xs++-- | Picks at random one element from the list+withOneOf :: Frp m => [a] -> Evt m b -> Evt m (a, b)+withOneOf xs evt = first (vec V.! ) <$> withRandomR (0, len - 1) evt+  where+    len = V.length vec+    vec = V.fromList xs++-- | Picks at random one element from the list. We also provide distribution of events.+-- Probability to pick up the element. Sum of probabilities should equal to 1.+freqOf :: (R.MonadRandom m, Frp m) => Dyn m [(a, Rational)] -> Evt m b -> Evt m a+freqOf dynVals evts = applyMay' ((\vals -> const (go vals)) <$> dynVals) evts+  where+    go vals = R.fromListMay vals++-- | Picks at random one element from the list. We also provide distribution of events.+-- Probability to pick up the element. Sum of probabilities should equal to 1.+withFreqOf :: (R.MonadRandom m, Frp m) => Dyn m [(a, Rational)] -> Evt m b -> Evt m (a, b)+withFreqOf dynVals evts = applyMay' (go <$> dynVals) evts+  where+    go vals x = fmap (fmap ((, x))) $ R.fromListMay vals++-- | Skips at random elements from the list. We provide frequency to skip events with dynamic first argument.+randSkip :: Frp m => Dyn m Double -> Evt m a -> Evt m a+randSkip prob evt = randSkipBy (const <$> prob) evt++-- | Skips elements at random. The probability to skip element depends on the element itself.+randSkipBy :: Frp m => Dyn m (a -> Double) -> Evt m a -> Evt m a+randSkipBy  prob evt = attachWithMay f prob $ withRandomR (0, 1 :: Double) evt+  where+    f getProb (curProb, a)+      | curProb < getProb a  = Nothing+      | otherwise            = Just a++--------------------------------------------------------------------------------++-- | Delays in the thread of execution. Note that it can interfere+-- and screw up functions like clock, timer, pulse, ticks+delay :: Frp m => NominalDiffTime -> Evt m a -> Evt m a+delay dt evt = Evt $ \go ->+  runEvt evt $ \x -> sleep dt >> go x++-- | Delays in background by forking on each event.+-- Note tht if delayed event was put into background prior+-- to stopping of the main event stream it will fire anyway.+-- There is no way to stop it.+delayFork :: Frp m => NominalDiffTime -> Evt m a -> Evt m a+delayFork dt evt = Evt $ \go ->+  runEvt evt $ \x -> void $ fork $ sleep dt >> go x++--------------------------------------------------------------------------------+-- effectful functor++class FunctorM f where+  fmap' :: Frp m => (a -> m b) -> f m a -> f m b++instance FunctorM Evt where+  fmap' f evt = Evt $ \go -> runEvt evt $ \x -> go =<< f x++instance FunctorM Dyn where+  fmap' f (ConstDyn a)                = Dyn f never (pure a) (pure ())+  fmap' f (Dyn extract evt s release) = Dyn (f <=< extract) evt s release++--------------------------------------------------------------------------------+-- Boolean instances++instance (Boolean b, Frp m) => Boolean (Dyn m b) where+  true = pure true+  false = pure false+  notB = fmap notB+  (&&*) = liftA2 (&&*)+  (||*) = liftA2 (||*)++type instance BooleanOf (Dyn m a) = Dyn m (BooleanOf a)++instance (Frp m, IfB a) => IfB (Dyn m a) where+  ifB = liftA3 ifB++instance (EqB a, Frp m) => EqB (Dyn m a) where+  (==*) = liftA2 (==*)++instance (OrdB a, Frp m) => OrdB (Dyn m a) where+  (<*) = liftA2 (<*)+  (>*) = liftA2 (>*)+  (<=*) = liftA2 (<=*)+  (>=*) = liftA2 (>=*)++--------------------------------------------------------------------------------+-- Vector Space instances++instance (AdditiveGroup a, Frp m) => AdditiveGroup (Dyn m a) where+  zeroV = pure zeroV+  (^+^) = liftA2 (^+^)+  (^-^) = liftA2 (^-^)+  negateV = fmap negateV++instance (VectorSpace a, Frp m) => VectorSpace (Dyn m a) where+  type Scalar (Dyn m a) = Dyn m (Scalar a)+  (*^) = liftA2 (*^)++instance (AffineSpace p, Frp m) => AffineSpace (Dyn m p) where+  type Diff (Dyn m p) = Dyn m (Diff p)+  (.-.) = liftA2 (.-.)+  (.+^) = liftA2 (.+^)++class BasisArity v where+  basisArity :: v -> Int++instance BasisArity Float where+  basisArity _ = 1++instance BasisArity Double where+  basisArity _ = 1++instance (BasisArity a, BasisArity b) =>  BasisArity (a, b) where+  basisArity v = basisArity (proxyA v) + basisArity (proxyB v)+    where+      proxyA :: (a, b) -> a+      proxyA _ = undefined++      proxyB :: (a, b) -> b+      proxyB _ = undefined++instance (BasisArity a, BasisArity b, BasisArity c) =>  BasisArity (a, b, c) where+  basisArity v = basisArity (proxyA v) + basisArity (proxyB v) + basisArity (proxyC v)+    where+      proxyA :: (a, b, c) -> a+      proxyA _ = undefined++      proxyB :: (a, b, c) -> b+      proxyB _ = undefined++      proxyC :: (a, b, c) -> c+      proxyC _ = undefined++instance (Frp m, BasisArity v) => BasisArity (Dyn m v) where+  basisArity v = basisArity (proxy v)+    where+      proxy :: Dyn m v -> v+      proxy _ = undefined++instance (BasisArity v, HasBasis v, Frp m) => HasBasis (Dyn m v) where+  type Basis (Dyn m v) = Dyn m (Basis v)+  basisValue = fmap basisValue+  decompose v = fmap unTupleD $ unListD (basisArity v) $ fmap decompose v++  decompose' = liftA2 decompose'++unTupleD :: Frp m => Dyn m (a, b) -> (Dyn m a, Dyn m b)+unTupleD x = (fmap fst x, fmap snd x)++unListD :: Frp m => Int -> Dyn m [a] -> [Dyn m a]+unListD n ds = fmap (\a -> fmap ( !! a) ds) [0.. pred n]++instance (HasNormal v, Frp m) => HasNormal (Dyn m v) where+  normalVec = fmap normalVec++instance (HasCross2 v, Frp m) => HasCross2 (Dyn m v) where+  cross2 = fmap cross2++instance (HasCross3 v, Frp m) => HasCross3 (Dyn m v) where+  cross3 = liftA2 cross3++--------------------------------------------------------------------------------+-- Temporal media instances++instance Frp m => Melody (Evt m a) where+  (+:+) evtA evtB = Evt $ \go -> do+      runEvt evtA go+      runEvt evtB go++instance Frp m => Harmony (Evt m a) where+  (=:=) = (<>)++instance Frp m => Compose (Evt m a) where++instance Frp m => Loop (Evt m a) where+  loop evt = Evt $ \go -> forever (runEvt evt go)++-- | Takes an event and repeats it all the time.+forevers :: Frp m => Evt m a -> Evt m a+forevers evt = Evt $ \go -> forever (runEvt evt go)++type instance DurOf (Evt m a) = NominalDiffTime++instance Frp m => Limit (Evt m a) where+  lim t evt = Evt $ \go ->+    race_ (runEvt evt go) (sleep t)++--------------------------------------------------------------------------------+-- Parser++data St a = Final a | Cont a+  deriving (Functor)++data Parser m a b = forall s . Parser+  { parser'init   :: s+  , parser'modify :: (a -> s -> m (Maybe (St s)))+  , parser'get    :: s -> m (Maybe b)+  }++runParser :: Frp m => Parser m a b -> Evt m a -> m (Maybe b)+runParser (Parser init modify get) evt = do+  ref <- proxyNewRef evt (Cont init)++  waitAsync $ do+    runEvt evt $ \x -> do+      st <- liftIO $ readRef ref+      case st of+        Final s -> stopSelf+        Cont s  -> do+          mS' <- modify x s+          forM_ mS' $ \case+            Cont s  -> liftIO $ writeRef ref (Cont s)+            Final s -> liftIO $ do+              writeRef ref (Final s)+              stopSelf++  st <- liftIO (readRef ref)+  case st of+    Final s -> get s+    _       -> pure Nothing++heads :: Frp m => Evt m a -> m a+heads evt = do+  ref <- proxyNewRef evt Nothing+  waitAsync $ do+    runEvt evt $ \x -> do+      liftIO $ writeRef ref (Just x)+      stopSelf+  fromJust <$> liftIO (readRef ref)++-- | Reads single event+takeP :: Frp m => Parser m a b -> Evt m a -> Evt m b+takeP (Parser init modify get) evt = Evt $ \go -> do+  ref <- proxyNewRef evt init+  waitAsync $ do+    runEvt evt $ \x -> do+      s <- liftIO $ readRef ref+      mS' <- modify x s+      forM_ mS' $ \case+        Cont s'  -> liftIO $ writeRef ref s'+        Final s' -> do+          mapM_ go =<< get s'+          stopSelf++cycleP :: Frp m => Parser m a b -> Evt m a -> Evt m b+cycleP (Parser init modify get) evt = Evt $ \go -> do+  ref <- proxyNewRef evt init+  waitAsync $ do+    runEvt evt $ \x -> do+      s <- liftIO $ readRef ref+      mS' <- modify x s+      forM_ mS' $ \case+        Cont s'  -> liftIO $ writeRef ref s'+        Final s' -> do+          mapM_ go =<< get s'+          liftIO $ writeRef ref init++-- | Takes first element of the event stream and shuts the stream down.+headP :: Frp m => Parser m a a+headP = Parser init modify get+  where+    init = Nothing+    modify a _ = pure $ Just (Final (Just a))+    get = pure++maybeP :: Frp m => (a -> Maybe b) -> Parser m a b+maybeP f = Parser init modify get+  where+    init = Nothing+    modify a _ = pure $ fmap (Final . Just) $ f a+    get = pure++instance Frp m => Functor (Parser m a) where+  fmap f (Parser init modify get) = Parser init modify (fmap (fmap f) . get)++instance Frp m => Applicative (Parser m a) where+  pure a = Parser () (\_ _ -> pure (Just (Final ()))) (const $ pure $ Just a)+  (Parser initF modifyF getF) <*> (Parser initA modifyA getA) = Parser initRes modifyRes getRes+    where+      initRes = (Cont initF, Cont initA)++      modifyRes inp (sf, sa) = case sf of+        Cont f -> do+          mF' <- modifyF inp f+          pure $ fmap (Cont . (, sa)) mF'+        Final f ->+          case sa of+            Cont a -> do+              mA' <- modifyA inp a+              pure $ flip fmap mA' $ \case+                  Cont a'  -> Cont (Final f, Cont a')+                  Final a' -> Final (Final f, Final a')+            Final a -> pure (Just (Final (sf, sa)))++      getRes = \case+        (Final f, Final a) -> do+          mf <- getF f+          ma <- getA a+          pure (mf <*> ma)+        _                  -> pure Nothing+++-- | Create a new Event and a function that will cause the Event to fire+newTriggerEvt :: (Frp m, MonadIO io) => m (Evt m a, a -> io ())+newTriggerEvt = do+  chan <- liftIO U.newChan+  pure (uchanEvt (fst chan), liftIO . U.writeChan (fst chan))+
+ src/Dyna/Ref.hs view
@@ -0,0 +1,26 @@+module Dyna.Ref(+  IsRef(..),+) where++import Data.IORef+import Control.Concurrent.STM+import Control.Concurrent.STM.TVar++class IsRef ref where+  newRef    :: a -> IO (ref a)+  readRef   :: ref a -> IO a+  writeRef  :: ref a -> a -> IO ()+  modifyRef :: ref a -> (a -> a) -> IO ()++instance IsRef IORef where+  newRef = newIORef+  readRef = readIORef+  writeRef = writeIORef+  modifyRef = modifyIORef'++instance IsRef TVar where+  newRef = newTVarIO+  readRef = readTVarIO+  writeRef ref v = atomically $ writeTVar ref v+  modifyRef ref f = atomically $ modifyTVar' ref f+
+ test/Spec.hs view
@@ -0,0 +1,2 @@+main :: IO ()+main = putStrLn "Test suite not yet implemented"