{-# LANGUAGE CPP, TupleSections #-}
{-# OPTIONS_HADDOCK not-home #-}
module Control.Effect.Type.Optional
 ( -- * Effects
   Optional(..)

   -- * Threading utilities
 , threadRegionalViaOptional
 ) where

import Data.Functor.Const
import Control.Effect.Internal.Union
import Control.Effect.Type.Regional
import Control.Monad.Trans.Reader (ReaderT(..), mapReaderT)
import Control.Monad.Trans.Except (ExceptT(..), mapExceptT)
import qualified Control.Monad.Trans.State.Strict as SSt
import qualified Control.Monad.Trans.State.Lazy as LSt
import qualified Control.Monad.Trans.Writer.Lazy as LWr
import qualified Control.Monad.Trans.Writer.Strict as SWr
import qualified Control.Monad.Trans.Writer.CPS as CPSWr


-- | A /helper primitive effect/ for manipulating a region, with the option
-- to execute it in full or in part. @s@ is expected to be a functor.
--
-- Helper primitive effects are effects that allow you to avoid interpreting one
-- of your own effects as a primitive if the power needed from direct access to
-- the underlying monad can instead be provided by the relevant helper primitive
-- effect. The reason why you'd want to do this is that helper primitive effects
-- already have 'ThreadsEff' instances defined for them, so you don't have to
-- define any for your own effect.
--
-- The helper primitive effects offered in this library are -- in order of
-- ascending power -- 'Control.Effect.Regional.Regional',
-- 'Control.Effect.Optional.Optional', 'Control.Effect.BaseControl.BaseControl'
-- and 'Control.Effect.Unlift.Unlift'.
--
-- The typical use-case of 'Optional' is to lift a natural transformation
-- of a base monad equipped with the power to recover from an exception.
-- 'Control.Effect.Optional.HoistOption' and accompanying interpreters is
-- provided as a specialization of 'Optional' for this purpose.
--
-- 'Optional' in its most general form lacks a pre-defined interpreter:
-- when not using 'Control.Effect.Optional.HoistOption', you're expected to
-- define your own interpreter for 'Optional' (treating it as a primitive effect).
--
-- __'Optional' is typically used as a primitive effect.__
-- If you define a 'Control.Effect.Carrier' that relies on a novel
-- non-trivial monad transformer @t@, then you need to make
-- a @Functor s => 'ThreadsEff' t ('Optional' s)@ instance (if possible).
-- 'Control.Effect.Optional.threadOptionalViaBaseControl'
-- can help you with that.
--
-- The following threading constraints accept 'Optional':
--
-- * 'Control.Effect.ReaderThreads'
-- * 'Control.Effect.State.StateThreads'
-- * 'Control.Effect.State.StateLazyThreads'
-- * 'Control.Effect.Error.ErrorThreads'
-- * 'Control.Effect.Writer.WriterThreads'
-- * 'Control.Effect.Writer.WriterLazyThreads'
-- * 'Control.Effect.NonDet.NonDetThreads'
-- * 'Control.Effect.Stepped.SteppedThreads'
-- * 'Control.Effect.Cont.ContThreads'
data Optional s :: Effect where
  Optionally :: s a -> m a -> Optional s m a

-- | A valid definition of 'threadEff' for a @'ThreadsEff' ('Regional' s) t@ instance,
-- given that @t@ threads @'Optional' f@ for any functor @f@.
threadRegionalViaOptional :: ( ThreadsEff t (Optional (Const s))
                             , Monad m)
                          => (forall x. Regional s m x -> m x)
                          -> Regional s (t m) a -> t m a
threadRegionalViaOptional :: (forall x. Regional s m x -> m x) -> Regional s (t m) a -> t m a
threadRegionalViaOptional forall x. Regional s m x -> m x
alg (Regionally s
s t m a
m) =
  (forall x. Optional (Const s) m x -> m x)
-> Optional (Const s) (t m) a -> t m a
forall (t :: (* -> *) -> * -> *) (e :: (* -> *) -> * -> *)
       (m :: * -> *) a.
(ThreadsEff t e, Monad m) =>
(forall x. e m x -> m x) -> e (t m) a -> t m a
threadEff
    (\(Optionally (Const s') m') -> Regional s m x -> m x
forall x. Regional s m x -> m x
alg (s -> m x -> Regional s m x
forall s (m :: * -> *) a. s -> m a -> Regional s m a
Regionally s
s' m x
m'))
    (Const s a -> t m a -> Optional (Const s) (t m) a
forall (s :: * -> *) a (m :: * -> *). s a -> m a -> Optional s m a
Optionally (s -> Const s a
forall k a (b :: k). a -> Const a b
Const s
s) t m a
m)
{-# INLINE threadRegionalViaOptional #-}

instance Functor s => ThreadsEff (ExceptT e) (Optional s) where
  threadEff :: (forall x. Optional s m x -> m x)
-> Optional s (ExceptT e m) a -> ExceptT e m a
threadEff forall x. Optional s m x -> m x
alg (Optionally s a
sa ExceptT e m a
m) = (m (Either e a) -> m (Either e a))
-> ExceptT e m a -> ExceptT e m a
forall (m :: * -> *) e a (n :: * -> *) e' b.
(m (Either e a) -> n (Either e' b))
-> ExceptT e m a -> ExceptT e' n b
mapExceptT (Optional s m (Either e a) -> m (Either e a)
forall x. Optional s m x -> m x
alg (Optional s m (Either e a) -> m (Either e a))
-> (m (Either e a) -> Optional s m (Either e a))
-> m (Either e a)
-> m (Either e a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. s (Either e a) -> m (Either e a) -> Optional s m (Either e a)
forall (s :: * -> *) a (m :: * -> *). s a -> m a -> Optional s m a
Optionally ((a -> Either e a) -> s a -> s (Either e a)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap a -> Either e a
forall a b. b -> Either a b
Right s a
sa)) ExceptT e m a
m
  {-# INLINE threadEff #-}

instance ThreadsEff (ReaderT i) (Optional s) where
  threadEff :: (forall x. Optional s m x -> m x)
-> Optional s (ReaderT i m) a -> ReaderT i m a
threadEff forall x. Optional s m x -> m x
alg (Optionally s a
sa ReaderT i m a
m) = (m a -> m a) -> ReaderT i m a -> ReaderT i m a
forall (m :: * -> *) a (n :: * -> *) b r.
(m a -> n b) -> ReaderT r m a -> ReaderT r n b
mapReaderT (Optional s m a -> m a
forall x. Optional s m x -> m x
alg (Optional s m a -> m a) -> (m a -> Optional s m a) -> m a -> m a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. s a -> m a -> Optional s m a
forall (s :: * -> *) a (m :: * -> *). s a -> m a -> Optional s m a
Optionally s a
sa) ReaderT i m a
m
  {-# INLINE threadEff #-}

instance Functor s => ThreadsEff (SSt.StateT s') (Optional s) where
  threadEff :: (forall x. Optional s m x -> m x)
-> Optional s (StateT s' m) a -> StateT s' m a
threadEff forall x. Optional s m x -> m x
alg (Optionally s a
sa StateT s' m a
m) = (s' -> m (a, s')) -> StateT s' m a
forall s (m :: * -> *) a. (s -> m (a, s)) -> StateT s m a
SSt.StateT ((s' -> m (a, s')) -> StateT s' m a)
-> (s' -> m (a, s')) -> StateT s' m a
forall a b. (a -> b) -> a -> b
$ \s'
s ->
    Optional s m (a, s') -> m (a, s')
forall x. Optional s m x -> m x
alg (Optional s m (a, s') -> m (a, s'))
-> Optional s m (a, s') -> m (a, s')
forall a b. (a -> b) -> a -> b
$ s (a, s') -> m (a, s') -> Optional s m (a, s')
forall (s :: * -> *) a (m :: * -> *). s a -> m a -> Optional s m a
Optionally ((a -> (a, s')) -> s a -> s (a, s')
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (, s'
s) s a
sa) (StateT s' m a -> s' -> m (a, s')
forall s (m :: * -> *) a. StateT s m a -> s -> m (a, s)
SSt.runStateT StateT s' m a
m s'
s)
  {-# INLINE threadEff #-}

instance Functor s => ThreadsEff (LSt.StateT s') (Optional s) where
  threadEff :: (forall x. Optional s m x -> m x)
-> Optional s (StateT s' m) a -> StateT s' m a
threadEff forall x. Optional s m x -> m x
alg (Optionally s a
sa StateT s' m a
m) = (s' -> m (a, s')) -> StateT s' m a
forall s (m :: * -> *) a. (s -> m (a, s)) -> StateT s m a
LSt.StateT ((s' -> m (a, s')) -> StateT s' m a)
-> (s' -> m (a, s')) -> StateT s' m a
forall a b. (a -> b) -> a -> b
$ \s'
s ->
    Optional s m (a, s') -> m (a, s')
forall x. Optional s m x -> m x
alg (Optional s m (a, s') -> m (a, s'))
-> Optional s m (a, s') -> m (a, s')
forall a b. (a -> b) -> a -> b
$ s (a, s') -> m (a, s') -> Optional s m (a, s')
forall (s :: * -> *) a (m :: * -> *). s a -> m a -> Optional s m a
Optionally ((a -> (a, s')) -> s a -> s (a, s')
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (, s'
s) s a
sa) (StateT s' m a -> s' -> m (a, s')
forall s (m :: * -> *) a. StateT s m a -> s -> m (a, s)
LSt.runStateT StateT s' m a
m s'
s)
  {-# INLINE threadEff #-}

instance (Functor s, Monoid w) => ThreadsEff (LWr.WriterT w) (Optional s) where
  threadEff :: (forall x. Optional s m x -> m x)
-> Optional s (WriterT w m) a -> WriterT w m a
threadEff forall x. Optional s m x -> m x
alg (Optionally s a
sa WriterT w m a
m) =
    (m (a, w) -> m (a, w)) -> WriterT w m a -> WriterT w m a
forall (m :: * -> *) a w (n :: * -> *) b w'.
(m (a, w) -> n (b, w')) -> WriterT w m a -> WriterT w' n b
LWr.mapWriterT (Optional s m (a, w) -> m (a, w)
forall x. Optional s m x -> m x
alg (Optional s m (a, w) -> m (a, w))
-> (m (a, w) -> Optional s m (a, w)) -> m (a, w) -> m (a, w)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. s (a, w) -> m (a, w) -> Optional s m (a, w)
forall (s :: * -> *) a (m :: * -> *). s a -> m a -> Optional s m a
Optionally ((a -> (a, w)) -> s a -> s (a, w)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (, w
forall a. Monoid a => a
mempty) s a
sa)) WriterT w m a
m
  {-# INLINE threadEff #-}

instance (Functor s, Monoid w) => ThreadsEff (SWr.WriterT w) (Optional s) where
  threadEff :: (forall x. Optional s m x -> m x)
-> Optional s (WriterT w m) a -> WriterT w m a
threadEff forall x. Optional s m x -> m x
alg (Optionally s a
sa WriterT w m a
m) =
    (m (a, w) -> m (a, w)) -> WriterT w m a -> WriterT w m a
forall (m :: * -> *) a w (n :: * -> *) b w'.
(m (a, w) -> n (b, w')) -> WriterT w m a -> WriterT w' n b
SWr.mapWriterT (Optional s m (a, w) -> m (a, w)
forall x. Optional s m x -> m x
alg (Optional s m (a, w) -> m (a, w))
-> (m (a, w) -> Optional s m (a, w)) -> m (a, w) -> m (a, w)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. s (a, w) -> m (a, w) -> Optional s m (a, w)
forall (s :: * -> *) a (m :: * -> *). s a -> m a -> Optional s m a
Optionally ((a -> (a, w)) -> s a -> s (a, w)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (, w
forall a. Monoid a => a
mempty) s a
sa)) WriterT w m a
m
  {-# INLINE threadEff #-}

instance (Functor s, Monoid w)
      => ThreadsEff (CPSWr.WriterT w) (Optional s) where
  threadEff :: (forall x. Optional s m x -> m x)
-> Optional s (WriterT w m) a -> WriterT w m a
threadEff forall x. Optional s m x -> m x
alg (Optionally s a
sa WriterT w m a
m) =
    (m (a, w) -> m (a, w)) -> WriterT w m a -> WriterT w m a
forall (n :: * -> *) w w' (m :: * -> *) a b.
(Monad n, Monoid w, Monoid w') =>
(m (a, w) -> n (b, w')) -> WriterT w m a -> WriterT w' n b
CPSWr.mapWriterT (Optional s m (a, w) -> m (a, w)
forall x. Optional s m x -> m x
alg (Optional s m (a, w) -> m (a, w))
-> (m (a, w) -> Optional s m (a, w)) -> m (a, w) -> m (a, w)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. s (a, w) -> m (a, w) -> Optional s m (a, w)
forall (s :: * -> *) a (m :: * -> *). s a -> m a -> Optional s m a
Optionally ((a -> (a, w)) -> s a -> s (a, w)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (, w
forall a. Monoid a => a
mempty) s a
sa)) WriterT w m a
m
  {-# INLINE threadEff #-}