module Polysemy
  ( -- * Core Types
    Semantic ()
  , Member

  -- * Running Semantic
  , run
  , runM

  -- * Interoperating With Other Monads
  , Lift ()
  , sendM

    -- * Lifting
  , raise

    -- * Creating New Effects
    -- | Effects should be defined as a GADT (enable @-XGADTs@), with kind @(* -> *) -> *@.
    -- Every primitive action in the effect should be its own constructor of
    -- the type. For example, we can model an effect which interacts with a tty
    -- console as follows:
    --
    -- @
    -- data Console m a where
    --   WriteLine :: String -> Console m ()
    --   ReadLine  :: Console m String
    -- @
    --
    -- Notice that the 'a' parameter gets instataniated at the /desired return
    -- type/ of the actions. Writing a line returns a '()', but reading one
    -- returns 'String'.
    --
    -- By enabling @-XTemplateHaskell@, we can use the 'makeSemantic' function
    -- to generate smart constructors for the actions. These smart constructors
    -- can be invoked directly inside of the 'Semantic' monad.
    --
    -- >>> makeSemantic ''Console
    --
    -- results in the following definitions:
    --
    -- @
    -- writeLine :: 'Member' Console r => String -> 'Semantic' r ()
    -- readLine  :: 'Member' Console r => 'Semantic' r String
    -- @
    --
    -- Effects which don't make use of the @m@ parameter are known as
    -- "first-order effects."

    -- ** Higher-Order Effects
    -- | Every effect has access to the @m@ parameter, which corresponds to the
    -- 'Semantic' monad it's used in. Using this parameter, we're capable of
    -- writing effects which themselves contain subcomputations.
    --
    -- For example, the definition of 'Polysemy.Error.Error' is
    --
    -- @
    -- data 'Polysemy.Error.Error' e m a where
    --   'Polysemy.Error.Throw' :: e -> 'Polysemy.Error.Error' e m a
    --   'Polysemy.Error.Catch' :: m a -> (e -> m a) -> 'Polysemy.Error.Error' e m a
    -- @
    --
    -- where 'Polysemy.Error.Catch' is an action that can run an exception
    -- handler if its first argument calls 'Polysemy.Error.throw'.
    --
    -- >>> makeSemantic ''Error
    --
    -- @
    -- 'Polysemy.Error.throw' :: 'Member' ('Polysemy.Error.Error' e) r => e -> 'Semantic' r a
    -- 'Polysemy.Error.catch'  :: 'Member' ('Polysemy.Error.Error' e) r => 'Semantic' r a -> (e -> 'Semantic' r a) -> 'Semantic' r a
    -- @
    --
    -- As you see, in the smart constructors, the @m@ parameter has become @'Semantic' r@.
  , makeSemantic
  , makeSemantic_

    -- * Combinators for Interpreting First-Order Effects
  , interpret
  , intercept
  , reinterpret
  , reinterpret2
  , reinterpret3

    -- * Combinators for Interpreting Higher-Order Effects
  , interpretH
  , interceptH
  , reinterpretH
  , reinterpret2H
  , reinterpret3H

    -- * Improving Performance for Interpreters
  , inlineRecursiveCalls

    -- * Composing IO-based Interpreters
  , (.@)
  , (.@@)

    -- * Tactics
    -- | Higher-order effects need to explicitly thread /other effects'/ state
    -- through themselves. Tactics are a domain-specific language for describing
    -- exactly how this threading should take place.
    --
    -- The first computation to be run should use 'runT', and subsequent
    -- computations /in the same environment/ should use 'bindT'. Any
    -- first-order constructors which appear in a higher-order context may use
    -- 'pureT' to satisfy the typechecker.
  , Tactical
  , WithTactics
  , getInitialStateT
  , pureT
  , runT
  , bindT

  -- * Reexports
  , Typeable
  ) where

import Data.Typeable
import Polysemy.Internal
import Polysemy.Internal.Combinators
import Polysemy.Internal.TH.Effect
import Polysemy.Internal.TH.Performance
import Polysemy.Internal.Tactics