polysemy-0.1.1.0: src/Polysemy.hs
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