rhine 0.1.1.0 → 0.2.0.0
raw patch · 9 files changed
+70/−108 lines, 9 filesdep ~basedep ~dunaiPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: base, dunai
API changes (from Hackage documentation)
- FRP.Rhine.Clock: instance (GHC.Base.Monad m1, GHC.Base.Monad m2, FRP.Rhine.Clock.Clock m1 a) => FRP.Rhine.Clock.Clock m2 (FRP.Rhine.Clock.HoistClock m1 m2 a)
- FRP.Rhine.Clock.Select: concatS :: Monad m => MStream m [b] -> MStream m b
- FRP.Rhine.Clock.Step: concatS :: Monad m => MSF m () [b] -> MSF m () b
- FRP.Rhine.SyncSF.Except: currentInput :: Monad m => SyncExcept m cl e b e
+ FRP.Rhine.Clock: instance (GHC.Base.Monad m1, GHC.Base.Monad m2, FRP.Rhine.Clock.Clock m1 cl) => FRP.Rhine.Clock.Clock m2 (FRP.Rhine.Clock.HoistClock m1 m2 cl)
+ FRP.Rhine.SyncSF: averageLin :: (Monad m, VectorSpace v, Groundfield v ~ Diff (TimeDomainOf cl)) => Diff (TimeDomainOf cl) -> SyncSF m cl v v
+ FRP.Rhine.SyncSF: averageLinFrom :: (Monad m, VectorSpace v, Groundfield v ~ Diff (TimeDomainOf cl)) => v -> Diff (TimeDomainOf cl) -> SyncSF m cl v v
+ FRP.Rhine.SyncSF: weightedAverageFrom :: (Monad m, VectorSpace v, Groundfield v ~ Diff (TimeDomainOf cl)) => v -> SyncSF m cl (v, Groundfield v) v
- FRP.Rhine.SyncSF: average :: (Monad m, VectorSpace v, Groundfield v ~ Diff (TimeDomainOf cl)) => Diff (TimeDomainOf cl) -> SyncSF m cl v v
+ FRP.Rhine.SyncSF: average :: (Monad m, VectorSpace v, Floating (Groundfield v), Groundfield v ~ Diff (TimeDomainOf cl)) => Diff (TimeDomainOf cl) -> SyncSF m cl v v
- FRP.Rhine.SyncSF: averageFrom :: (Monad m, VectorSpace v, Groundfield v ~ Diff (TimeDomainOf cl)) => v -> Diff (TimeDomainOf cl) -> SyncSF m cl v v
+ FRP.Rhine.SyncSF: averageFrom :: (Monad m, VectorSpace v, Floating (Groundfield v), Groundfield v ~ Diff (TimeDomainOf cl)) => v -> Diff (TimeDomainOf cl) -> SyncSF m cl v v
Files
- README.md +2/−0
- examples/test/Test.hs +0/−38
- rhine.cabal +5/−12
- src/FRP/Rhine/Clock.hs +2/−2
- src/FRP/Rhine/Clock/Realtime/Audio.hs +0/−11
- src/FRP/Rhine/Clock/Select.hs +4/−12
- src/FRP/Rhine/Clock/Step.hs +2/−12
- src/FRP/Rhine/SyncSF.hs +53/−8
- src/FRP/Rhine/SyncSF/Except.hs +2/−13
README.md view
@@ -1,6 +1,8 @@ # README -------- +[](https://travis-ci.org/turion/rhine)+ Rhine is a library for synchronous and asynchronous Functional Reactive Programming (FRP). It separates the aspects of clocking, scheduling and resampling from each other, and ensures clock-safety on the type level.
− examples/test/Test.hs
@@ -1,38 +0,0 @@-{-# LANGUAGE Arrows #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE TypeFamilies #-}----- rhine-import FRP.Rhine-import FRP.Rhine.Clock.Realtime.Millisecond-import FRP.Rhine.Schedule.Concurrently---- | Calculates and prints the rounding errors that accumulate--- when calculating the time since the start of the simulation--- via an Euler integral.-showRoundingError- :: Diff (TimeDomainOf cl) ~ Double- => String -> SyncSF IO cl () ()-showRoundingError clName = proc () -> do- correct <- timeInfoOf sinceStart -< ()- simulated <- arr_ 1 >>> integral -< ()- liftS putStrLn -<- "Clock " ++ clName- ++ " ticks at time " ++ show correct- ++ " and simulates " ++ show simulated- ++ " => rounding error " ++ show (correct - simulated)---- | Show the rounding error for the 1000 milliseconds clock.-showREMS1000 :: SyncSF IO (Millisecond 1000) () ()-showREMS1000 = showRoundingError "Millisecond 1000"---- | Show the rounding error for the 350 milliseconds clock.-showREMS350 :: SyncSF IO (Millisecond 350) () ()-showREMS350 = showRoundingError "Millisecond 350"---- | The main program runs both synchronous signal functions in parallel,--- using a concurrent (GHC threads) schedule.-main :: IO ()-main = flow $ showREMS350 @@ waitClock **@ concurrently @** showREMS1000 @@ waitClock
rhine.cabal view
@@ -1,6 +1,6 @@ name: rhine -version: 0.1.1.0+version: 0.2.0.0 synopsis: Functional Reactive Programming with type-level clocks @@ -44,7 +44,7 @@ source-repository this type: git location: git@github.com:turion/rhine.git- tag: v0.1.1.0+ tag: v0.2.0.0 library@@ -84,7 +84,7 @@ -- Other library packages from which modules are imported. build-depends: base >= 4.7 && < 5- , dunai == 0.1.1.*+ , dunai == 0.2.0.* , transformers >= 0.4 && < 0.6 , time >= 1.6 && < 1.7 , free >= 4.12 && < 4.13@@ -98,19 +98,12 @@ -- Base language which the package is written in. default-language: Haskell2010 -executable test- hs-source-dirs: examples/test- main-is: Test.hs- ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N- build-depends: base >= 4.8 && <5- , rhine- default-language: Haskell2010 executable HelloWorld hs-source-dirs: examples main-is: HelloWorld.hs ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N- build-depends: base >= 4.8 && <5+ build-depends: base >= 4.7 && <5 , rhine default-language: Haskell2010 @@ -118,6 +111,6 @@ hs-source-dirs: examples main-is: Demonstration.hs ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N- build-depends: base >= 4.8 && <5+ build-depends: base >= 4.7 && <5 , rhine default-language: Haskell2010
src/FRP/Rhine/Clock.hs view
@@ -130,8 +130,8 @@ , monadMorphism :: forall a . m1 a -> m2 a } -instance (Monad m1, Monad m2, Clock m1 a)- => Clock m2 (HoistClock m1 m2 a) where+instance (Monad m1, Monad m2, Clock m1 cl)+ => Clock m2 (HoistClock m1 m2 cl) where type TimeDomainOf (HoistClock m1 m2 cl) = TimeDomainOf cl type Tag (HoistClock m1 m2 cl) = Tag cl startClock HoistClock {..} = do
src/FRP/Rhine/Clock/Realtime/Audio.hs view
@@ -22,14 +22,11 @@ import Data.Time.Clock -- 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@@ -155,11 +152,3 @@ { 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/Select.hs view
@@ -10,8 +10,7 @@ import FRP.Rhine -- dunai--- import Data.MonadicStreamFunction.Async (concatS)--- TODO dunai 0.1.2+import Data.MonadicStreamFunction.Async (concatS) -- base import Data.Maybe (catMaybes, maybeToList)@@ -27,7 +26,7 @@ instance (Monad m, Clock m cl) => Clock m (SelectClock cl a) where- type TimeDomainOf (SelectClock cl _) = TimeDomainOf cl+ type TimeDomainOf (SelectClock cl a) = TimeDomainOf cl type Tag (SelectClock cl a) = a startClock SelectClock {..} = do (runningClock, initialTime) <- startClock mainClock@@ -56,15 +55,8 @@ , (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-+-- | Helper function that runs an 'MSF' with 'Maybe' output+-- until it returns a value. filterS :: Monad m => MSF m () (Maybe b) -> MSF m () b filterS = concatS . (>>> arr maybeToList)
src/FRP/Rhine/Clock/Step.hs view
@@ -11,6 +11,8 @@ -- base import GHC.TypeLits +-- dunai+import Data.MonadicStreamFunction.Async (concatS) -- rhine import FRP.Rhine@@ -49,15 +51,3 @@ k <- arr (+1) <<< count -< () returnA -< [ (k, Left ()) | k `mod` n1 == 0 ] ++ [ (k, Right ()) | k `mod` n2 == 0 ]----- * To be ported to dunai---- TODO Will be in dunai-concatS :: Monad m => MSF m () [b] -> MSF 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
src/FRP/Rhine/SyncSF.hs view
@@ -1,7 +1,8 @@-{-# LANGUAGE Arrows #-}-{-# LANGUAGE RankNTypes #-}-{-# LANGUAGE RecordWildCards #-}-{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE Arrows #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE TypeFamilies #-} module FRP.Rhine.SyncSF where @@ -163,25 +164,69 @@ => SyncSF m cl v v derivative = derivativeFrom zeroVector +-- | A weighted moving average signal function.+-- The output is the average of the first input,+-- weighted by the second input+-- (which is assumed to be always between 0 and 1).+-- The weight is applied to the average of the last tick,+-- so a weight of 1 simply repeats the past value unchanged,+-- whereas a weight of 0 outputs the current value.+weightedAverageFrom+ :: ( Monad m, VectorSpace v+ , Groundfield v ~ Diff (TimeDomainOf cl))+ => v -- ^ The initial position+ -> SyncSF m cl (v, Groundfield v) v+weightedAverageFrom v0 = feedback v0 $ proc ((v, weight), vAvg) -> do+ let+ vAvg' = weight *^ vAvg ^+^ (1 - weight) *^ v+ returnA -< (vAvg', vAvg') --- | An average, or low pass. It will average out, or filter,+-- | An exponential moving average, or low pass.+-- It will average out, or filter, -- all features below a given time scale. averageFrom :: ( Monad m, VectorSpace v+ , Floating (Groundfield v) , Groundfield v ~ Diff (TimeDomainOf cl)) => v -- ^ The initial position -> Diff (TimeDomainOf cl) -- ^ The time scale on which the signal is averaged -> SyncSF m cl v v-averageFrom v0 t = feedback v0 $ proc (v, vAvg) -> do+averageFrom v0 t = proc v -> do TimeInfo {..} <- timeInfo -< ()- let vAvg' = (v ^* sinceTick ^+^ vAvg ^* t) ^/ (sinceTick + t)- returnA -< (vAvg', vAvg')+ let+ weight = exp $ - (sinceTick / t)+ weightedAverageFrom v0 -< (v, weight) -- | An average, or low pass, initialised to zero. average :: ( Monad m, VectorSpace v+ , Floating (Groundfield v) , Groundfield v ~ Diff (TimeDomainOf cl)) => Diff (TimeDomainOf cl) -- ^ The time scale on which the signal is averaged -> SyncSF m cl v v average = averageFrom zeroVector++-- | A linearised version of 'averageFrom'.+-- It is more efficient, but only accurate+-- if the supplied time scale is much bigger+-- than the average time difference between two ticks.+averageLinFrom+ :: ( Monad m, VectorSpace v+ , Groundfield v ~ Diff (TimeDomainOf cl))+ => v -- ^ The initial position+ -> Diff (TimeDomainOf cl) -- ^ The time scale on which the signal is averaged+ -> SyncSF m cl v v+averageLinFrom v0 t = proc v -> do+ TimeInfo {..} <- timeInfo -< ()+ let+ weight = t / (sinceTick + t)+ weightedAverageFrom v0 -< (v, weight)++-- | Linearised version of 'average'.+averageLin+ :: ( Monad m, VectorSpace v+ , Groundfield v ~ Diff (TimeDomainOf cl))+ => Diff (TimeDomainOf cl) -- ^ The time scale on which the signal is averaged+ -> SyncSF m cl v v+averageLin = averageLinFrom zeroVector
src/FRP/Rhine/SyncSF/Except.hs view
@@ -34,8 +34,7 @@ -- | 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+once f = MSFE.once $ lift . f -- | A variant of |once| without input. once_ :: Monad m => m e -> SyncExcept m cl a b e@@ -54,17 +53,7 @@ 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+step f = MSFE.step $ lift . f