{- |
Create and remove 'ReaderT' layers in 'ClSF's.
-}

{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE TypeFamilies #-}
module FRP.Rhine.ClSF.Reader where

-- base
import Data.Tuple (swap)

-- transformers
import Control.Monad.Trans.Reader

-- dunai
import qualified Control.Monad.Trans.MSF.Reader as MSF

-- rhine
import FRP.Rhine.ClSF.Core


-- | Commute two 'ReaderT' transformer layers past each other
commuteReaders :: ReaderT r1 (ReaderT r2 m) a -> ReaderT r2 (ReaderT r1 m) a
commuteReaders a
  = ReaderT $ \r1 -> ReaderT $ \r2 -> runReaderT (runReaderT a r2) r1

-- | Create ("wrap") a 'ReaderT' layer in the monad stack of a behaviour.
--   Each tick, the 'ReaderT' side effect is performed
--   by passing the original behaviour the extra @r@ input.
readerS
  :: Monad m
  => ClSF m cl (a, r) b -> ClSF (ReaderT r m) cl a b
readerS behaviour
  = morphS commuteReaders $ MSF.readerS $ arr swap >>> behaviour

-- | Remove ("run") a 'ReaderT' layer from the monad stack
--   by making it an explicit input to the behaviour.
runReaderS
  :: Monad m
  => ClSF (ReaderT r m) cl a b -> ClSF m cl (a, r) b
runReaderS behaviour
  = arr swap >>> (MSF.runReaderS $ morphS commuteReaders behaviour)

-- | Remove a 'ReaderT' layer by passing the readonly environment explicitly.
runReaderS_
  :: Monad m
  => ClSF (ReaderT r m) cl a b -> r -> ClSF m cl a b
runReaderS_ behaviour r = arr (, r) >>> runReaderS behaviour