packages feed

rhine-1.8: src/FRP/Rhine/Reactimation/Combinators.hs

{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TypeFamilies #-}

{- |
Combinators to create 'Rhine's (main programs) from basic components
such as 'ClSF's, clocks, 'ResamplingBuffer's and 'Schedule's.

The combinator names are often mixed of the symbols @, @*@ and @>@,
and several other symbols.
The general mnemonic for combinator names is:

* @ annotates a data processing unit such as a signal function, network or buffer
  with temporal information like a clock or a schedule.
* @*@ composes parallely.
* @>@ composes sequentially.
-}
module FRP.Rhine.Reactimation.Combinators where

-- rhine
import FRP.Rhine.ClSF.Core
import FRP.Rhine.Clock
import FRP.Rhine.Clock.Proxy
import FRP.Rhine.ResamplingBuffer
import FRP.Rhine.SN
import FRP.Rhine.SN.Combinators
import FRP.Rhine.Schedule
import FRP.Rhine.Type

-- * Combinators and syntactic sugar for high-level composition of signal networks.

infix 5 @@

{- FOURMOLU_DISABLE -}
{- | Create a synchronous 'Rhine' by combining a clocked signal function with a matching clock.
   Synchronicity is ensured by requiring that data enters (@In cl@)
   and leaves (@Out cl@) the system at the same as it is processed (@cl@).
-}
(@@) ::
  ( cl ~ In cl
  , cl ~ Out cl
  , Monad m
  , Clock m cl
  , GetClockProxy cl
  ) =>
  ClSF  m cl a b ->
          cl     ->
  Rhine m cl a b
(@@) = Rhine . synchronous
{-# INLINE (@@) #-}

{- | A purely syntactical convenience construction
   enabling quadruple syntax for sequential composition, as described below.
-}
infix 2 >--

data RhineAndResamplingBuffer m cl1 inCl2 a c
  = forall b.
    RhineAndResamplingBuffer (Rhine m cl1 a b) (ResamplingBuffer m (Out cl1) inCl2 b c)

-- | Syntactic sugar for 'RhineAndResamplingBuffer'.
(>--) ::
  Rhine                    m      cl1        a b   ->
  ResamplingBuffer         m (Out cl1) inCl2   b c ->
  RhineAndResamplingBuffer m      cl1  inCl2 a   c
(>--) = RhineAndResamplingBuffer

{- | The combinators for sequential composition allow for the following syntax:

@
rh1   :: Rhine            m      cl1           a b
rh1   =  ...

rh2   :: Rhine            m               cl2      c d
rh2   =  ...

rb    :: ResamplingBuffer m (Out cl1) (In cl2)   b c
rb    =  ...

rh    :: Rhine m (SequentialClock cl1 cl2) a d
rh    =  rh1 >-- rb --> rh2
@
-}
infixr 1 -->
(-->) ::
  ( Clock m cl1
  , Clock m cl2
  , Monad m
  , Time cl1 ~ Time cl2
  , Time (Out cl1) ~ Time cl1
  , Time (In  cl2) ~ Time cl2
  , Clock m (Out cl1), Clock m (Out cl2)
  , Clock m (In  cl1), Clock m (In  cl2)
  , In cl2 ~ inCl2
  , GetClockProxy cl1, GetClockProxy cl2
  ) =>
  RhineAndResamplingBuffer m cl1 inCl2 a b ->
  Rhine m cl2 b c ->
  Rhine m (SequentialClock cl1 cl2) a c
RhineAndResamplingBuffer (Rhine sn1 cl1) rb --> (Rhine sn2 cl2) =
  Rhine (sequential sn1 rb sn2) (SequentialClock cl1 cl2)

{- | The combinators for parallel composition allow for the following syntax:

@
rh1   :: Rhine m                clL      a         b
rh1   =  ...

rh2   :: Rhine m                    clR  a           c
rh2   =  ...

rh    :: Rhine m (ParallelClock clL clR) a (Either b c)
rh    =  rh1 +\@+ rh2
@
-}
infix 3 +@+
(+@+) ::
  ( Monad m, Clock m clL, Clock m clR
  , Clock m (Out clL), Clock m (Out clR)
  , GetClockProxy clL, GetClockProxy clR
  , Time clL ~ Time (Out clL), Time clR ~ Time (Out clR)
  , Time clL ~ Time (In  clL), Time clR ~ Time (In  clR)
  , Time clL ~ Time clR
  ) =>
  Rhine m                clL      a         b ->
  Rhine m                    clR  a           c ->
  Rhine m (ParallelClock clL clR) a (Either b c)
Rhine sn1 clL +@+ Rhine sn2 clR =
  Rhine (sn1 ++++ sn2) (ParallelClock clL clR)

{- | The combinators for parallel composition allow for the following syntax:

@
rh1   :: Rhine m                clL      a b
rh1   =  ...

rh2   :: Rhine m                    clR  a b
rh2   =  ...

rh    :: Rhine m (ParallelClock clL clR) a b
rh    =  rh1 |\@| rh2
@
-}
infix 3 |@|

(|@|) ::
  ( Monad m
  , Clock m clL
  , Clock m clR
  , Clock m (Out clL)
  , Clock m (Out clR)
  , GetClockProxy clL
  , GetClockProxy clR
  , Time clL ~ Time (Out clL)
  , Time clR ~ Time (Out clR)
  , Time clL ~ Time (In clL)
  , Time clR ~ Time (In clR)
  , Time clL ~ Time clR
  ) =>
  Rhine m                clL      a b ->
  Rhine m                    clR  a b ->
  Rhine m (ParallelClock clL clR) a b
Rhine sn1 clL |@| Rhine sn2 clR =
  Rhine (sn1 |||| sn2) (ParallelClock clL clR)

-- | Postcompose a 'Rhine' with a pure function.
(@>>^) ::
  Monad m =>
  Rhine m cl a b       ->
              (b -> c) ->
  Rhine m cl a      c
Rhine sn cl @>>^ f = Rhine (sn >>>^ f) cl

-- | Precompose a 'Rhine' with a pure function.
(^>>@) ::
  Monad m =>
            (a -> b)  ->
  Rhine m cl      b c ->
  Rhine m cl a      c
f ^>>@ Rhine sn cl = Rhine (f ^>>> sn) cl

-- | Postcompose a 'Rhine' with a 'ClSF'.
(@>-^) ::
  ( Clock m (Out cl), GetClockProxy cl, Monad m
  , Time cl ~ Time (Out cl)
  ) =>
  Rhine m      cl  a b   ->
  ClSF  m (Out cl)   b c ->
  Rhine m      cl  a   c
Rhine sn cl @>-^ clsf = Rhine (sn >--^ clsf) cl

-- | Precompose a 'Rhine' with a 'ClSF'.
(^->@) ::
  ( Clock m (In cl), GetClockProxy cl, Monad m
  , Time cl ~ Time (In cl)
  ) =>
  ClSF  m (In cl) a b   ->
  Rhine m     cl    b c ->
  Rhine m     cl  a   c
clsf ^->@ Rhine sn cl = Rhine (clsf ^--> sn) cl
{- FOURMOLU_ENABLE -}