{- |
The core functionality of clocked signal functions,
supplying the type of clocked signal functions itself ('ClSF'),
behaviours (clock-independent/polymorphic signal functions),
and basic constructions of 'ClSF's that may use awareness of time as an effect.
-}

{-# LANGUAGE Arrows #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TypeFamilies #-}
module FRP.Rhine.ClSF.Core
  ( module FRP.Rhine.ClSF.Core
  , module Control.Arrow
  , module X
  )
  where

-- base
import Control.Arrow

-- transformers
import Control.Monad.Trans.Class
import Control.Monad.Trans.Reader (ReaderT, mapReaderT, withReaderT)

-- dunai
import Data.MonadicStreamFunction (MSF, arrM, constM, morphS, liftTransS)
import Data.MonadicStreamFunction as X hiding ((>>>^), (^>>>))

-- rhine
import FRP.Rhine.Clock      as X


-- * Clocked signal functions and behaviours

-- | A (synchronous, clocked) monadic stream function
--   with the additional side effect of being time-aware,
--   that is, reading the current 'TimeInfo' of the clock @cl@.
type ClSF m cl a b = MSF (ReaderT (TimeInfo cl) m) a b

-- | A clocked signal is a 'ClSF' with no input required.
--   It produces its output on its own.
type ClSignal m cl a = forall arbitrary . ClSF m cl arbitrary a

-- | A (side-effectful) behaviour is a time-aware stream
--   that doesn't depend on a particular clock.
--   @time@ denotes the 'TimeDomain'.
type Behaviour m time a = forall cl. time ~ Time cl => ClSignal m cl a

-- | Compatibility to U.S. american spelling.
type Behavior  m time a = Behaviour m time a

-- | A (side-effectful) behaviour function is a time-aware synchronous stream
--   function that doesn't depend on a particular clock.
--   @time@ denotes the 'TimeDomain'.
type BehaviourF m time a b = forall cl. time ~ Time cl => ClSF m cl a b

-- | Compatibility to U.S. american spelling.
type BehaviorF  m time a b = BehaviourF m time a b

-- * Utilities to create 'ClSF's from simpler data

-- | Hoist a 'ClSF' along a monad morphism.
hoistClSF
  :: (Monad m1, Monad m2)
  => (forall c. m1 c -> m2 c)
  -> ClSF m1 cl a b
  -> ClSF m2 cl a b
hoistClSF :: (forall c. m1 c -> m2 c) -> ClSF m1 cl a b -> ClSF m2 cl a b
hoistClSF forall c. m1 c -> m2 c
hoist = (forall c.
 ReaderT (TimeInfo cl) m1 c -> ReaderT (TimeInfo cl) m2 c)
-> ClSF m1 cl a b -> ClSF m2 cl a b
forall (m2 :: Type -> Type) (m1 :: Type -> Type) a b.
(Monad m2, Monad m1) =>
(forall c. m1 c -> m2 c) -> MSF m1 a b -> MSF m2 a b
morphS ((forall c.
  ReaderT (TimeInfo cl) m1 c -> ReaderT (TimeInfo cl) m2 c)
 -> ClSF m1 cl a b -> ClSF m2 cl a b)
-> (forall c.
    ReaderT (TimeInfo cl) m1 c -> ReaderT (TimeInfo cl) m2 c)
-> ClSF m1 cl a b
-> ClSF m2 cl a b
forall a b. (a -> b) -> a -> b
$ (m1 c -> m2 c)
-> ReaderT (TimeInfo cl) m1 c -> ReaderT (TimeInfo cl) m2 c
forall (m :: Type -> Type) a (n :: Type -> Type) b r.
(m a -> n b) -> ReaderT r m a -> ReaderT r n b
mapReaderT m1 c -> m2 c
forall c. m1 c -> m2 c
hoist

-- | Hoist a 'ClSF' and its clock along a monad morphism.
hoistClSFAndClock
  :: (Monad m1, Monad m2)
  => (forall c. m1 c -> m2 c)
  -> ClSF m1 cl a b
  -> ClSF m2 (HoistClock m1 m2 cl) a b
hoistClSFAndClock :: (forall c. m1 c -> m2 c)
-> ClSF m1 cl a b -> ClSF m2 (HoistClock m1 m2 cl) a b
hoistClSFAndClock forall c. m1 c -> m2 c
hoist
  = (forall c.
 ReaderT (TimeInfo cl) m1 c
 -> ReaderT (TimeInfo (HoistClock m1 m2 cl)) m2 c)
-> ClSF m1 cl a b -> ClSF m2 (HoistClock m1 m2 cl) a b
forall (m2 :: Type -> Type) (m1 :: Type -> Type) a b.
(Monad m2, Monad m1) =>
(forall c. m1 c -> m2 c) -> MSF m1 a b -> MSF m2 a b
morphS ((forall c.
  ReaderT (TimeInfo cl) m1 c
  -> ReaderT (TimeInfo (HoistClock m1 m2 cl)) m2 c)
 -> ClSF m1 cl a b -> ClSF m2 (HoistClock m1 m2 cl) a b)
-> (forall c.
    ReaderT (TimeInfo cl) m1 c
    -> ReaderT (TimeInfo (HoistClock m1 m2 cl)) m2 c)
-> ClSF m1 cl a b
-> ClSF m2 (HoistClock m1 m2 cl) a b
forall a b. (a -> b) -> a -> b
$ (TimeInfo (HoistClock m1 m2 cl) -> TimeInfo cl)
-> ReaderT (TimeInfo cl) m2 c
-> ReaderT (TimeInfo (HoistClock m1 m2 cl)) m2 c
forall r' r (m :: Type -> Type) a.
(r' -> r) -> ReaderT r m a -> ReaderT r' m a
withReaderT ((Tag (HoistClock m1 m2 cl) -> Tag cl)
-> TimeInfo (HoistClock m1 m2 cl) -> TimeInfo cl
forall cl1 cl2.
(Time cl1 ~ Time cl2) =>
(Tag cl1 -> Tag cl2) -> TimeInfo cl1 -> TimeInfo cl2
retag Tag (HoistClock m1 m2 cl) -> Tag cl
forall a. a -> a
id) (ReaderT (TimeInfo cl) m2 c
 -> ReaderT (TimeInfo (HoistClock m1 m2 cl)) m2 c)
-> (ReaderT (TimeInfo cl) m1 c -> ReaderT (TimeInfo cl) m2 c)
-> ReaderT (TimeInfo cl) m1 c
-> ReaderT (TimeInfo (HoistClock m1 m2 cl)) m2 c
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (m1 c -> m2 c)
-> ReaderT (TimeInfo cl) m1 c -> ReaderT (TimeInfo cl) m2 c
forall (m :: Type -> Type) a (n :: Type -> Type) b r.
(m a -> n b) -> ReaderT r m a -> ReaderT r n b
mapReaderT m1 c -> m2 c
forall c. m1 c -> m2 c
hoist

-- | Lift a 'ClSF' into a monad transformer.
liftClSF
  :: (Monad m, MonadTrans t, Monad (t m))
  => ClSF    m  cl a b
  -> ClSF (t m) cl a b
liftClSF :: ClSF m cl a b -> ClSF (t m) cl a b
liftClSF = (forall c. m c -> t m c) -> ClSF m cl a b -> ClSF (t m) cl a b
forall (m1 :: Type -> Type) (m2 :: Type -> Type) cl a b.
(Monad m1, Monad m2) =>
(forall c. m1 c -> m2 c) -> ClSF m1 cl a b -> ClSF m2 cl a b
hoistClSF forall c. m c -> t m c
forall (t :: (Type -> Type) -> Type -> Type) (m :: Type -> Type) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift

-- | Lift a 'ClSF' and its clock into a monad transformer.
liftClSFAndClock
  :: (Monad m, MonadTrans t, Monad (t m))
  => ClSF    m                 cl  a b
  -> ClSF (t m) (LiftClock m t cl) a b
liftClSFAndClock :: ClSF m cl a b -> ClSF (t m) (LiftClock m t cl) a b
liftClSFAndClock = (forall c. m c -> t m c)
-> ClSF m cl a b -> ClSF (t m) (LiftClock m t cl) a b
forall (m1 :: Type -> Type) (m2 :: Type -> Type) cl a b.
(Monad m1, Monad m2) =>
(forall c. m1 c -> m2 c)
-> ClSF m1 cl a b -> ClSF m2 (HoistClock m1 m2 cl) a b
hoistClSFAndClock forall c. m c -> t m c
forall (t :: (Type -> Type) -> Type -> Type) (m :: Type -> Type) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift

-- | A monadic stream function without dependency on time
--   is a 'ClSF' for any clock.
timeless :: Monad m => MSF m a b -> ClSF m cl a b
timeless :: MSF m a b -> ClSF m cl a b
timeless = MSF m a b -> ClSF m cl a b
forall (t :: (Type -> Type) -> Type -> Type) (m :: Type -> Type) a
       b.
(MonadTrans t, Monad m, Monad (t m)) =>
MSF m a b -> MSF (t m) a b
liftTransS

-- | Utility to lift Kleisli arrows directly to 'ClSF's.
arrMCl :: Monad m => (a -> m b) -> ClSF m cl a b
arrMCl :: (a -> m b) -> ClSF m cl a b
arrMCl = MSF m a b -> ClSF m cl a b
forall (m :: Type -> Type) a b cl.
Monad m =>
MSF m a b -> ClSF m cl a b
timeless (MSF m a b -> ClSF m cl a b)
-> ((a -> m b) -> MSF m a b) -> (a -> m b) -> ClSF m cl a b
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (a -> m b) -> MSF m a b
forall (m :: Type -> Type) a b. Monad m => (a -> m b) -> MSF m a b
arrM

-- | Version without input.
constMCl :: Monad m => m b -> ClSF m cl a b
constMCl :: m b -> ClSF m cl a b
constMCl = MSF m a b -> ClSF m cl a b
forall (m :: Type -> Type) a b cl.
Monad m =>
MSF m a b -> ClSF m cl a b
timeless (MSF m a b -> ClSF m cl a b)
-> (m b -> MSF m a b) -> m b -> ClSF m cl a b
forall b c a. (b -> c) -> (a -> b) -> a -> c
. m b -> MSF m a b
forall (m :: Type -> Type) b a. Monad m => m b -> MSF m a b
constM

{- | Call a 'ClSF' every time the input is 'Just a'.

Caution: This will not change the time differences since the last tick.
For example,
while @integrate 1@ is approximately the same as @timeInfoOf sinceInit@,
@mapMaybe $ integrate 1@ is very different from
@mapMaybe $ timeInfoOf sinceInit@.
The former only integrates when the input is @Just 1@,
whereas the latter always returns the correct time since initialisation.
-}
mapMaybe
  :: Monad m
  => ClSF m cl        a         b
  -> ClSF m cl (Maybe a) (Maybe b)
mapMaybe :: ClSF m cl a b -> ClSF m cl (Maybe a) (Maybe b)
mapMaybe ClSF m cl a b
behaviour = proc Maybe a
ma -> case Maybe a
ma of
  Maybe a
Nothing -> MSF (ReaderT (TimeInfo cl) m) (Maybe b) (Maybe b)
forall (a :: Type -> Type -> Type) b. Arrow a => a b b
returnA                -< Maybe b
forall a. Maybe a
Nothing
  Just a
a  -> (b -> Maybe b) -> MSF (ReaderT (TimeInfo cl) m) b (Maybe b)
forall (a :: Type -> Type -> Type) b c.
Arrow a =>
(b -> c) -> a b c
arr b -> Maybe b
forall a. a -> Maybe a
Just MSF (ReaderT (TimeInfo cl) m) b (Maybe b)
-> ClSF m cl a b -> MSF (ReaderT (TimeInfo cl) m) a (Maybe b)
forall k (cat :: k -> k -> Type) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
<<< ClSF m cl a b
behaviour -< a
a
-- TODO Consider integrating up the time deltas