THEff-0.1.1.0: src/Control/THEff.hs
{-# OPTIONS_HADDOCK show-extensions #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE RankNTypes #-}
{-|
Module : Control.THEff
Description : Main module of TH Eff package.
Copyright : (c) Kolodezny Diver, 2015
License : GPL-3
Maintainer : kolodeznydiver@gmail.com
Stability : experimental
Portability : Portable
-}
module Control.THEff (
-- * Overview
-- |
-- This package implements effects, as alternative to monad
-- transformers. Actually, the effects themselves are created without
-- the use of TH, but the binding of nested effects described by
-- mkEff splice. For example.
--
-- > {-# LANGUAGE KindSignatures #-}
-- > {-# LANGUAGE FlexibleInstances #-}
-- > {-# LANGUAGE MultiParamTypeClasses #-}
-- > {-# LANGUAGE TemplateHaskell #-}
-- > {-# LANGUAGE ScopedTypeVariables #-}
-- >
-- > import Control.THEff
-- > import Control.THEff.Reader
-- > import Control.THEff.State
-- >
-- > mkEff "MyReader" ''Reader ''Int ''Lift
-- > mkEff "SomeState" ''State ''Bool ''MyReader
-- > mkEff "OtherRdr" ''Reader ''Float ''SomeState
-- >
-- > main:: IO ()
-- > main = do
-- > r <- runMyReader 100
-- > $ runSomeState False
-- > $ runOtherRdr pi $ do
-- > i :: Int <- ask -- MyReader
-- > f :: Float <- ask -- OtherRdr
-- > b <- get -- SomeState
-- > put $ not b -- SomeState
-- > lift $ putStrLn "print from effect!" -- Lift
-- > return $ show $ fromIntegral i * f
-- > print r
--
-- For more information about extensible effects , see the original paper at
-- <http://okmij.org/ftp/Haskell/extensible/exteff.pdf>.
-- But, this package is significantly different from the original.
-- It uses a chains of ordinary GADTs created by TH.
-- No Typeable, unsafe... , ExistentialQuantification ...
--
-- Note. Further, wherever referred to __/runEEEE/__ is meant `mkEff' generated function, e.g.
-- runMyReader, runSomeState, runOtherRdr .
--
-- See more in samples/*.hs
-- * Base THEff support
mkEff
, Eff(..)
, EffClass(..)
-- * No monadic start effect
, NoEff(..)
, effNoEff
, runNoEff
-- * Monadic start effect
, Lift'(..)
, EffClassM(..)
, lift
, Lift(..)
, runLift
) where
import Control.THEff.TH.Internal(mkEff)
-- | The Monad of effects
newtype Eff w a = Eff {runEff :: (a -> w) -> w}
instance Functor (Eff w) where
fmap f (Eff g) = Eff $ \k -> g (k . f)
instance Applicative (Eff w) where
pure = return
Eff f <*> Eff g = Eff $ \k -> f (\v -> g (k . v))
instance Monad (Eff w) where
return x = Eff $ \k -> k x
m >>= f = Eff $ \k -> runEff m (\v -> runEff (f v) k)
-- | Helper class to transfer the action effects by chain.
-- Instances of this class are created in @mkEff@.
class EffClass w v e where
effAction:: ((r -> e) -> w v e) -> Eff e r
effAction f = Eff $ \k -> toEff $ f k
toEff:: w v e -> e
-- | The first effect in a chain of effects not use monads.
-- The chain of effects should start or that type, or @Lift@ (See below.)
newtype NoEff (m:: * -> *) a = NoEff { unNoEff :: a}
-- | This function is used in the 'mkEff' generated runEEEE... functions.
-- @effNoEff _ = error "THEff: Attempting to call the effect NoEff that does not have any actions!"@
effNoEff :: a -> b
effNoEff _ = error "THEff: Attempting to call the effect NoEff that does not have any actions!"
-- | This function is used in the 'mkEff' generated runEEEE... functions.
-- Do not use it alone.
runNoEff :: Eff (NoEff m a) a -> a
runNoEff m = unNoEff $ runEff m NoEff
-- | Helper data type for transfer the monadic action effects by chain.
data Lift' m v = forall a. Lift' (m a) (a -> v)
-- | Helper class to transfer the monadic action effects by chain.
-- Instances of this class are created in @mkEff@.
class EffClassM m e where
effLift:: Lift' m r -> Eff e r
effLift (Lift' m g) = Eff $ \k -> toEffM $ Lift' m (k . g)
-- |
toEffM:: Lift' m e -> e
-- | Lift a Monad to an Effect.
lift:: (Monad m, EffClassM m e) => m a -> Eff e a
lift m = effLift $ Lift' m id
-- | The first effect in a chain of monadic effects.
-- The chain of effects should start or that type, or @NoEff@.
data Lift m a = Lift_ (Lift' m (Lift m a))
| LiftResult a
instance EffClassM m (Lift m a) where
toEffM = Lift_
-- | This function is used in the @mkEff@ generated runEEEE... functions.
-- Do not use it alone.
runLift :: Monad m => Eff (Lift m a) a -> m a
runLift e = loop $ runEff e LiftResult
where
loop (Lift_ (Lift' m g)) = m >>= loop . g
loop (LiftResult r) = return r