packages feed

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 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   ]