content: {-# 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 -}
  