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.
Signal processing units can be annotated by clocks,
which hold the information when data will be
input, processed and output.
Different components of the signal network
will become active at different times, or work
at different rates.
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:
flow $ arrMSync_ (putStrLn "Hello World!") (waitClock :: Millisecond 100)
[
Skip to Readme]
| Versions |
0.1.0.0, 0.1.1.0, 0.2.0.0, 0.2.0.0, 0.3.0.0, 0.4.0.0, 0.4.0.1, 0.4.0.2, 0.4.0.3, 0.4.0.4, 0.5.0.0, 0.5.0.1, 0.5.1.0, 0.5.1.1, 0.6.0, 0.7.0, 0.7.1, 0.8.0.0, 0.8.0.1, 0.8.1, 0.8.1.1, 0.9, 1.0, 1.1, 1.2, 1.3, 1.4, 1.4.0.1, 1.5, 1.6 |
| Change log |
None available |
| Dependencies |
base (>=4.7 && <5), containers (>=0.5 && <0.6), dunai (>=0.2.0 && <0.2.1), free (>=4.12 && <4.13), rhine, time (>=1.6 && <1.7), transformers (>=0.4 && <0.6) [details] |
| License |
BSD-3-Clause |
| Author |
Manuel Bärenz |
| Maintainer |
maths@manuelbaerenz.de |
| Category |
FRP |
| Source repo |
head: git clone git@github.com:turion/rhine.git this: git clone git@github.com:turion/rhine.git(tag v0.2.0.0) |
| Uploaded |
by turion at 2017-11-05T19:50:46Z |
README

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.
Complex reactive programs often process data at different rates.
For example, games, GUIs and media applications
may output audio and video signals, or receive
user input at unpredictable times.
Coordinating these different rates is a hard problem in general.
If not enough care is taken, buffer underruns and overflows, space and time leaks,
accidental synchronisation of independent sub-systems,
and concurrency issues such as dead-locks may all occur.
Rhine tackles these problems by annotating
the signal processing components with clocks,
which hold the information when data will be
input, processed and output.
Different components of the signal network
will become active at different times, or work
at different rates. If components running under different clocks need to communicate, it
has to be decided when each component be-
comes active ("scheduling"), and how data is
transferred between the different rates ("resampling").
Rhine separates all these aspects from each
other, and from the individual signal processing of each subsystem.
It offers a flexible API to all of them and implements several
reusable standard solutions. In the places
where these aspects need to intertwine, typing
constraints on clocks come into effect, enforcing clock safety.
A typical example, which can be run as cabal run Demonstration,
would be:
-- | Create a simple message containing the time stamp since program start,
-- for each tick of the clock.
-- Since 'createMessage' works for arbitrary clocks (and doesn't need further input data),
-- it is a 'Behaviour'.
-- @td@ is the 'TimeDomain' of any clock used to sample,
-- and it needs to be constrained in order for time differences
-- to have a 'Show' instance.
createMessage
:: (Monad m, Show (Diff td))
=> String
-> Behaviour m td String
createMessage str
= timeInfoOf sinceStart >-> arr show
>-> arr (("Clock " ++ str ++ " has ticked at: ") ++)
-- | Output a message /every second/ (= every 1000 milliseconds).
-- Let us assume we want to assure that 'printEverySecond'
-- is only called every second,
-- then we constrain its type signature with the clock @Millisecond 1000@.
printEverySecond :: Show a => SyncSF IO (Millisecond 1000) a ()
printEverySecond = arrMSync print
-- | Specialise 'createMessage' to a specific clock.
ms500 :: SyncSF IO (Millisecond 500) () String
ms500 = createMessage "500 MS"
ms1200 :: SyncSF IO (Millisecond 1200) () String
ms1200 = createMessage "1200 MS"
-- | Create messages every 500 ms and every 1200 ms,
-- collecting all of them in a list,
-- which is output every second.
main :: IO ()
main = flow $
ms500 @@ waitClock **@ concurrently @** ms1200 @@ waitClock
>-- collect -@- concurrently -->
printEverySecond @@ waitClock
-- | Uncomment the following for a type error (the clocks don't match):
-- typeError = ms500 >>> printEverySecond