packages feed

rhine 0.1.0.0 → 0.1.1.0

raw patch · 10 files changed

+244/−46 lines, 10 filesdep ~dunaidep ~transformersPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependency ranges changed: dunai, transformers

API changes (from Hackage documentation)

- FRP.Rhine.Clock.Realtime.Audio: instance (GHC.TypeLits.KnownNat bufferSize, FRP.Rhine.Clock.Realtime.Audio.AudioClockRate rate) => FRP.Rhine.Clock.Clock GHC.Types.IO (FRP.Rhine.Clock.Realtime.Audio.AudioClock rate bufferSize)
- FRP.Rhine.SyncSF: infixl 4 <-<
- FRP.Rhine.SyncSF: infixr 4 >->
+ FRP.Rhine.Clock: liftClock :: (Monad m, MonadTrans t) => cl -> LiftClock m t cl
+ FRP.Rhine.Clock: type LiftClock m t cl = HoistClock m (t m) cl
+ FRP.Rhine.Clock.Realtime.Audio: instance (Control.Monad.IO.Class.MonadIO m, GHC.TypeLits.KnownNat bufferSize, FRP.Rhine.Clock.Realtime.Audio.AudioClockRate rate) => FRP.Rhine.Clock.Clock m (FRP.Rhine.Clock.Realtime.Audio.AudioClock rate bufferSize)
+ FRP.Rhine.Clock.Realtime.Stdin: StdinClock :: StdinClock
+ FRP.Rhine.Clock.Realtime.Stdin: data StdinClock
+ FRP.Rhine.Clock.Realtime.Stdin: instance Control.Monad.IO.Class.MonadIO m => FRP.Rhine.Clock.Clock m FRP.Rhine.Clock.Realtime.Stdin.StdinClock
+ FRP.Rhine.Clock.Realtime.Stdin: instance GHC.Base.Monoid FRP.Rhine.Clock.Realtime.Stdin.StdinClock
+ FRP.Rhine.Clock.Select: SelectClock :: cl -> (Tag cl -> Maybe a) -> SelectClock cl a
+ FRP.Rhine.Clock.Select: [mainClock] :: SelectClock cl a -> cl
+ FRP.Rhine.Clock.Select: [select] :: SelectClock cl a -> Tag cl -> Maybe a
+ FRP.Rhine.Clock.Select: concatS :: Monad m => MStream m [b] -> MStream m b
+ FRP.Rhine.Clock.Select: data SelectClock cl a
+ FRP.Rhine.Clock.Select: filterS :: Monad m => MSF m () (Maybe b) -> MSF m () b
+ FRP.Rhine.Clock.Select: instance (GHC.Base.Monad m, FRP.Rhine.Clock.Clock m cl) => FRP.Rhine.Clock.Clock m (FRP.Rhine.Clock.Select.SelectClock cl a)
+ FRP.Rhine.Clock.Select: schedSelectClocks :: (Monad m, Monoid cl, Clock m cl) => Schedule m (SelectClock cl a) (SelectClock cl b)
+ FRP.Rhine.SyncSF: infixl 6 <-<
+ FRP.Rhine.SyncSF: infixr 6 >->
+ FRP.Rhine.SyncSF: type Behaviour m td a = forall cl. td ~ TimeDomainOf cl => SyncSignal m cl a
+ FRP.Rhine.SyncSF.Except: commuteReaderExcept :: ReaderT r (ExceptT e m) a -> ExceptT e (ReaderT r m) a
+ FRP.Rhine.SyncSF.Except: currentInput :: Monad m => SyncExcept m cl e b e
+ FRP.Rhine.SyncSF.Except: data Empty :: *
+ FRP.Rhine.SyncSF.Except: exceptS :: Monad m => MSF (ExceptT e m) a b -> MSF m a (Either e b)
+ FRP.Rhine.SyncSF.Except: once :: Monad m => (a -> m e) -> SyncExcept m cl a b e
+ FRP.Rhine.SyncSF.Except: once_ :: Monad m => m e -> SyncExcept m cl a b e
+ FRP.Rhine.SyncSF.Except: runMSFExcept :: MSFExcept m a b e -> MSF (ExceptT e m) a b
+ FRP.Rhine.SyncSF.Except: safe :: Monad m => MSF m a b -> MSFExcept m a b e
+ FRP.Rhine.SyncSF.Except: safely :: Monad m => MSFExcept m a b Empty -> MSF m a b
+ FRP.Rhine.SyncSF.Except: step :: Monad m => (a -> m (b, e)) -> SyncExcept m cl a b e
+ FRP.Rhine.SyncSF.Except: throwOn :: Monad m => e -> SyncSF (ExceptT e m) cl Bool ()
+ FRP.Rhine.SyncSF.Except: throwOn' :: Monad m => SyncSF (ExceptT e m) cl (Bool, e) ()
+ FRP.Rhine.SyncSF.Except: throwS :: Monad m => SyncSF (ExceptT e m) cl e a
+ FRP.Rhine.SyncSF.Except: try :: Monad m => SyncSF (ExceptT e m) cl a b -> SyncExcept m cl a b e
+ FRP.Rhine.SyncSF.Except: type SyncExcept m cl a b e = MSFExcept (ReaderT (TimeInfo cl) m) a b e
+ FRP.Rhine.TimeDomain: data UTCTime :: *
- FRP.Rhine.SyncSF: (<-<) :: Monad m => SyncSF m cl b c -> SyncSF m cl a b -> SyncSF m cl a c
+ FRP.Rhine.SyncSF: (<-<) :: Category cat => cat b c -> cat a b -> cat a c
- FRP.Rhine.SyncSF: (>->) :: Monad m => SyncSF m cl a b -> SyncSF m cl b c -> SyncSF m cl a c
+ FRP.Rhine.SyncSF: (>->) :: Category cat => cat a b -> cat b c -> cat a c
- FRP.Rhine.SyncSF: type Behaviour m td a = forall cl. td ~ TimeDomainOf cl => SyncSF m cl () a
+ FRP.Rhine.SyncSF: type SyncSF m cl a b = MSF (ReaderT (TimeInfo cl) m) a b

Files

README.md view
@@ -1,4 +1,4 @@-* README+# README --------  Rhine is a library for synchronous and asynchronous Functional Reactive Programming (FRP).
rhine.cabal view
@@ -1,6 +1,6 @@ name:                rhine -version:             0.1.0.0+version:             0.1.1.0  synopsis: Functional Reactive Programming with type-level clocks @@ -17,7 +17,7 @@   To schedule the components and allow them to communicate,   several standard scheduling and resampling solutions are implemented.   Own schedules and resampling buffers can be implemented in a reusable fashion.-  A (synchronous) program outputting "Hello World" every tenth of a second looks like this:+  A (synchronous) program outputting "Hello World!" every tenth of a second looks like this:   @flow $ arrMSync_ (putStrLn "Hello World!") @@ (waitClock :: Millisecond 100)@  @@ -44,7 +44,7 @@ source-repository this   type:     git   location: git@github.com:turion/rhine.git-  tag:      v0.1.0.0+  tag:      v0.1.1.0   library@@ -57,6 +57,8 @@     FRP.Rhine.Clock.Realtime.Audio     FRP.Rhine.Clock.Realtime.Busy     FRP.Rhine.Clock.Realtime.Millisecond+    FRP.Rhine.Clock.Realtime.Stdin+    FRP.Rhine.Clock.Select     FRP.Rhine.Clock.Step     FRP.Rhine.Reactimation     FRP.Rhine.Reactimation.Tick@@ -74,6 +76,7 @@     FRP.Rhine.SF     FRP.Rhine.SF.Combinators     FRP.Rhine.SyncSF+    FRP.Rhine.SyncSF.Except     FRP.Rhine.TimeDomain    -- LANGUAGE extensions used by modules in this package.@@ -81,8 +84,8 @@    -- Other library packages from which modules are imported.   build-depends:       base         >= 4.7   && < 5-                    ,  dunai        >= 0.1.1 && < 0.2-                    ,  transformers >= 0.5   && < 0.6+                    ,  dunai        == 0.1.1.*+                    ,  transformers >= 0.4   && < 0.6                     ,  time         >= 1.6   && < 1.7                     ,  free         >= 4.12  && < 4.13                     ,  containers   >= 0.5   && < 0.6
src/FRP/Rhine/Clock.hs view
@@ -7,6 +7,9 @@ {-# LANGUAGE TypeFamilies          #-} module FRP.Rhine.Clock where +-- transformers+import Control.Monad.Trans.Class (lift, MonadTrans)+ -- dunai import Data.MonadicStreamFunction @@ -139,3 +142,11 @@       ( hoistMSF monadMorphism runningClock       , initialTime       )++type LiftClock m t cl = HoistClock m (t m) cl++liftClock :: (Monad m, MonadTrans t) => cl -> LiftClock m t cl+liftClock hoistedClock = HoistClock+  { monadMorphism = lift+  , ..+  }
src/FRP/Rhine/Clock/Realtime/Audio.hs view
@@ -5,7 +5,8 @@ {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE TypeFamilies          #-} -{-# OPTIONS_GHC -Wno-unticked-promoted-constructors #-}+-- {-# OPTIONS_GHC -Wno-unticked-promoted-constructors #-}+-- TODO Find out exact version of cabal? GHC? that have a problem with this  module FRP.Rhine.Clock.Realtime.Audio   ( AudioClock (..)@@ -20,13 +21,15 @@ import GHC.TypeLits    (Nat, natVal, KnownNat) import Data.Time.Clock --- transformers?+-- transformers -- TODO Delete as soon as dunai is updated import Control.Monad.Trans.Class (lift)+import Control.Monad.IO.Class   -- dunai import Control.Monad.Trans.MSF.Except+  hiding (once, once_) -- TODO Delete with dunai 0.1.2  -- rhine import FRP.Rhine@@ -87,7 +90,8 @@ theBufferSize = fromInteger . natVal  -instance (KnownNat bufferSize, AudioClockRate rate) => Clock IO (AudioClock rate bufferSize) where+instance (MonadIO m, KnownNat bufferSize, AudioClockRate rate)+      => Clock m (AudioClock rate bufferSize) where   type TimeDomainOf (AudioClock rate bufferSize) = UTCTime   type Tag          (AudioClock rate bufferSize) = Maybe Double @@ -96,22 +100,20 @@       step       = picosecondsToDiffTime -- The only sufficiently precise conversion function                      $ round (10 ^ (12 :: Integer) / theRateNum audioClock :: Double)       bufferSize = theBufferSize audioClock-      once f = try $ arrM (lift . f) >>> throwS -- TODO Delete once dunai is updated-      once_ = once . const -      runningClock :: UTCTime -> Maybe Double -> MSF IO () (UTCTime, Maybe Double)+      runningClock :: MonadIO m => UTCTime -> Maybe Double -> MSF m () (UTCTime, Maybe Double)       runningClock initialTime maybeWasLate = safely $ do         bufferFullTime <- try $ proc () -> do           n <- count    -< ()           let nextTime = (realToFrac step * fromIntegral (n :: Int)) `addUTCTime` initialTime           _ <- throwOn' -< (n >= bufferSize, nextTime)           returnA       -< (nextTime, if n == 0 then maybeWasLate else Nothing)-        currentTime <- once_ getCurrentTime+        currentTime <- once_ $ liftIO getCurrentTime         let-          lateDiff = realToFrac $ currentTime `diffUTCTime` bufferFullTime+          lateDiff = currentTime `diffTime` bufferFullTime           late     = if lateDiff > 0 then Just lateDiff else Nothing         safe $ runningClock bufferFullTime late-    initialTime <- getCurrentTime+    initialTime <- liftIO getCurrentTime     return       ( runningClock initialTime Nothing       , initialTime@@ -153,3 +155,11 @@   { unscaledClock = PureAudioClock   , rescale       = double2Float }++-- ** To be ported to @dunai@++once :: Monad m => (a -> m e) -> MSFExcept m a b e+once f = try $ arrM (lift . f) >>> throwS+-- TODO Delete once dunai is updated+once_ :: Monad m => m e -> MSFExcept m a b e+once_ = once . const
+ src/FRP/Rhine/Clock/Realtime/Stdin.hs view
@@ -0,0 +1,35 @@+{-# LANGUAGE FlexibleInstances     #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE TypeFamilies          #-}+module FRP.Rhine.Clock.Realtime.Stdin where++-- base+import Data.Time.Clock++-- transformers+import Control.Monad.IO.Class++-- rhine+import FRP.Rhine++{- |+A clock that ticks for every line entered on the console,+outputting the entered line as its |Tag|.+-}+data StdinClock = StdinClock++instance MonadIO m => Clock m StdinClock where+  type TimeDomainOf StdinClock = UTCTime+  type Tag          StdinClock = String++  startClock _ = do+    initialTime <- liftIO getCurrentTime+    return+      (     arrM_ (liftIO getCurrentTime)+        &&& arrM_ (liftIO getLine)+      , initialTime+      )++instance Monoid StdinClock where+  mempty      = StdinClock+  mappend _ _ = StdinClock
+ src/FRP/Rhine/Clock/Select.hs view
@@ -0,0 +1,70 @@+{-# LANGUAGE Arrows                #-}+{-# LANGUAGE FlexibleInstances     #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE RecordWildCards       #-}+{-# LANGUAGE TupleSections         #-}+{-# LANGUAGE TypeFamilies          #-}+module FRP.Rhine.Clock.Select where++-- rhine+import FRP.Rhine++-- dunai+-- import Data.MonadicStreamFunction.Async (concatS)+-- TODO dunai 0.1.2++-- base+import Data.Maybe (catMaybes, maybeToList)++-- | A clock that selects certain subevents of type 'a',+--   from the tag of a main clock.+data SelectClock cl a = SelectClock+  { mainClock :: cl -- ^ The main clock+  -- | Return 'Nothing' if no tick of the subclock is required,+  --   or 'Just a' if the subclock should tick, with tag 'a'.+  , select    :: Tag cl -> Maybe a+  }+++instance (Monad m, Clock m cl) => Clock m (SelectClock cl a) where+  type TimeDomainOf (SelectClock cl _) = TimeDomainOf cl+  type Tag          (SelectClock cl a) = a+  startClock SelectClock {..} = do+    (runningClock, initialTime) <- startClock mainClock+    let+      runningSelectClock = filterS $ proc _ -> do+        (time, tag) <- runningClock -< ()+        returnA                     -< (time, ) <$> select tag+    return (runningSelectClock, initialTime)+++-- | A universal schedule for two subclocks of the same main clock.+--   The main clock must be a monoid (e.g. a singleton).+schedSelectClocks+  :: (Monad m, Monoid cl, Clock m cl)+  => Schedule m (SelectClock cl a) (SelectClock cl b)+schedSelectClocks = Schedule {..}+  where+    startSchedule subClock1 subClock2 = do+      (runningClock, initialTime) <- startClock+        $ mainClock subClock1 `mappend` mainClock subClock2+      let+        runningSelectClocks = concatS $ proc _ -> do+          (time, tag) <- runningClock -< ()+          returnA                     -< catMaybes+            [ (time, ) . Left  <$> select subClock1 tag+            , (time, ) . Right <$> select subClock2 tag ]+      return (runningSelectClocks, initialTime)++-- ** To be ported to @dunai@++concatS :: Monad m => MStream m [b] -> MStream m b+concatS msf = MSF $ \_ -> tick msf []+  where+    tick msf (b:bs) = return (b, MSF $ \_ -> tick msf bs)+    tick msf []     = do+      (bs, msf') <- unMSF msf ()+      tick msf' bs++filterS :: Monad m => MSF m () (Maybe b) -> MSF m () b+filterS = concatS . (>>> arr maybeToList)
src/FRP/Rhine/Reactimation/Tick.hs view
@@ -206,22 +206,7 @@         , buffer2  = buffer2 rightTickable         , lastTime = ParallelLastTime lastTimeA (lastTime rightTickable)         }-tick Tickable-  { ticksf   = Synchronous _-  , lastTime = SequentialLastTime {}-  } _ _ = error "Impossible pattern in tick"-tick Tickable-  { ticksf   = Synchronous _-  , lastTime = ParallelLastTime {}-  } _ _ = error "Impossible pattern in tick"-tick Tickable-  { ticksf   = Sequential {}-  , lastTime = LeafLastTime _-  } _ _ = error "Impossible pattern in tick"-tick Tickable-  { ticksf   = Parallel {}-  , lastTime = LeafLastTime _-  } _ _ = error "Impossible pattern in tick"+tick Tickable {} _ _ = error "Impossible pattern in tick"  -- TODO It seems wasteful to unwrap and rewrap log(N) Tickables -- (where N is the size of the clock tree) each tick,
src/FRP/Rhine/SyncSF.hs view
@@ -8,11 +8,12 @@  -- base import Control.Arrow+import Control.Category (Category) import qualified Control.Category (id)  -- transformers import Control.Monad.Trans.Reader-  ( ReaderT, ask, asks, mapReaderT, withReaderT)+  (ReaderT, ask, asks, mapReaderT, withReaderT)  -- dunai import Data.MonadicStreamFunction@@ -31,10 +32,14 @@ --   that is, reading the current 'TimeInfo' of the clock 'cl'. type SyncSF m cl a b = MSF (ReaderT (TimeInfo cl) m) a b +-- | A synchronous signal is a |SyncSF| with no input required.+--   It produces its output on its own.+type SyncSignal m cl a = SyncSF m cl () a+ -- | A (side-effectful) behaviour is a time-aware stream --   that doesn't depend on a particular clock.---   'td' denotes the time domain.-type Behaviour m td a = forall cl. td ~ TimeDomainOf cl => SyncSF m cl () a+--   'td' denotes the |TimeDomain|.+type Behaviour m td a = forall cl. td ~ TimeDomainOf cl => SyncSignal m cl a  -- | Compatibility to U.S. american spelling. type Behavior  m td a = Behaviour m td a@@ -80,24 +85,29 @@  -- * Useful aliases +-- TODO Is it cleverer to generalise to Arrow? {- | Alias for 'Control.Category.>>>' (sequential composition) with higher operator precedence, designed to work with the other operators, e.g.:  > syncsf1 >-> syncsf2 @@ clA **@ sched @** syncsf3 >-> syncsf4 @@ clB++The type signature specialises e.g. to++> (>->) :: Monad m => SyncSF m cl a b -> SyncSF m cl b c -> SyncSF m cl a c -}-infixr 4 >->-(>->) :: Monad m-      => SyncSF m cl a b-      -> SyncSF m cl   b c-      -> SyncSF m cl a   c+infixr 6 >->+(>->) :: Category cat+      => cat a b+      -> cat   b c+      -> cat a   c (>->) = (>>>) --- | Alias for 'Control.Arrow.<<<'.-infixl 4 <-<-(<-<) :: Monad m-      => SyncSF m cl   b c-      -> SyncSF m cl a b-      -> SyncSF m cl a   c+-- | Alias for 'Control.Category.<<<'.+infixl 6 <-<+(<-<) :: Category cat+      => cat   b c+      -> cat a b+      -> cat a   c (<-<) = (<<<)  {- | Output a constant value.
+ src/FRP/Rhine/SyncSF/Except.hs view
@@ -0,0 +1,70 @@+{-# LANGUAGE Arrows #-}+module FRP.Rhine.SyncSF.Except+  ( module FRP.Rhine.SyncSF.Except+  , module X+  , safe, safely, Empty, exceptS, runMSFExcept+  )+  where++-- transformers+import Control.Monad.Trans.Class (lift)+import Control.Monad.Trans.Except as X+import Control.Monad.Trans.Reader++-- dunai+import Control.Monad.Trans.MSF.Except hiding (try, once, once_, throwOn, throwOn', throwS)+-- TODO Find out whether there is a cleverer way to handle exports+import qualified Control.Monad.Trans.MSF.Except as MSFE++-- rhine+import FRP.Rhine+++type SyncExcept m cl a b e = MSFExcept (ReaderT (TimeInfo cl) m) a b e++-- | Commute the effects of the |ReaderT| and the |ExceptT| monad.+commuteReaderExcept :: ReaderT r (ExceptT e m) a -> ExceptT e (ReaderT r m) a+commuteReaderExcept a = ExceptT $ ReaderT $ \r -> runExceptT $ runReaderT a r++-- | Enter the monad context in the exception+--   for |SyncSF|s in the |ExceptT| monad.+try :: Monad m => SyncSF (ExceptT e m) cl a b -> SyncExcept m cl a b e+try = MSFE.try . liftMSFPurer commuteReaderExcept++-- | Within the same tick, perform a monadic action,+--   and immediately throw the value as an exception.+once :: Monad m => (a -> m e) -> SyncExcept m cl a b e+-- once f = MSFE.once $ lift . f -- TODO dunai 0.1.2+once f = try $ arrMSync (lift . f) >>> throwS++-- | A variant of |once| without input.+once_ :: Monad m => m e -> SyncExcept m cl a b e+once_ = once . const++-- |+throwS :: Monad m => SyncSF (ExceptT e m) cl e a+throwS = arrMSync throwE++throwOn' :: Monad m => SyncSF (ExceptT e m) cl (Bool, e) ()+throwOn' = proc (b, e) -> if b+  then throwS  -< e+  else returnA -< ()++throwOn :: Monad m => e -> SyncSF (ExceptT e m) cl Bool ()+throwOn e = proc b -> throwOn' -< (b, e)+++-- ** Will be in dunai-0.1.2++-- | Advances a single tick with the given Kleisli arrow,+--   and then throws an exception.+step :: Monad m => (a -> m (b, e)) -> SyncExcept m cl a b e+step f = try $ proc a -> do+  n      <- count               -< ()+  (b, e) <- arrMSync (lift . f) -< a+  _      <- throwOn'            -< (n > (1 :: Int), e)+  returnA                       -< b++-- | Immediately throw the current input as an exception.+currentInput :: Monad m => SyncExcept m cl e b e+currentInput = try throwS
src/FRP/Rhine/TimeDomain.hs view
@@ -1,7 +1,11 @@ {-# LANGUAGE FlexibleInstances          #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE TypeFamilies               #-}-module FRP.Rhine.TimeDomain where+module FRP.Rhine.TimeDomain+  ( module FRP.Rhine.TimeDomain+  , UTCTime+  )+  where  -- time import Data.Time.Clock (UTCTime, diffUTCTime)