{-# LANGUAGE AllowAmbiguousTypes #-}

{-|
Module:       Control.Monad.Freer
Description:  Freer - an extensible effects library
Copyright:    (c) 2016 Allele Dev; 2017 Ixperta Solutions s.r.o.; 2017 Alexis King
License:      BSD3
Maintainer:   Alexis King <lexi.lambda@gmail.com>
Stability:    experimental
Portability:  GHC specific language extensions.

This library is an implementation of an /extensible effect system/ for Haskell,
a general-purpose way of tracking effects at the type level and handling them in
different ways. The concept of an “effect” is very general: it encompasses the
things most people consider side-effects, like generating random values,
interacting with the file system, and mutating state, but it also includes
things like access to an immutable global environment and exception handling.

Traditional Haskell tracks and composes effects using /monad transformers/,
which involves modeling each effects using what is conceptually a separate
monad. In contrast, @freer-simple@ provides exactly __one__ monad, 'Eff',
parameterized by a type-level list of effects. For example, a computation that
produces an 'Integer' by consuming a 'String' from the global environment and
acting upon a single mutable cell containing a 'Bool' would have the following
type:

@
'Eff' '['Control.Monad.Freer.Reader.Reader' 'String', 'Control.Monad.Freer.State.State' 'Bool'] 'Integer'
@

For comparison, this is the equivalent stack of monad transformers:

@
ReaderT 'String' (State 'Bool') 'Integer'
@

However, this is slightly misleading: the example with 'Eff' is actually
/more general/ than the corresponding example using transformers because the
implementations of effects are not /concrete/. While @StateT@ specifies a
/specific/ implementation of a pseudo-mutable cell,
'Control.Monad.Freer.State.State' is merely an interface with a set of available
operations. Using 'Control.Monad.Freer.State.runState' will “run” the
'Control.Monad.Freer.State.State' effect much the same way that @StateT@ does,
but a hypothetical handler function @runStateTVar@ could implement the state in
terms of a STM 'Control.Concurrent.STM.TVar'.

The @freer-simple@ effect library is divided into three parts:

  1. First, @freer-simple@ provides the 'Eff' monad, an implementation of
     extensible effects that allows effects to be tracked at the type level and
     interleaved at runtime.

  2. Second, it provides a built-in library of common effects, such as
     'Control.Monad.Freer.Reader.Reader', 'Control.Monad.Freer.Writer.Writer',
     'Control.Monad.Freer.State.State', and 'Control.Monad.Freer.Error.Error'.
     These effects can be used with 'Eff' out of the box with an interface that
     is similar to the equivalent monad transformers.

  3. Third, it provides a set of combinators for implementing your /own/
     effects, which can either be implemented entirely independently, in terms
     of other existing effects, or even in terms of existing monads, making it
     possible to use @freer-simple@ with existing monad transformer stacks.

One of the core ideas of @freer-simple@ is that /most/ effects that occur in
practical applications are really different incarnations of a small set of
fundamental effect types. Therefore, while it’s possible to write new effect
handlers entirely from scratch, it’s more common that you will wish to define
new effects in terms of other effects. @freer-simple@ makes this possible by
providing the 'reinterpret' function, which allows /translating/ an effect into
another one.

For example, imagine an effect that represents interactions with a file system:

@
data FileSystem r where
  ReadFile :: 'FilePath' -> FileSystem 'String'
  WriteFile :: 'FilePath' -> 'String' -> FileSystem ()
@

An implementation that uses the real file system would, of course, be
implemented in terms of 'IO'. An alternate implementation, however, might be
implemented in-memory in terms of 'Control.Monad.Freer.State.State'. With
'reinterpret', this implementation is trivial:

@
runInMemoryFileSystem :: [('FilePath', 'String')] -> 'Eff' (FileSystem ': effs) '~>' 'Eff' effs
runInMemoryFileSystem initVfs = 'Control.Monad.Freer.State.evalState' initVfs '.' fsToState where
  fsToState :: 'Eff' (FileSystem ': effs) '~>' 'Eff' ('Control.Monad.Freer.State.State' [('FilePath', 'String')] ': effs)
  fsToState = 'reinterpret' '$' \case
    ReadFile path -> 'Control.Monad.Freer.State.get' '>>=' \\vfs -> case 'lookup' path vfs of
      'Just' contents -> 'pure' contents
      'Nothing' -> 'error' ("readFile: no such file " ++ path)
    WriteFile path contents -> 'Control.Monad.Freer.State.modify' $ \\vfs ->
      (path, contents) : 'Data.List.deleteBy' (('==') ``Data.Function.on`` 'fst') (path, contents) vfs
@

This handler is easy to write, doesn’t require any knowledge of how
'Control.Monad.Freer.State.State' is implemented, is entirely encapsulated, and
is composable with all other effect handlers. This idea—making it easy to define
new effects in terms of existing ones—is the concept around which @freer-simple@
is based.

= Effect Algebras

In @freer-simple@, effects are defined using /effect algebras/, which are
representations of an effect’s operations as a generalized algebraic datatype,
aka GADT. This might sound intimidating, but you really don’t need to know very
much at all about how GADTs work to use @freer-simple@; instead, you can just
learn the syntax entirely in terms of what it means for defining effects.

Consider the definition of the @FileSystem@ effect from the above example:

@
data FileSystem r where
  ReadFile :: 'FilePath' -> FileSystem 'String'
  WriteFile :: 'FilePath' -> 'String' -> FileSystem ()
@

The first line, @data FileSystem r where@, defines a new effect. All effects
have at least one parameter, normally named @r@, which represents the /result/
or /return type/ of the operation. For example, take a look at the type of
@ReadFile@:

@
ReadFile :: 'FilePath' -> FileSystem 'String'
@

This is very similar to the type of 'readFile' from the standard "Prelude",
which has type @'FilePath' -> 'IO' 'String'@. The only difference is that the
name of the effect, in this case @FileSystem@, replaces the use of the monad,
in this case 'IO'.

Also notice that @ReadFile@ and @WriteFile@ begin with capital letters. This is
because they are actually /data constructors/. This means that
@ReadFile "foo.txt"@ actually constructs a /value/ of type
@FileSystem 'String'@, and this is useful, since it allows effect handlers like
@runInMemoryFileSystem@ to pattern-match on the effect’s constructors and get
the values out.

To actually /use/ our @FileSystem@ effect, however, we have to write just a
little bit of glue to connect our effect definition to the 'Eff' monad, which we
do using the 'send' function. We can write an ordinary function for each of the
@FileSystem@ constructors that mechanically calls 'send':

@
readFile :: 'Member' FileSystem effs => 'FilePath' -> 'Eff' effs 'String'
readFile path = 'send' (ReadFile path)

writeFile :: 'Member' FileSystem effs => 'FilePath' -> 'String' -> 'Eff' effs ()
writeFile path contents = 'send' (WriteFile path contents)
@

Notice the use of the 'Member' constraint on these functions. This constraint
means that the 'FileSystem' effect can be anywhere within the type-level list
represented by the @effs@ variable. If the signature of 'readFile' were more
concrete, like this:

@
readFile :: 'FilePath' -> 'Eff' '[FileSystem] 'String'
@

…then 'readFile' would /only/ be usable with an 'Eff' computation that /only/
performed @FileSystem@ effects, which isn’t especially useful.

Since writing these functions is entirely mechanical, they can be generated
automatically using Template Haskell; see "Control.Monad.Freer.TH" for more
details.
-}
module Control.Monad.Freer
  ( -- * Effect Monad
    Eff

    -- ** Effect Constraints
    -- | As mentioned in the documentation for 'Eff', it’s rare to actually
    -- specify a concrete list of effects for an 'Eff' computation, since that
    -- has two significant downsides:
    --
    --   1. It couples the computation to that /specific/ list of effects, so it
    --      cannot be used in functions that perform a strict superset of
    --      effects.
    --
    --   2. It forces the effects to be handled in a particular order, which
    --      can make handler code brittle when the list of effects is changed.
    --
    -- Fortunately, these restrictions are easily avoided by using
    -- /effect constraints/, such as 'Member' or 'Members', which decouple a
    -- computation from a particular concrete list of effects.
  , Member
  , Members
  , LastMember

    -- ** Sending Arbitrary Effects
  , send
  , sendM

    -- ** Lifting Effect Stacks
  , raise

    -- * Handling Effects
    -- | Once an effectful computation has been produced, it needs to somehow be
    -- executed. This is where /effect handlers/ come in. Each effect can have
    -- an arbitrary number of different effect handlers, which can be used to
    -- interpret the same effects in different ways. For example, it is often
    -- useful to have two effect handlers: one that uses 'sendM' and
    -- 'interpretM' to interpret the effect in 'IO', and another that uses
    -- 'interpret', 'reinterpret', or 'translate' to interpret the effect in an
    -- entirely pure way for the purposes of testing.
    --
    -- This module doesn’t provide any effects or effect handlers (those are in
    -- their own modules, like "Control.Monad.Freer.Reader" and
    -- "Control.Monad.Freer.Error"), but it /does/ provide a set of combinators
    -- for constructing new effect handlers. It also provides the 'run' and
    -- 'runM' functions for extracting the actual result of an effectful
    -- computation once all effects have been handled.

    -- ** Running the Eff monad
  , run
  , runM

    -- ** Building Effect Handlers
    -- *** Basic effect handlers
  , interpret
  , interpose
  , subsume
    -- *** Derived effect handlers
  , reinterpret
  , reinterpret2
  , reinterpret3
  , reinterpretN
  , translate
    -- *** Monadic effect handlers
  , interpretM
    -- *** Advanced effect handlers
  , interpretWith
  , interposeWith

    -- * Re-exported bindings
  , type (~>)
  ) where

import Control.Natural (type (~>))

import qualified Control.Monad.Freer.Internal as Internal

import Control.Monad.Freer.Internal
  ( Eff
  , LastMember
  , Member
  , Members
  , Weakens
  , (:++:)
  , handleRelay
  , raise
  , replaceRelay
  , replaceRelayN
  , run
  , runM
  , send
  , sendM
  )

-- | The simplest way to produce an effect handler. Given a natural
-- transformation from some effect @eff@ to some effectful computation with
-- effects @effs@, produces a natural transformation from @'Eff' (eff ': effs)@
-- to @'Eff' effs@.
interpret :: forall eff effs. (eff ~> Eff effs) -> Eff (eff ': effs) ~> Eff effs
interpret f = interpretWith (\e -> (f e >>=))
{-# INLINE interpret #-}

-- | Like 'interpret', but instead of handling the effect, allows responding to
-- the effect while leaving it unhandled.
interpose :: forall eff effs. Member eff effs => (eff ~> Eff effs) -> Eff effs ~> Eff effs
interpose f = interposeWith (\e -> (f e >>=))
{-# INLINE interpose #-}

-- | Interprets an effect in terms of another identical effect. This can be used
-- to eliminate duplicate effects.
subsume :: forall eff effs. Member eff effs => Eff (eff ': effs) ~> Eff effs
subsume = interpret send
{-# INLINE subsume #-}

-- | Like 'interpret', but instead of removing the interpreted effect @f@,
-- reencodes it in some new effect @g@.
reinterpret :: forall f g effs. (f ~> Eff (g ': effs)) -> Eff (f ': effs) ~> Eff (g ': effs)
reinterpret f = replaceRelay pure (\e -> (f e >>=))
{-# INLINE reinterpret #-}

-- | Like 'reinterpret', but encodes the @f@ effect in /two/ new effects instead
-- of just one.
reinterpret2
  :: forall f g h effs
   . (f ~> Eff (g ': h ': effs)) -> Eff (f ': effs) ~> Eff (g ': h ': effs)
reinterpret2 = reinterpretN @[g, h]
{-# INLINE reinterpret2 #-}

-- | Like 'reinterpret', but encodes the @f@ effect in /three/ new effects
-- instead of just one.
reinterpret3
  :: forall f g h i effs
   . (f ~> Eff (g ': h ': i ': effs))
  -> Eff (f ': effs) ~> Eff (g ': h ': i ': effs)
reinterpret3 = reinterpretN @[g, h, i]
{-# INLINE reinterpret3 #-}

-- | Like 'interpret', 'reinterpret', 'reinterpret2', and 'reinterpret3', but
-- allows the result to have any number of additional effects instead of simply
-- 0-3. The problem is that this completely breaks type inference, so you will
-- have to explicitly pick @gs@ using @TypeApplications@. Prefer 'interpret',
-- 'reinterpret', 'reinterpret2', or 'reinterpret3' where possible.
reinterpretN
  :: forall gs f effs. Weakens gs
  => (f ~> Eff (gs :++: effs)) -> Eff (f ': effs) ~> Eff (gs :++: effs)
reinterpretN f = replaceRelayN @gs pure (\e -> (f e >>=))
{-# INLINE reinterpretN #-}

-- | Runs an effect by translating it into another effect. This is effectively a
-- more restricted form of 'reinterpret', since both produce a natural
-- transformation from @'Eff' (f ': effs)@ to @'Eff' (g ': effs)@ for some
-- effects @f@ and @g@, but 'translate' does not permit using any of the other
-- effects in the implementation of the interpreter.
--
-- In practice, this difference in functionality is not particularly useful, and
-- 'reinterpret' easily subsumes all of the functionality of 'translate', but
-- the way 'translate' restricts the result leads to much better type inference.
--
-- @
-- 'translate' f = 'reinterpret' ('send' . f)
-- @
translate :: forall f g effs. (f ~> g) -> Eff (f ': effs) ~> Eff (g ': effs)
translate f = reinterpret (send . f)
{-# INLINE translate #-}

-- | Like 'interpret', this function runs an effect without introducing another
-- one. Like 'translate', this function runs an effect by translating it into
-- another effect in isolation, without access to the other effects in @effs@.
-- Unlike either of those functions, however, this runs the effect in a final
-- monad in @effs@, intended to be run with 'runM'.
--
-- @
-- 'interpretM' f = 'interpret' ('sendM' . f)
-- @
interpretM
  :: forall eff m effs
   . (Monad m, LastMember m effs)
  => (eff ~> m) -> Eff (eff ': effs) ~> Eff effs
interpretM f = interpret (sendM . f)
{-# INLINE interpretM #-}

-- | A highly general way of handling an effect. Like 'interpret', but
-- explicitly passes the /continuation/, a function of type @v -> 'Eff' effs b@,
-- to the handler function. Most handlers invoke this continuation to resume the
-- computation with a particular value as the result, but some handlers may
-- return a value without resumption, effectively aborting the computation to
-- the point where the handler is invoked. This is useful for implementing
-- things like 'Control.Monad.Freer.Error.catchError', for example.
--
-- @
-- 'interpret' f = 'interpretWith' (\e -> (f e '>>='))
-- @
interpretWith
  :: forall eff effs b
   . (forall v. eff v -> (v -> Eff effs b) -> Eff effs b)
  -> Eff (eff ': effs) b
  -> Eff effs b
interpretWith = handleRelay pure
{-# INLINE interpretWith #-}

-- | Combines the interposition behavior of 'interpose' with the
-- continuation-passing capabilities of 'interpretWith'.
--
-- @
-- 'interpose' f = 'interposeWith' (\e -> (f e '>>='))
-- @
interposeWith
  :: forall eff effs b
   . Member eff effs
  => (forall v. eff v -> (v -> Eff effs b) -> Eff effs b)
  -> Eff effs b
  -> Eff effs b
interposeWith = Internal.interpose pure
{-# INLINE interposeWith #-}