{-# LANGUAGE GADTs #-}

{- |
Run closed 'Rhine's (which are signal functions together with matching clocks)
as main loops.
-}
module FRP.Rhine.Reactimation where

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

-- * Running a Rhine

{- |
Takes a closed 'Rhine' (with trivial input and output),
and runs it indefinitely.
This is typically the main loop.

All input has to be created, and all output has to be consumed
by means of side effects in a monad 'm'.

Basic usage (synchronous case):

@
sensor :: ClSF MyMonad MyClock () a
sensor = constMCl produceData

processing :: ClSF MyMonad MyClock a b
processing = ...

actuator :: ClSF MyMonad MyClock b ()
actuator = arrMCl consumeData

mainSF :: ClSF MyMonad MyClock () ()
mainSF = sensor >-> processing >-> actuator

main :: MyMonad ()
main = flow $ mainSF @@ clock
@
-}

-- TODO Can we chuck the constraints into Clock m cl?
flow ::
  ( Monad m
  , Clock m cl
  , GetClockProxy cl
  , Time cl ~ Time (In cl)
  , Time cl ~ Time (Out cl)
  ) =>
  Rhine m cl () () ->
  m void
flow :: forall (m :: Type -> Type) cl void.
(Monad m, Clock m cl, GetClockProxy cl, Time cl ~ Time (In cl),
 Time cl ~ Time (Out cl)) =>
Rhine m cl () () -> m void
flow Rhine m cl () ()
rhine = do
  Automaton m () (Maybe ())
automaton <- Rhine m cl () () -> m (Automaton m () (Maybe ()))
forall (m :: Type -> Type) cl a b.
(Monad m, Clock m cl, GetClockProxy cl) =>
Rhine m cl a b -> m (Automaton m a (Maybe b))
eraseClock Rhine m cl () ()
rhine
  Automaton m () () -> m void
forall (m :: Type -> Type) void.
Monad m =>
Automaton m () () -> m void
reactimate (Automaton m () () -> m void) -> Automaton m () () -> m void
forall a b. (a -> b) -> a -> b
$ Automaton m () (Maybe ())
automaton Automaton m () (Maybe ())
-> Automaton m (Maybe ()) () -> Automaton m () ()
forall {k} (cat :: k -> k -> Type) (a :: k) (b :: k) (c :: k).
Category cat =>
cat a b -> cat b c -> cat a c
>>> (Maybe () -> ()) -> Automaton m (Maybe ()) ()
forall b c. (b -> c) -> Automaton m b c
forall (a :: Type -> Type -> Type) b c.
Arrow a =>
(b -> c) -> a b c
arr (() -> Maybe () -> ()
forall a b. a -> b -> a
const ())
{-# INLINE flow #-}

{- | Like 'flow', but with the type signature specialized to @m ()@.

This is sometimes useful when dealing with ambiguous types.
-}
flow_ ::
  ( Monad m
  , Clock m cl
  , GetClockProxy cl
  , Time cl ~ Time (In cl)
  , Time cl ~ Time (Out cl)
  ) =>
  Rhine m cl () () ->
  m ()
flow_ :: forall (m :: Type -> Type) cl.
(Monad m, Clock m cl, GetClockProxy cl, Time cl ~ Time (In cl),
 Time cl ~ Time (Out cl)) =>
Rhine m cl () () -> m ()
flow_ = Rhine m cl () () -> m ()
forall (m :: Type -> Type) cl void.
(Monad m, Clock m cl, GetClockProxy cl, Time cl ~ Time (In cl),
 Time cl ~ Time (Out cl)) =>
Rhine m cl () () -> m void
flow

{- | Run a synchronous 'ClSF' with its clock as a main loop,
   similar to Yampa's, or Dunai's, 'reactimate'.
-}
reactimateCl ::
  ( Monad m
  , Clock m cl
  , GetClockProxy cl
  , cl ~ In cl
  , cl ~ Out cl
  ) =>
  cl ->
  ClSF m cl () () ->
  m ()
reactimateCl :: forall (m :: Type -> Type) cl.
(Monad m, Clock m cl, GetClockProxy cl, cl ~ In cl, cl ~ Out cl) =>
cl -> ClSF m cl () () -> m ()
reactimateCl cl
cl ClSF m cl () ()
clsf = Rhine m cl () () -> m ()
forall (m :: Type -> Type) cl void.
(Monad m, Clock m cl, GetClockProxy cl, Time cl ~ Time (In cl),
 Time cl ~ Time (Out cl)) =>
Rhine m cl () () -> m void
flow (Rhine m cl () () -> m ()) -> Rhine m cl () () -> m ()
forall a b. (a -> b) -> a -> b
$ ClSF m cl () ()
clsf ClSF m cl () () -> cl -> Rhine m cl () ()
forall cl (m :: Type -> Type) a b.
(cl ~ In cl, cl ~ Out cl) =>
ClSF m cl a b -> cl -> Rhine m cl a b
@@ cl
cl
{-# INLINE reactimateCl #-}