content: -- | Defines @newtype@s that serve as combinators
  -- to compose deriving via strategies.
  
  {-# LANGUAGE DataKinds #-}
  {-# LANGUAGE GeneralizedNewtypeDeriving #-}
  {-# LANGUAGE KindSignatures #-}
  {-# LANGUAGE TypeInType #-}
  {-# LANGUAGE TypeOperators #-}
  {-# LANGUAGE UnboxedTuples #-}
  
  module Capability.Accessors
    ( Coerce(..)
    , Rename(..)
    , Field(..)
    , Pos(..)
    , Ctor(..)
    , Lift(..)
    , (:.:)(..)
    ) where
  
  import Control.Monad.IO.Class (MonadIO)
  import Control.Monad.Primitive (PrimMonad)
  import Data.Kind (Type)
  import GHC.TypeLits (Nat, Symbol)
  
  -- | Coerce the type in the context @m@ to @to@.
  --
  -- Example:
  --
  -- @
  -- newtype MyInt = MyInt Int
  -- newtype MyReader a = MyReader (Reader Int a)
  --   deriving (HasReader "a" MyInt) via
  --     Coerce MyInt (MonadReader (Reader Int))
  -- @
  --
  -- Converts the @'Capability.Reader.HasReader' \"a\" Int@ instance of
  -- @'Capability.Reader.MonadReader' (Reader Int)@ to a
  -- @'Capability.Reader.HasReader' \"a\" MyInt@
  -- instance using @Coercible Int MyInt@.
  newtype Coerce (to :: Type) m (a :: Type) = Coerce (m a)
    deriving (Functor, Applicative, Monad, MonadIO, PrimMonad)
  
  -- | Rename the tag.
  --
  -- Example:
  --
  -- @
  -- newtype MyReader a = MyReader (Reader Int a)
  --   deriving (HasReader "foo" Int) via
  --     Rename "bar" (MonadReader (Reader Int))
  -- @
  --
  -- Converts the @'Capability.Reader.HasReader' \"bar\" Int@ instance of
  -- @'Capability.Reader.MonadReader' (Reader Int)@ to a
  -- @'Capability.Reader.HasReader' \"foo\" Int@ instance by renaming the tag.
  --
  -- Note, that 'Capability.Reader.MonadReader' itself does not fix a tag,
  -- and @Rename@ is redundant in this example.
  --
  -- See 'Pos' below for a common use-case.
  newtype Rename (oldtag :: k) m (a :: Type) = Rename (m a)
    deriving (Functor, Applicative, Monad, MonadIO, PrimMonad)
  
  -- | Access the record field @field@ in the context @m@.
  --
  -- Example:
  --
  -- @
  -- data Foo = Foo { foo :: Int }
  -- newtype MyReader a = MyReader (Reader Foo a)
  --   deriving (HasReader "foo" Int) via
  --     Field "foo" () (MonadReader (Reader Foo))
  -- @
  --
  -- Converts the @'Capability.Reader.HasReader' () Foo@ instance of
  -- @'Capability.Reader.MonadReader' (Reader Foo)@ to a
  -- @'Capability.Reader.HasReader' \"foo\" Int@
  -- instance by focusing on the field @foo@ in the @Foo@ record.
  --
  -- See 'Rename' for a way to change the tag.
  newtype Field (field :: Symbol) (oldtag :: k) m (a :: Type) = Field (m a)
    deriving (Functor, Applicative, Monad, MonadIO, PrimMonad)
  
  -- | Access the value at position @pos@ in the context @m@.
  --
  -- Example:
  --
  -- @
  -- newtype MyReader a = MyReader (Reader (Int, Bool) a)
  --   deriving (HasReader 1 Int) via
  --     Pos 1 () (MonadReader (Reader (Int, Bool)))
  -- @
  --
  -- Converts the @'Capability.Reader.HasReader' () (Int, Bool)@ instance of
  -- @'Capability.Reader.MonadReader' (Reader (Int, Bool))@ to a
  -- @'Capability.Reader.HasReader' 1 Int@ instance
  -- by focusing on the first element of the tuple.
  --
  -- The implied number tag can be renamed to a more descriptive name using
  -- the 'Rename' combinator:
  --
  -- @
  -- newtype MyReader a = MyReader (Reader (Int, Bool) a)
  --   deriving (HasReader "foo" Int) via
  --     Rename 1 (Pos 1 () (MonadReader (Reader (Int, Bool))))
  -- @
  newtype Pos (pos :: Nat) (oldtag :: k) m (a :: Type) = Pos (m a)
    deriving (Functor, Applicative, Monad, MonadIO, PrimMonad)
  
  -- | Choose the given constructor in the sum-type in context @m@.
  --
  -- Example:
  --
  -- @
  -- data MyError = ErrA String | ErrB String
  -- newtype MyExcept a = MyExcept (ExceptT MyError Identity a)
  --   deriving (HasThrow \"ErrB" String) via
  --     Ctor \"ErrB" () (MonadError (ExceptT MyError Identity))
  -- @
  --
  -- Converts the @'Capability.Error.HasThrow' () \"MyError\"@ instance of
  -- @'Capability.Error.MonadError' (ExceptT MyError Identity)@ to a
  -- @'Capability.Error.HasThrow' \"ErrB\" String@
  -- instance by wrapping thrown @String@s in the @ErrB@ constructor.
  newtype Ctor (ctor :: Symbol) (oldtag :: k) m (a :: Type) = Ctor (m a)
    deriving (Functor, Applicative, Monad, MonadIO, PrimMonad)
  
  -- | Skip one level in a monad transformer stack.
  --
  -- Note, that instances generated with this strategy can incur a performance
  -- penalty.
  --
  -- Example:
  --
  -- @
  -- newtype MyStates a = MyStates (StateT Int (State Bool) a)
  --   deriving (HasState "foo" Bool) via
  --     Lift (StateT Int (MonadState (State Bool)))
  -- @
  --
  -- Uses the 'Control.Monad.Trans.Class.MonadTrans' instance of
  -- @StateT Int@ to lift
  -- the @'Capability.State.HasState' "\foo\" Bool@ instance of the underlying
  -- @'Capability.State.MonadState' (State Bool)@ over the
  -- @StateT Int@ monad transformer.
  newtype Lift m (a :: Type) = Lift (m a)
    deriving (Functor, Applicative, Monad, MonadIO, PrimMonad)
  
  
  -- | Compose two accessors.
  --
  -- This is not necessary in deriving via clauses, but in places where a
  -- transformer is expected as a type argument. E.g. 'HasError.wrapError'.
  newtype (:.:)
    (t2 :: (Type -> Type) -> Type -> Type)
    (t1 :: (Type -> Type) -> Type -> Type)
    (m :: Type -> Type)
    (a :: Type)
    = (:.:) (m a)
    deriving (Functor, Applicative, Monad, MonadIO, PrimMonad)
  infixr 9 :.:
  