extensible-effects 1.1.0 → 1.2.0
raw patch · 12 files changed
+463/−145 lines, 12 filesdep +HUnitdep +test-framework-hunit
Dependencies added: HUnit, test-framework-hunit
Files
- extensible-effects.cabal +11/−2
- src/Control/Eff.hs +1/−0
- src/Control/Eff/Lift.hs +5/−1
- src/Control/Eff/Reader/Lazy.hs +50/−0
- src/Control/Eff/Reader/Strict.hs +50/−0
- src/Control/Eff/State.hs +0/−123
- src/Control/Eff/State/Lazy.hs +44/−0
- src/Control/Eff/State/Strict.hs +57/−0
- src/Control/Eff/Writer/Lazy.hs +58/−0
- src/Control/Eff/Writer/Strict.hs +59/−0
- src/Data/OpenUnion1.hs +22/−4
- test/Test.hs +106/−15
extensible-effects.cabal view
@@ -1,11 +1,13 @@ Name: extensible-effects-Version: 1.1.0+Version: 1.2.0 Synopsis: An Alternative to Monad Transformers Description: This package introduces datatypes for typeclass-constrained effects, as an alternative to monad-transformer based (datatype-constrained) approach of multi-layered effects. For more information, see the original paper at <http://okmij.org/ftp/Haskell/extensible/exteff.pdf>.++ Any help is appreciated! Category: Control Author: Oleg Kiselyov, Amr Sabry, Cameron Swords, Ben Foppa Stability: Experimental@@ -26,7 +28,12 @@ Control.Eff.Exception Control.Eff.Fresh Control.Eff.Lift- Control.Eff.State+ Control.Eff.Reader.Lazy+ Control.Eff.Reader.Strict+ Control.Eff.State.Lazy+ Control.Eff.State.Strict+ Control.Eff.Writer.Lazy+ Control.Eff.Writer.Strict Control.Eff.Trace other-modules: Data.OpenUnion1 @@ -43,7 +50,9 @@ build-depends: base == 4.*, QuickCheck == 2.*,+ HUnit == 1.2.*, test-framework == 0.8.*,+ test-framework-hunit == 0.3.*, test-framework-quickcheck2 == 0.3.*, extensible-effects
src/Control/Eff.hs view
@@ -65,6 +65,7 @@ Eff , VE (..) , Member+ , SetMember , Union , (:>) , inj
src/Control/Eff/Lift.hs view
@@ -1,5 +1,7 @@ {-# LANGUAGE TypeOperators #-}+{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE UndecidableInstances #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE ExistentialQuantification #-}@@ -24,8 +26,10 @@ instance Functor (Lift m) where fmap f (Lift m k) = Lift m (f . k) +instance SetMember Lift (Lift m) (Lift m :> ())+ -- | Lift a Monad to an Effect.-lift :: (Typeable1 m, Member (Lift m) r) => m a -> Eff r a+lift :: (Typeable1 m, Member (Lift m) r, SetMember Lift (Lift m) r) => m a -> Eff r a lift m = send (inj . Lift m) -- | The handler of Lift requests. It is meant to be terminal:
+ src/Control/Eff/Reader/Lazy.hs view
@@ -0,0 +1,50 @@+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE DeriveFunctor #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE FlexibleContexts #-}+-- | Lazy read-only state+module Control.Eff.Reader.Lazy( Reader+ , ask+ , local+ , reader+ , runReader+ ) where++import Control.Applicative ((<$>))+import Data.Typeable++import Control.Eff++-- | The request for a value of type e from the current environment.+-- This environment is analogous to a parameter of type e.+newtype Reader e v = Reader (e -> v)+ deriving (Typeable, Functor)++-- | Get the current value from a Reader.+ask :: (Typeable e, Member (Reader e) r) => Eff r e+ask = send (inj . Reader)++-- | Locally rebind the value in the dynamic environment.+-- This function both requests and admins Reader requests.+local :: (Typeable e, Member (Reader e) r)+ => (e -> e)+ -> Eff r a+ -> Eff r a+local f m = do+ e <- f <$> ask+ let loop (Val x) = return x+ loop (E u) = interpose u loop (\(Reader k) -> loop (k e))+ loop (admin m)++-- | Request the environment value using a transformation function.+reader :: (Typeable e, Member (Reader e) r) => (e -> a) -> Eff r a+reader f = f <$> ask++-- | The handler of Reader requests. The return type shows that+-- all Reader requests are fully handled.+runReader :: Typeable e => Eff (Reader e :> r) w -> e -> Eff r w+runReader m e = loop (admin m)+ where+ loop (Val x) = return x+ loop (E u) = handleRelay u loop (\(Reader k) -> loop (k e))
+ src/Control/Eff/Reader/Strict.hs view
@@ -0,0 +1,50 @@+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE DeriveFunctor #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE FlexibleContexts #-}+-- | Strict read-only state+module Control.Eff.Reader.Strict( Reader+ , ask+ , local+ , reader+ , runReader+ ) where++import Control.Applicative ((<$>))+import Data.Typeable++import Control.Eff++-- | The request for a value of type e from the current environment.+-- This environment is analogous to a parameter of type e.+newtype Reader e v = Reader (e -> v)+ deriving (Typeable, Functor)++-- | Get the current value from a Reader.+ask :: (Typeable e, Member (Reader e) r) => Eff r e+ask = send (inj . Reader)++-- | Locally rebind the value in the dynamic environment.+-- This function both requests and admins Reader requests.+local :: (Typeable e, Member (Reader e) r)+ => (e -> e)+ -> Eff r a+ -> Eff r a+local f m = do+ e <- f <$> ask+ let loop (Val x) = return x+ loop (E u) = interpose u loop (\(Reader k) -> loop (k e))+ loop (admin m)++-- | Request the environment value using a transformation function.+reader :: (Typeable e, Member (Reader e) r) => (e -> a) -> Eff r a+reader f = f <$> ask++-- | The handler of Reader requests. The return type shows that+-- all Reader requests are fully handled.+runReader :: Typeable e => Eff (Reader e :> r) w -> e -> Eff r w+runReader m !e = loop (admin m) where+ loop (Val x) = return x+ loop (E u) = handleRelay u loop (\(Reader k) -> loop (k e))
− src/Control/Eff/State.hs
@@ -1,123 +0,0 @@-{-# LANGUAGE TypeOperators #-}-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveFunctor #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE MultiParamTypeClasses #-}--- | Strict state effect------ Example: implementing `Control.Eff.Fresh`------ > runFresh' :: (Typeable i, Enum i, Num i) => Eff (Fresh i :> r) w -> i -> Eff r w--- > runFresh' m s = fst <$> runState s (loop $ admin m)--- > where--- > loop (Val x) = return x--- > loop (E u) = case decomp u of--- > Right (Fresh k) -> do--- > n <- getState--- > putState (n + 1)--- > loop (k n)--- > Left u' -> send (\k -> unsafeReUnion $ k <$> u') >>= loop-module Control.Eff.State(- -- * Read-write State- State- , getState- , putState- , onState- , runState- -- * Reader- , Reader- , getReader- , runReader- , local- -- * Writer- , Writer- , putWriter- , runWriter- , runPusher- ) where--import Control.Applicative ((<$>), (<|>))-import Data.Typeable--import Control.Eff---- | Strict state effect-data State s w = State (s -> s) (s -> w)- deriving (Typeable, Functor)---- | Write a new value of the state.-putState :: Typeable e => Member (State e) r => e -> Eff r ()-putState = onState . const---- | Return the current value of the state.-getState :: Typeable e => Member (State e) r => Eff r e-getState = send (inj . State id)---- | Transform the state with a function.-onState :: (Typeable s, Member (State s) r) => (s -> s) -> Eff r ()-onState f = send (\k -> inj (State f (\_ -> k ())))---- | Run a State effect.-runState :: Typeable s- => s -- ^ Initial state- -> Eff (State s :> r) w -- ^ Effect incorporating State- -> Eff r (s, w) -- ^ Effect containing final state and a return value-runState s0 = loop s0 . admin where- loop s (Val x) = return (s, x)- loop s (E u) = handleRelay u (loop s) $- \(State t k) -> let s' = t s in s' `seq` loop s' (k s')---- --------------------------------------------------------------------------- The Reader monad---- | The request for a value of type e from the current environment.--- This environment is analogous to a parameter of type e.-newtype Reader e v = Reader (e -> v)- deriving (Typeable, Functor)---- | Get the current value from a Reader.-getReader :: (Typeable e, Member (Reader e) r) => Eff r e-getReader = send (inj . Reader)---- | The handler of Reader requests. The return type shows that--- all Reader requests are fully handled.-runReader :: Typeable e => Eff (Reader e :> r) w -> e -> Eff r w-runReader m e = loop (admin m) where- loop (Val x) = return x- loop (E u) = handleRelay u loop (\(Reader k) -> loop (k e))---- | Locally rebind the value in the dynamic environment.--- This function both requests and admins Reader requests.-local :: (Typeable e, Member (Reader e) r) =>- (e -> e) -> Eff r a -> Eff r a-local f m = do- e <- f <$> getReader- let loop (Val x) = return x- loop (E u) = interpose u loop (\(Reader k) -> loop (k e))- loop (admin m)---- --------------------------------------------------------------------------- | The request to remember a value of type e in the current environment-data Writer e v = Writer e v- deriving (Typeable, Functor)--putWriter :: (Typeable e, Member (Writer e) r) => e -> Eff r ()-putWriter e = send $ \f -> inj $ Writer e $ f ()---- | Handle Writer requests by overwriting previous values.--- If no value of type @e@ was returned, Nothing is returned;--- otherwise return Just the most recent value written.-runWriter :: Typeable e => Eff (Writer e :> r) w -> Eff r (Maybe e, w)-runWriter = loop . admin- where- correctVal f = fmap $ \(x, y) -> (f x, y)-- loop (Val x) = return (Nothing, x)- loop (E u) = handleRelay u loop (\(Writer e v) -> correctVal (<|> Just e) $ loop v)---- | Handle Writer requests by stacking written values on to a list.-runPusher :: Typeable e => Eff (Writer e :> r) w -> Eff r ([e], w)-runPusher = loop . admin- where- loop (Val x) = return ([], x)- loop (E u) = handleRelay u loop (\(Writer e v) -> (\(es, v') -> (e:es, v')) <$> loop v)
+ src/Control/Eff/State/Lazy.hs view
@@ -0,0 +1,44 @@+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE DeriveFunctor #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE FlexibleContexts #-}+-- | Lazy state effect+module Control.Eff.State.Lazy( State+ , get+ , put+ , modify+ , runState+ ) where++import Data.Typeable++import Control.Eff++-- | Strict state effect+data State s w = State (s -> s) (s -> w)+ deriving (Typeable, Functor)++-- | Write a new value of the state.+put :: (Typeable e, Member (State e) r) => e -> Eff r ()+put = modify . const++-- | Return the current value of the state.+get :: (Typeable e, Member (State e) r) => Eff r e+get = send (inj . State id)++-- | Transform the state with a function.+modify :: (Typeable s, Member (State s) r) => (s -> s) -> Eff r ()+modify f = send $ \k -> inj $ State f $ \_ -> k ()++-- | Run a State effect.+runState :: Typeable s+ => s -- ^ Initial state+ -> Eff (State s :> r) w -- ^ Effect incorporating State+ -> Eff r (s, w) -- ^ Effect containing final state and a return value+runState s0 = loop s0 . admin where+ loop s (Val x) = return (s, x)+ loop s (E u) = handleRelay u (loop s) $+ \(State t k) -> let s' = t s+ in loop s' (k s')
+ src/Control/Eff/State/Strict.hs view
@@ -0,0 +1,57 @@+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE DeriveFunctor #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE FlexibleContexts #-}+-- | Strict state effect+--+-- Example: implementing `Control.Eff.Fresh`+--+-- > runFresh' :: (Typeable i, Enum i, Num i) => Eff (Fresh i :> r) w -> i -> Eff r w+-- > runFresh' m s = fst <$> runState s (loop $ admin m)+-- > where+-- > loop (Val x) = return x+-- > loop (E u) = case decomp u of+-- > Right (Fresh k) -> do+-- > n <- get+-- > put (n + 1)+-- > loop (k n)+-- > Left u' -> send (\k -> unsafeReUnion $ k <$> u') >>= loop+module Control.Eff.State.Strict( State+ , get+ , put+ , modify+ , runState+ ) where++import Data.Typeable++import Control.Eff++-- | Strict state effect+data State s w = State (s -> s) (s -> w)+ deriving (Typeable, Functor)++-- | Write a new value of the state.+put :: (Typeable e, Member (State e) r) => e -> Eff r ()+put !s = modify $ const s++-- | Return the current value of the state.+get :: (Typeable e, Member (State e) r) => Eff r e+get = send (inj . State id)++-- | Transform the state with a function.+modify :: (Typeable s, Member (State s) r) => (s -> s) -> Eff r ()+modify f = send $ \k -> inj $ State f $ \_ -> k ()++-- | Run a State effect.+runState :: Typeable s+ => s -- ^ Initial state+ -> Eff (State s :> r) w -- ^ Effect incorporating State+ -> Eff r (s, w) -- ^ Effect containing final state and a return value+runState s0 = loop s0 . admin where+ loop !s (Val x) = return (s, x)+ loop !s (E u) = handleRelay u (loop s) $+ \(State t k) -> let s' = t s+ in loop s' (k s')
+ src/Control/Eff/Writer/Lazy.hs view
@@ -0,0 +1,58 @@+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE DeriveFunctor #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE FlexibleContexts #-}+-- | Lazy write-only state.+module Control.Eff.Writer.Lazy( Writer+ , tell+ , censor+ , runWriter+ , runFirstWriter+ , runLastWriter+ , runMonoidWriter+ ) where++import Control.Applicative ((<$>), (<|>))+import Data.Monoid+import Data.Typeable++import Control.Eff++-- | The request to remember a value of type w in the current environment+data Writer w v = Writer w v+ deriving (Typeable, Functor)++-- | Write a new value.+tell :: (Typeable w, Member (Writer w) r) => w -> Eff r ()+tell w = send $ \f -> inj $ Writer w $ f ()++-- | Transform the state being produced.+censor :: (Typeable w, Member (Writer w) r) => (w -> w) -> Eff r a -> Eff r a+censor f = loop . admin+ where+ loop (Val x) = return x+ loop (E u) = interpose u loop+ $ \(Writer w v) -> tell (f w) >> loop v++-- | Handle Writer requests, using a user-provided function to accumulate values.+runWriter :: Typeable w => (w -> b -> b) -> b -> Eff (Writer w :> r) a -> Eff r (b, a)+runWriter accum b = loop . admin+ where+ first f (x, y) = (f x, y)++ loop (Val x) = return (b, x)+ loop (E u) = handleRelay u loop+ $ \(Writer w v) -> first (accum w) <$> loop v++-- | Handle Writer requests by taking the first value provided.+runFirstWriter :: Typeable w => Eff (Writer w :> r) a -> Eff r (Maybe w, a)+runFirstWriter = runWriter (\w b -> Just w <|> b) Nothing++-- | Handle Writer requests by overwriting previous values.+runLastWriter :: Typeable w => Eff (Writer w :> r) a -> Eff r (Maybe w, a)+runLastWriter = runWriter (\w b -> b <|> Just w) Nothing++-- | Handle Writer requests, using a Monoid instance to accumulate values.+runMonoidWriter :: (Monoid w, Typeable w) => Eff (Writer w :> r) a -> Eff r (w, a)+runMonoidWriter = runWriter (<>) mempty
+ src/Control/Eff/Writer/Strict.hs view
@@ -0,0 +1,59 @@+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE DeriveFunctor #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE FlexibleContexts #-}+-- | Strict write-only state.+module Control.Eff.Writer.Strict( Writer+ , tell+ , censor+ , runWriter+ , runFirstWriter+ , runLastWriter+ , runMonoidWriter+ ) where++import Control.Applicative ((<$>), (<|>))+import Data.Monoid+import Data.Typeable++import Control.Eff++-- | The request to remember a value of type w in the current environment+data Writer w v = Writer !w v+ deriving (Typeable, Functor)++-- | Write a new value.+tell :: (Typeable w, Member (Writer w) r) => w -> Eff r ()+tell !w = send $ \f -> inj $ Writer w $ f ()++-- | Transform the state being produced.+censor :: (Typeable w, Member (Writer w) r) => (w -> w) -> Eff r a -> Eff r a+censor f = loop . admin+ where+ loop (Val x) = return x+ loop (E u) = interpose u loop+ $ \(Writer w v) -> tell (f w) >> loop v++-- | Handle Writer requests, using a user-provided function to accumulate values.+runWriter :: Typeable w => (w -> b -> b) -> b -> Eff (Writer w :> r) a -> Eff r (b, a)+runWriter accum !b = loop . admin+ where+ first f (x, y) = (f x, y)++ loop (Val x) = return (b, x)+ loop (E u) = handleRelay u loop+ $ \(Writer w v) -> first (accum w) <$> loop v++-- | Handle Writer requests by taking the first value provided.+runFirstWriter :: Typeable w => Eff (Writer w :> r) a -> Eff r (Maybe w, a)+runFirstWriter = runWriter (\w b -> Just w <|> b) Nothing++-- | Handle Writer requests by overwriting previous values.+runLastWriter :: Typeable w => Eff (Writer w :> r) a -> Eff r (Maybe w, a)+runLastWriter = runWriter (\w b -> b <|> Just w) Nothing++-- | Handle Writer requests, using a Monoid instance to accumulate values.+runMonoidWriter :: (Monoid w, Typeable w) => Eff (Writer w :> r) a -> Eff r (w, a)+runMonoidWriter = runWriter (<>) mempty
src/Data/OpenUnion1.hs view
@@ -2,6 +2,8 @@ {-# LANGUAGE EmptyDataDecls #-} {-# LANGUAGE ExistentialQuantification #-} {-# LANGUAGE KindSignatures #-}+{-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE PolyKinds #-} {-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, FlexibleContexts #-} {-# LANGUAGE OverlappingInstances #-} {-# LANGUAGE UndecidableInstances #-}@@ -11,12 +13,13 @@ -- This implementation relies on _closed_ overlapping instances -- (or closed type function overlapping soon to be added to GHC). module Data.OpenUnion1( Union+ , SetMember+ , Member , (:>) , inj , prj , prjForce , decomp- , Member , unsafeReUnion ) where @@ -46,11 +49,26 @@ infixr 1 :> data ((a :: * -> *) :> b) --- | There's a @`Member` t r@ instance if t is an element of the sum datatype r.-class Member (t :: * -> *) r+-- | The @`Member` t r@ determines whether @t@ is anywhere in the sum type @r@.+class Member t r instance Member t (t :> r) instance Member t r => Member t (t' :> r) +-- | `SetMember` is similar to `Member`, but it allows types to belong to a+-- "set", by taking advantage of the @r set -> t@ fundep:+--+-- > -- Terminal effects (effects which must be run last)+-- > data Terminal+-- >+-- > -- Make Lifts part of the Terminal effects set.+-- > -- The fundep assures that there can only be one Terminal effect for any r.+-- > instance Member (Lift m) r => SetMember Terminal (Lift m) r+-- >+-- > -- Only allow a single unique Lift effect, by making a "Lift" set.+-- > instance Member (Lift m) r => SetMember Lift (Lift m) r+class Member t r => SetMember set (t :: * -> *) r | r set -> t+instance SetMember set t r => SetMember set t (t' :> r)+ {-# INLINE inj #-} -- | Construct a Union. inj :: (Functor t, Typeable1 t, Member t r) => t v -> Union r v@@ -70,7 +88,7 @@ {-# INLINE decomp #-} -- | Try extracting the contents of a Union as a given type. -- If we can't, return a reduced Union that excludes the type we just checked.-decomp :: (Typeable1 t, Member t (t :> r)) => Union (t :> r) v -> Either (Union r v) (t v)+decomp :: Typeable1 t => Union (t :> r) v -> Either (Union r v) (t v) decomp u = Right <$> prj u <?> Left (unsafeReUnion u) {-# INLINE unsafeReUnion #-}
test/Test.hs view
@@ -1,16 +1,34 @@+{-# LANGUAGE TypeOperators #-} {-# LANGUAGE FlexibleContexts #-}-import Control.Eff-import Control.Eff.Lift-import Control.Eff.State-+import Control.Exception (Exception, ErrorCall, catch) import Control.Monad (void) import Data.Typeable import Test.Framework (defaultMain, testGroup)+import Test.Framework.Providers.HUnit import Test.Framework.Providers.QuickCheck2 +import Test.HUnit hiding (State) import Test.QuickCheck +import Control.Eff+import Control.Eff.Lift+import Control.Eff.Reader.Lazy as LazyR+import Control.Eff.State.Lazy as LazyS+import Control.Eff.Writer.Lazy as LazyW+import Control.Eff.Reader.Strict as StrictR+import Control.Eff.State.Strict as StrictS+import Control.Eff.Writer.Strict as StrictW++withError :: a -> ErrorCall -> a+withError a _ = a++assertUndefined :: a -> Assertion+assertUndefined a = catch (seq a $ assertFailure "") (withError $ return ())++assertNoUndefined :: a -> Assertion+assertNoUndefined a = catch (seq a $ return ()) (withError $ assertFailure "")+ main :: IO () main = defaultMain tests @@ -24,30 +42,103 @@ testDocs :: [Integer] -> Property testDocs l = let- (total1, ()) = run $ runState 0 $ sumAll l- (last1, ()) = run $ runWriter $ writeAll l- (total2, (last2, ())) = run $ runState 0 $ runWriter $ writeAndAdd l- (last3, (total3, ())) = run $ runWriter $ runState 0 $ writeAndAdd l+ (total1, ()) = run $ LazyS.runState 0 $ sumAll l+ (last1, ()) = run $ LazyW.runLastWriter $ writeAll l+ (total2, (last2, ())) = run $ LazyS.runState 0 $ LazyW.runLastWriter $ writeAndAdd l+ (last3, (total3, ())) = run $ LazyW.runLastWriter $ LazyS.runState 0 $ writeAndAdd l in allEqual [safeLast l, last1, last2, last3] .&&. allEqual [sum l, total1, total2, total3] where- writeAll :: (Typeable a, Member (Writer a) e)+ writeAll :: (Typeable a, Member (LazyW.Writer a) e) => [a] -> Eff e ()- writeAll = mapM_ putWriter+ writeAll = mapM_ LazyW.tell - sumAll :: (Typeable a, Num a, Member (State a) e)+ sumAll :: (Typeable a, Num a, Member (LazyS.State a) e) => [a] -> Eff e ()- sumAll = mapM_ (onState . (+))+ sumAll = mapM_ (LazyS.modify . (+)) - writeAndAdd :: (Member (Writer Integer) e, Member (State Integer) e)+ writeAndAdd :: (Member (LazyW.Writer Integer) e, Member (LazyS.State Integer) e) => [Integer] -> Eff e () writeAndAdd l = do writeAll l sumAll l -tests = [- testProperty "Documentation example." testDocs+testCensor :: [Integer] -> Property+testCensor l = property+ $ listE (mapM_ (LazyW.tell . inc) l) == listE (LazyW.censor inc $ mapM_ LazyW.tell l)+ where+ inc :: Integer -> Integer+ inc = (+1)++ listE :: Eff (LazyW.Writer Integer :> ()) () -> [Integer]+ listE = fst . run . LazyW.runWriter (:) []++testReaderLaziness :: Assertion+testReaderLaziness = let e = run $ LazyR.runReader voidReader (undefined :: ())+ in assertNoUndefined (e :: ())+ where+ voidReader = do+ _ <- (LazyR.ask :: Eff (LazyR.Reader () :> ()) ())+ return ()++testReaderStrictness :: Assertion+testReaderStrictness = let e = run $ StrictR.runReader voidReader (undefined :: ())+ in assertUndefined (e :: ())+ where+ voidReader = do+ _ <- (StrictR.ask :: Eff (StrictR.Reader () :> ()) ())+ return ()++testStateLaziness :: Assertion+testStateLaziness = let (r, ()) = run+ $ LazyS.runState undefined+ $ getVoid+ >> putVoid undefined+ >> putVoid ()+ in assertNoUndefined r+ where+ getVoid :: Eff (LazyS.State () :> ()) ()+ getVoid = LazyS.get++ putVoid :: () -> Eff (LazyS.State () :> ()) ()+ putVoid = LazyS.put++testStateStrictness :: Assertion+testStateStrictness = let (r, ()) = run+ $ StrictS.runState undefined+ $ getVoid+ >> putVoid undefined+ >> putVoid ()+ in assertUndefined r+ where+ getVoid :: Eff (StrictS.State () :> ()) ()+ getVoid = StrictS.get++ putVoid :: () -> Eff (StrictS.State () :> ()) ()+ putVoid = StrictS.put++testLastWriterLaziness :: Assertion+testLastWriterLaziness = let (Just m, ()) = run $ LazyW.runLastWriter $ mapM_ LazyW.tell [undefined, ()]+ in assertNoUndefined (m :: ())++testLastWriterStrictness :: Assertion+testLastWriterStrictness = let (Just m, ()) = run $ StrictW.runLastWriter $ mapM_ StrictW.tell [undefined, ()]+ in assertUndefined (m :: ())++testFirstWriterLaziness :: Assertion+testFirstWriterLaziness = let (Just m, ()) = run $ LazyW.runFirstWriter $ mapM_ LazyW.tell [(), undefined]+ in assertNoUndefined (m :: ())++tests =+ [ testProperty "Documentation example." testDocs+ , testCase "Test runReader laziness." testReaderLaziness+ , testCase "Test runReader strictness." testReaderStrictness+ , testCase "Test runState laziness." testStateLaziness+ , testCase "Test runState strictness." testStateStrictness+ , testCase "Test runLastWriter laziness." testLastWriterLaziness+ , testCase "Test runLastWriter strictness." testLastWriterStrictness+ , testCase "Test runFirstWriter laziness." testFirstWriterLaziness ]