content: {-# OPTIONS -Wno-orphans #-}
  {-# LANGUAGE LinearTypes #-}
  {-# LANGUAGE NoImplicitPrelude #-}
  {-# OPTIONS_HADDOCK hide #-}
  
  module Control.Functor.Linear.Internal.Reader
    ( --  ReaderT monad transformer
      Reader,
      reader,
      runReader,
      mapReader,
      withReader,
      ReaderT (..),
      runReaderT,
      mapReaderT,
      withReaderT,
      ask,
      local,
      asks,
    )
  where
  
  import Control.Functor.Linear.Internal.Class
  import Control.Functor.Linear.Internal.Instances ()
  import Control.Functor.Linear.Internal.MonadTrans
  import qualified Control.Monad as NonLinear ()
  import qualified Control.Monad.Trans.Reader as NonLinear
  import Data.Functor.Identity
  import qualified Data.Functor.Linear.Internal.Applicative as Data
  import qualified Data.Functor.Linear.Internal.Functor as Data
  import Data.Unrestricted.Linear.Internal.Consumable
  import Data.Unrestricted.Linear.Internal.Dupable
  import Prelude.Linear.Internal (runIdentity', ($), (.))
  
  -- # Linear ReaderT
  -------------------------------------------------------------------------------
  
  -- | A linear reader monad transformer.
  -- This reader monad requires that use of the read-only state is explict.
  --
  -- The monad instance requires that @r@ be 'Dupable'.  This means that you
  -- should use the linear reader monad just like the non-linear monad, except
  -- that the type system ensures that you explicity use or discard the
  -- read-only state (with the 'Consumable' instance).
  newtype ReaderT r m a = ReaderT (r %1 -> m a)
  
  -- XXX: Replace with a newtype deconstructor once it can be inferred as linear.
  
  -- | Provide an intial read-only state and run the monadic computation in
  -- a reader monad transformer
  runReaderT :: ReaderT r m a %1 -> r %1 -> m a
  runReaderT (ReaderT f) = f
  
  instance (Data.Functor m) => Data.Functor (ReaderT r m) where
    fmap f = mapReaderT (Data.fmap f)
  
  instance (Functor m) => Functor (ReaderT r m) where
    fmap f = mapReaderT (fmap f)
  
  instance (Data.Applicative m, Dupable r) => Data.Applicative (ReaderT r m) where
    pure x = ReaderT $ \r -> lseq r (Data.pure x)
    ReaderT f <*> ReaderT x = ReaderT ((\(r1, r2) -> f r1 Data.<*> x r2) . dup)
  
  instance (Applicative m, Dupable r) => Applicative (ReaderT r m) where
    pure x = ReaderT $ \r -> lseq r (pure x)
    ReaderT f <*> ReaderT x = ReaderT ((\(r1, r2) -> f r1 Data.<*> x r2) . dup)
  
  instance (Monad m, Dupable r) => Monad (ReaderT r m) where
    ReaderT x >>= f = ReaderT ((\(r1, r2) -> x r1 >>= (\a -> runReaderT (f a) r2)) . dup)
  
  type Reader r = ReaderT r Identity
  
  ask :: (Applicative m) => ReaderT r m r
  ask = ReaderT pure
  
  withReaderT :: (r' %1 -> r) %1 -> ReaderT r m a %1 -> ReaderT r' m a
  withReaderT f m = ReaderT $ runReaderT m . f
  
  local :: (r %1 -> r) %1 -> ReaderT r m a %1 -> ReaderT r m a
  local = withReaderT
  
  reader :: (Monad m) => (r %1 -> a) %1 -> ReaderT r m a
  reader f = ReaderT (return . f)
  
  runReader :: Reader r a %1 -> r %1 -> a
  runReader m = runIdentity' . runReaderT m
  
  mapReader :: (a %1 -> b) %1 -> Reader r a %1 -> Reader r b
  mapReader f = mapReaderT (Identity . f . runIdentity')
  
  mapReaderT :: (m a %1 -> n b) %1 -> ReaderT r m a %1 -> ReaderT r n b
  mapReaderT f m = ReaderT (f . runReaderT m)
  
  withReader :: (r' %1 -> r) %1 -> Reader r a %1 -> Reader r' a
  withReader = withReaderT
  
  asks :: (Monad m) => (r %1 -> a) %1 -> ReaderT r m a
  asks f = ReaderT (return . f)
  
  instance (Dupable r) => MonadTrans (ReaderT r) where
    lift x = ReaderT (`lseq` x)
  
  -- # Instances for nonlinear ReaderT
  -------------------------------------------------------------------------------
  
  instance (Functor m) => Functor (NonLinear.ReaderT r m) where
    fmap f (NonLinear.ReaderT g) = NonLinear.ReaderT $ \r -> fmap f (g r)
  
  instance (Applicative m) => Applicative (NonLinear.ReaderT r m) where
    pure x = NonLinear.ReaderT $ \_ -> pure x
    NonLinear.ReaderT f <*> NonLinear.ReaderT x = NonLinear.ReaderT $ \r -> f r <*> x r
  
  instance (Monad m) => Monad (NonLinear.ReaderT r m) where
    NonLinear.ReaderT x >>= f = NonLinear.ReaderT $ \r -> x r >>= (\a -> runReaderT' (f a) r)
  
  -- XXX: Temporary, until newtype record projections are linear.
  runReaderT' :: NonLinear.ReaderT r m a %1 -> r -> m a
  runReaderT' (NonLinear.ReaderT f) = f
  
  instance MonadTrans (NonLinear.ReaderT r) where
    lift x = NonLinear.ReaderT (\_ -> x)
  