packages feed

extensible-effects-2.0.1.0: test/Test.hs

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE NoMonomorphismRestriction #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# OPTIONS -fno-warn-missing-signatures #-}
import Control.Exception (ErrorCall, catch)
import Data.Typeable

import Test.Framework (defaultMain)
import Test.Framework.Providers.HUnit
import Test.Framework.Providers.QuickCheck2
import Test.Framework.TH

import Test.HUnit hiding (State)
import Test.QuickCheck

import Control.Eff
import Data.OpenUnion
import qualified Control.Eff.Reader.Lazy as LazyR
import qualified Control.Eff.Reader.Strict as StrictR
import qualified Control.Eff.Writer.Lazy as LazyW
import qualified Control.Eff.Writer.Strict as StrictW
import qualified Control.Eff.State.Strict as StrictS
import qualified Control.Eff.State.Lazy as LazyS
import qualified Control.Eff.State.LazyState as LazierS
import Control.Eff.Exception
import Control.Eff.Choose as Choose
import Control.Eff.NdetEff
import Control.Monad (liftM2, msum, guard, mzero, mplus)
import Data.Monoid

import Control.Eff.Example as Eg
import Control.Eff.Fresh
import Control.Eff.Lift
import Control.Eff.Operational as Op
import Control.Eff.Operational.Example as Op.Eg
import Control.Eff.Trace
import Control.Eff.Coroutine
import Control.Eff.Cut

-- {{{ utils: TODO: move them out

import GHC.IO.Handle
import System.IO
import System.Directory

-- | capture stdout
-- [[https://stackoverflow.com/a/9664017][source]]
catchOutput :: IO a -> IO (a, String)
catchOutput f = do
  tmpd <- getTemporaryDirectory
  (tmpf, tmph) <- openTempFile tmpd "haskell_stdout"
  stdout_dup <- hDuplicate stdout
  hDuplicateTo tmph stdout
  hClose tmph
  fVal <- f
  hDuplicateTo stdout_dup stdout
  str <- readFile tmpf
  removeFile tmpf
  return (fVal, str)

showLn :: Show a => a -> String
showLn x = unlines $ [show x]

showLines :: Show a => [a] -> String
showLines xs = unlines $ map show xs

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 "")

allEqual :: Eq a => [a] -> Bool
allEqual = all (uncurry (==)) . pairs
  where
    pairs l = zip l $ tail l

safeLast [] = Nothing
safeLast l = Just $ last l

-- }}}

main :: IO ()
main = $(defaultMainGenerator)

-- {{{ Documentation example

prop_Documentation_example :: [Integer] -> Property
prop_Documentation_example l = let
  ((), total1) = run $ LazyS.runState (Eg.sumAll l) 0
  ((), last1) = run $ LazyW.runLastWriter $ Eg.writeAll l
  (((), last2), total2) = run $ LazyS.runState (LazyW.runLastWriter (Eg.writeAndAdd l)) 0
  (((), total3), last3) = run $ LazyW.runLastWriter $ LazyS.runState (Eg.writeAndAdd l) 0
  in
   allEqual [safeLast l, last1, last2, last3]
   .&&. allEqual [sum l, total1, total2, total3]

-- }}}

-- {{{ Reader

add :: Monad m => m Int -> m Int -> m Int
add = liftM2 (+)
t1 = LazyR.ask `add` return (1::Int)

case_Lazy1_Reader_t1 :: Assertion
case_Lazy1_Reader_t1 = let
  t1' = do v <- LazyR.ask; return (v + 1 :: Int)
  t1r = LazyR.runReader t1 (10::Int)
  in
    -- 'LazyR.run t1' should result in type-error
    11 @=? (run t1r)

t2 = do
  v1 <- LazyR.ask
  v2 <- LazyR.ask
  return $ fromIntegral (v1 + (1::Int)) + (v2 + (2::Float))


case_Lazy1_Reader_t2 :: Assertion
case_Lazy1_Reader_t2 = let
  t2r = LazyR.runReader t2 (10::Int)
  t2rr = flip LazyR.runReader (20::Float) . flip LazyR.runReader (10::Int) $ t2
  in
    33.0 @=? (run t2rr)

-- The opposite order of layers
{- If we mess up, we get an error
t2rrr1' = run $ runReader (runReader t2 (20::Float)) (10::Float)
    No instance for (Member (Reader Int) [])
      arising from a use of `t2'
-}
case_Lazy1_Reader_t2' :: Assertion
case_Lazy1_Reader_t2' = 33.0 @=?
  (run $ LazyR.runReader (LazyR.runReader t2 (20::Float)) (10::Int))


case_Lazy1_Reader_t3 :: Assertion
case_Lazy1_Reader_t3 = let
  t3 = t1 `add` LazyR.local (+ (10::Int)) t1
  in
    212 @=? (run $ LazyR.runReader t3 (100::Int))

-- The following example demonstrates true interleaving of Reader Int
-- and Reader Float layers
{-
t4
  :: (Member (Reader Int) r, Member (Reader Float) r) =>
     () -> Eff r Float
-}
t4 = liftM2 (+) (LazyR.local (+ (10::Int)) t2)
                (LazyR.local (+ (30::Float)) t2)

case_Lazy1_Reader_t4 :: Assertion
case_Lazy1_Reader_t4 = 106.0 @=?
  (run $ LazyR.runReader (LazyR.runReader t4 (20::Float)) (10::Int))

-- The opposite order of layers gives the same result
case_Lazy1_Reader_t4' :: Assertion
case_Lazy1_Reader_t4' = 106.0 @=?
  (run $ LazyR.runReader (LazyR.runReader t4 (20::Float)) (10::Int))

-- Map an effectful function
case_Lazy1_Reader_tmap :: Assertion
case_Lazy1_Reader_tmap = let
  tmap = mapM f [1..5]
  in
    ([11,12,13,14,15] :: [Int]) @=?
    (run $ LazyR.runReader tmap (10::Int))
  where
    f x = LazyR.ask `add` return x

-- {{{ Reader.runReader

case_Lazy1_Reader_runReader :: Assertion
case_Lazy1_Reader_runReader = let
  e = run $ LazyR.runReader voidReader (undefined :: ())
  in
   assertNoUndefined (e :: ())
  where
    voidReader = do
        _ <- (LazyR.ask :: Eff '[LazyR.Reader ()] ())
        return ()

case_Strict1_Reader_runReader :: Assertion
case_Strict1_Reader_runReader = let
  e = run $ StrictR.runReader voidReader (undefined :: ())
  in
   assertUndefined (e :: ())
  where
    voidReader = do
        _ <- (StrictR.ask :: Eff '[StrictR.Reader ()] ())
        return ()

-- }}}

-- }}}

-- {{{ State.runState

-- {{{ Lazy

case_Lazy1_State_runState :: Assertion
case_Lazy1_State_runState = let
  (r, ()) = run
            $ flip 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

-- }}}

-- {{{ Strict1

case_Strict1_State_runState :: Assertion
case_Strict1_State_runState = let
  (r, ()) = run
            $ (flip 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

case_Strict1_State_ts1 :: Assertion
case_Strict1_State_ts1 = (10,10) @=? (run (StrictS.runState ts1 (0::Int)))
  where
    ts1 = do
      StrictS.put (10 ::Int)
      x <- StrictS.get
      return (x::Int)

case_Strict1_State_ts11 :: Assertion
case_Strict1_State_ts11 =
  (10,10) @=? (run (StrictS.runStateR ts11 (0::Int)))
  where
    ts11 = do
      StrictW.tell (10 ::Int)
      x <- StrictR.ask
      return (x::Int)

case_Strict1_State_ts2 :: Assertion
case_Strict1_State_ts2 = (30::Int,20::Int) @=?
  (run (StrictS.runState ts2 (0::Int)))
  where
    ts2 = do
      StrictS.put (10::Int)
      x <- StrictS.get
      StrictS.put (20::Int)
      y <- StrictS.get
      return (x+y)

case_Strict1_State_ts21 :: Assertion
case_Strict1_State_ts21 = (30::Int,20::Int) @=?
  (run (StrictS.runStateR ts21 (0::Int)))
  where
    ts21 = do
      StrictW.tell (10::Int)
      x <- StrictR.ask
      StrictW.tell (20::Int)
      y <- StrictR.ask
      return (x+y)

tes1 :: (Member (StrictS.State Int) r
        , Member (Exc [Char]) r) => Eff r b
tes1 = do
  incr
  throwExc "exc"
  where
    incr = StrictS.get >>= StrictS.put . (+ (1::Int))

case_Strict1_State_ter1 :: Assertion
case_Strict1_State_ter1 = (Left "exc" :: Either String Int,2) @=?
  (run $ StrictS.runState (runExc tes1) (1::Int))

case_Strict1_State_ter2 :: Assertion
case_Strict1_State_ter2 = (Left "exc" :: Either String (Int,Int)) @=?
  (run $ runExc (StrictS.runState tes1 (1::Int)))

teCatch :: Member (Exc String) r => Eff r a -> Eff r [Char]
teCatch m = catchExc (m >> return "done") (\e -> return (e::String))

case_Strict1_State_ter3 :: Assertion
case_Strict1_State_ter3 = (Right "exc" :: Either String String,2) @=?
  (run $ StrictS.runState (runExc (teCatch tes1)) (1::Int))

case_Strict1_State_ter4 :: Assertion
case_Strict1_State_ter4 = (Right ("exc",2) :: Either String (String,Int)) @=?
  (run $ runExc (StrictS.runState (teCatch tes1) (1::Int)))

-- }}}

-- }}}

-- {{{ Writer

addGet :: Member (LazyR.Reader Int) r  => Int -> Eff r Int
addGet x = LazyR.ask >>= \i -> return (i+x)

addN n = foldl (>>>) return (replicate n addGet) 0
 where f >>> g = (>>= g) . f

case_Lazy1_Writer_rdwr :: Assertion
case_Lazy1_Writer_rdwr = (10, ["begin", "end"]) @=?
  (run . (`LazyR.runReader` (1::Int)) . LazyW.runListWriter $ rdwr)
  where
    rdwr = do
      LazyW.tell "begin"
      r <- addN 10
      LazyW.tell "end"
      return r

-- {{{ Writer.censor

prop_Lazy1_Writer_censor :: [Integer] -> Property
prop_Lazy1_Writer_censor 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 = snd . run . LazyW.runListWriter

-- }}}

-- {{{ Writer.runFirstWriter

case_Lazy1_Writer_runFirstWriter :: Assertion
case_Lazy1_Writer_runFirstWriter = let
  ((), Just m) = run $ LazyW.runFirstWriter $ mapM_ LazyW.tell [(), undefined]
  in
   assertNoUndefined (m :: ())

-- }}}

-- {{{ Writer.runLastWriter

case_Lazy1_Writer_runLastWriter :: Assertion
case_Lazy1_Writer_runLastWriter = let
  ((), Just m) = run $ LazyW.runLastWriter $ mapM_ LazyW.tell [undefined, ()]
  in
   assertNoUndefined (m :: ())

case_Strict1_Writer_runLastWriter :: Assertion
case_Strict1_Writer_runLastWriter = let
  ((), Just m) = run $ StrictW.runLastWriter $ mapM_ StrictW.tell [undefined, ()]
  in
   assertUndefined (m :: ())

-- }}}

-- }}}

-- {{{ Exception

-- The type is inferred
-- et1 :: Eff r Int
et1 = return 1 `add` return 2

case_Exception1_et1 :: Assertion
case_Exception1_et1 = 3 @=? (run et1)

-- The type is inferred
-- et2 :: Member (Exc Int) r => Eff r Int
et2 = return 1 `add` throwExc (2::Int)

-- The following won't type: unhandled exception!
-- ex2rw = run et2
{-
    Could not deduce (Data.OpenUnion51.FindElem (Exc Int) '[])
      arising from a use of `et2'
-}

case_Exception1_et21 :: Assertion
case_Exception1_et21 = (Left (2::Int)) @=?
  (run et21)
  where
    -- The inferred type shows that ex21 is now pure
    -- et21 :: Eff r (Either Int Int)

    et21 = runExc et2

-- {{{ TooBig example from paper

-- The example from the paper
newtype TooBig = TooBig Int deriving (Eq, Show)
-- The type is inferred
ex2 :: Member (Exc TooBig) r => Eff r Int -> Eff r Int
ex2 m = do
  v <- m
  if v > 5 then throwExc (TooBig v)
     else return v

-- specialization to tell the type of the exception
runErrBig :: Eff (Exc TooBig ': r) a -> Eff r (Either TooBig a)
runErrBig = runExc

case_Exception1_ex2r :: Assertion
case_Exception1_ex2r = (Right 5) @=? (run ex2r)
  where
    ex2r = LazyR.runReader (runErrBig (ex2 LazyR.ask)) (5::Int)

case_Exception1_ex2r1 :: Assertion
case_Exception1_ex2r1 = (Left (TooBig 7)) @=? (run ex2r1)
  where
    ex2r1 = LazyR.runReader (runErrBig (ex2 LazyR.ask)) (7::Int)

-- Different order of handlers (layers)
case_Exception1_ex2r2 :: Assertion
case_Exception1_ex2r2 = (Left (TooBig 7)) @=? (run ex2r2)
  where
    ex2r2 = runErrBig (LazyR.runReader (ex2 LazyR.ask) (7::Int))

-- }}}

-- {{{ Alternative

-- Implementing the operator <|> from Alternative:
--  a <|> b does
--   -- tries a, and if succeeds, returns its result
--   -- otherwise, tries b, and if succeeds, returns its result
--   -- otherwise, throws mappend of exceptions of a and b

-- We use SetMember in the signature rather than Member to
-- ensure that the computation throws only one type of exceptions.
-- Otherwise, this construction is not very useful.
alttry :: forall e r a. (Monoid e, SetMember Exc (Exc e) r) =>
          Eff r a -> Eff r a -> Eff r a
alttry ma mb =
  catchExc ma $ \ea ->
  catchExc mb $ \eb -> throwExc (mappend (ea::e) eb)

case_Exception1_alttry :: Assertion
case_Exception1_alttry =
  [Right 10,Right 10,Right 10,Left "bummer1bummer2"] @=?
  [
  run . runExc $
     (return 1 `add` throwExc "bummer1") `alttry`
     (return 10),
  run . runExc $
     (return 10) `alttry`
     (return 1 `add` throwExc "bummer2"),
  run . runExc $
     (return 10) `alttry` return 20,
  run . runExc $
     (return 1 `add` throwExc "bummer1") `alttry`
     (return 1 `add` throwExc "bummer2")
     ]

-- }}}

-- {{{ Eff Failure

case_Failure1_Effect :: Assertion
case_Failure1_Effect =
  let go :: Eff (Exc () ': StrictW.Writer Int ': '[]) Int
         -> Int
      go = snd . run . StrictW.runWriter (+) 0 . ignoreFail
      ret = go $ do
        StrictW.tell (1 :: Int)
        StrictW.tell (2 :: Int)
        StrictW.tell (3 :: Int)
        () <- die
        StrictW.tell (4 :: Int)
        return 5
   in assertEqual "Fail should stop writing" 6 ret

-- }}}

-- }}}

-- {{{ Choose

case_Choose1_exc11 :: Assertion
case_Choose1_exc11 = [2,3] @=? (run exc11)
  where
    exc11 = Choose.makeChoice exc1
    exc1 = return 1 `add` Choose.choose [1,2]

case_Choose_ex2 :: Assertion
case_Choose_ex2 =
  let ex2_1 = run . makeChoice . runErrBig $ ex2 (Choose.choose [5,7,1])
      ex2_2 = run . runErrBig . makeChoice $ ex2 (Choose.choose [5,7,1])
  in
    assertEqual "Choose: Combining exceptions and non-determinism: ex2_1"
    expected1 ex2_1
    >> assertEqual "Choose: Combining exceptions and non-determinism: ex2_2"
    expected2 ex2_2
  where
    expected1 = [Right 5,Left (TooBig 7),Right 1]
    expected2 = Left (TooBig 7)

case_Choose_exRec :: Assertion
case_Choose_exRec =
  let exRec_1 = run . runErrBig . makeChoice $ exRec (ex2 (Choose.choose [5,7,1]))
      exRec_2 = run . makeChoice . runErrBig $ exRec (ex2 (Choose.choose [5,7,1]))
      exRec_3 = run . runErrBig . makeChoice $ exRec (ex2 (Choose.choose [5,7,11,1]))
  in
    assertEqual "Choose: error recovery: exRec_1" expected1 exRec_1
    >> assertEqual "Choose: error recovery: exRec_2" expected2 exRec_2
    >> assertEqual "Choose: error recovery: exRec_1" expected3 exRec_3
  where
    expected1 = Right [5,7,1]
    expected2 = [Right 5,Right 7,Right 1]
    expected3 = Left (TooBig 11)
    -- Errror recovery part
    -- The code is the same as in transf1.hs. The inferred signatures differ
    -- Was: exRec :: MonadError TooBig m => m Int -> m Int
    -- exRec :: Member (Exc TooBig) r => Eff r Int -> Eff r Int
    exRec m = catchExc m handler
      where handler (TooBig n) | n <= 7 = return n
            handler e = throwExc e

-- }}}

-- {{{ NdetEff

case_NdetEff_testCA :: Assertion
case_NdetEff_testCA = [2, 4..10] @=? (run $ makeChoiceA testCA)
  where
    testCA :: (Integral a) => Eff (NdetEff ': r) a
    testCA = do
      i <- msum . fmap return $ [1..10]
      guard (i `mod` 2 == 0)
      return i

case_NdetEff_ifte :: Assertion
case_NdetEff_ifte =
  let primes = ifte_test_run
  in
    assertEqual "NdetEff: test ifte using primes"
    [2,3,5,7,11,13,17,19,23,29] primes
  where
    ifte_test = do
      n <- gen
      ifte (do
               d <- gen
               guard $ d < n && n `mod` d == 0
               -- _ <- trace ("d: " ++ show d) (return ())
           )
        (\_ -> mzero)
        (return n)
        where gen = msum . fmap return $ [2..30]

    ifte_test_run :: [Int]
    ifte_test_run = run . makeChoiceA $ ifte_test


-- called reflect in the LogicT paper
case_NdetEff_reflect :: Assertion
case_NdetEff_reflect =
  let tsplitr10 = run $ StrictW.runListWriter $ makeChoiceA tsplit
      tsplitr11 = run $ StrictW.runListWriter $ makeChoiceA (msplit tsplit >>= unmsplit)
      tsplitr20 = run $ makeChoiceA $ StrictW.runListWriter tsplit
      tsplitr21 = run $ makeChoiceA $ StrictW.runListWriter (msplit tsplit >>= unmsplit)
  in
    assertEqual "tsplitr10" expected1 tsplitr10
    >> assertEqual "tsplitr11" expected1 tsplitr11
    >> assertEqual "tsplitr20" expected2 tsplitr20
    >> assertEqual "tsplitr21" expected21 tsplitr21
  where
    expected1 = ([1, 2],["begin", "end"])
    expected2 = [(1, ["begin"]), (2, ["end"])]
    expected21 = [(1, ["begin"]), (2, ["begin", "end"])]

    unmsplit :: Member NdetEff r => (Maybe (a, Eff r a)) -> Eff r a
    unmsplit Nothing      = mzero
    unmsplit (Just (a,m)) = return a `mplus` m

    tsplit =
      (StrictW.tell "begin" >> return 1) `mplus`
      (StrictW.tell "end"   >> return 2)

-- }}}

-- {{{ test Lift building

-- | Ensure that https://github.com/RobotGymnast/extensible-effects/issues/11 stays resolved.
case_Lift_building :: Assertion
case_Lift_building = runLift possiblyAmbiguous
  where
    possiblyAmbiguous :: (Monad m, SetMember Lift (Lift m) r) => Eff r ()
    possiblyAmbiguous = lift $ return ()

-- }}}

-- {{{ Nested Eff

prop_NestedEff :: Property
prop_NestedEff = forAll arbitrary (\x -> property (qu x == x))
  where
    qu :: Bool -> Bool
    qu x = run $ StrictR.runReader (readerAp x) readerId

    readerAp :: Bool -> Eff '[StrictR.Reader (Eff '[StrictR.Reader Bool] Bool)] Bool
    readerAp x = do
      f <- StrictR.ask
      return . run $ StrictR.runReader f x

    readerId :: Eff '[StrictR.Reader Bool] Bool
    readerId = do
      x <- StrictR.ask
      return x

-- }}}

-- {{{ Operational Monad

case_Operational_Monad :: Assertion
case_Operational_Monad =
  let comp :: (Member (LazyS.State [String]) r
               , Member (LazyW.Writer String) r)
              => Eff r ()
      comp = Op.runProgram Op.Eg.adventPure Op.Eg.prog
      go = snd . run . LazyW.runMonoidWriter $ LazyS.evalState comp ["foo", "bar"]
  in
   assertEqual
   "Evaluating Operational Monad example"
   (unlines ["getting input...",
             "ok",
             "the input is foo"]) go

-- }}}

-- {{{ Yield

yieldInt :: Member (Yield Int ()) r => Int -> Eff r ()
yieldInt = yield

case_Coroutines_c1 :: Assertion
case_Coroutines_c1 = do
  ((), actual) <- catchOutput c1
  assertEqual
    "Coroutine: Simple coroutines using Eff"
    (unlines ["1", "2", "Done"]) actual
  where
    th1 :: Member (Yield Int ()) r => Eff r ()
    th1 = yieldInt 1 >> yieldInt 2

    c1 = runTrace (loop =<< runC th1)
      where loop (Y x k) = trace (show (x::Int)) >> k () >>= loop
            loop (Done)    = trace ("Done")

case_Coroutines_c2 :: Assertion
case_Coroutines_c2 = do
  ((), actual1) <- catchOutput c2
  assertEqual "Coroutine: Add dynamic variables"
    (unlines ["10", "10", "Done"]) actual1
  ((), actual2) <- catchOutput c21
  assertEqual "Coroutine: locally changing the dynamic environment for the suspension"
    (unlines ["10", "11", "Done"]) actual2
  where
    -- The code is essentially the same as that in transf.hs (only added
    -- a type specializtion on yield). The inferred signature is different though.
    -- Before it was
    --    th2 :: MonadReader Int m => CoT Int m ()
    -- Now it is more general:
    th2 :: (Member (Yield Int ()) r, Member (StrictR.Reader Int) r) => Eff r ()
    th2 = StrictR.ask >>= yieldInt >> (StrictR.ask >>= yieldInt)

    -- Code is essentially the same as in transf.hs; no liftIO though
    c2 = runTrace $ StrictR.runReader (loop =<< runC th2) (10::Int)
      where loop (Y x k) = trace (show (x::Int)) >> k () >>= loop
            loop Done    = trace "Done"

    -- locally changing the dynamic environment for the suspension
    c21 = runTrace $ StrictR.runReader (loop =<< runC th2) (10::Int)
      where loop (Y x k) = trace (show (x::Int)) >> StrictR.local (+(1::Int)) (k ()) >>= loop
            loop Done    = trace "Done"

case_Coroutines_c3 :: Assertion
case_Coroutines_c3 = do
  ((), actual1) <- catchOutput c3
  assertEqual "Coroutine: two sorts of local rebinding"
    (unlines ["10", "10", "20", "20", "Done"]) actual1
  ((), actual2) <- catchOutput c31
  let expected2 = (unlines ["10", "11", "21", "21", "Done"])
  assertEqual "Coroutine: locally changing the dynamic environment for the suspension"
    expected2 actual2
  ((), actual3) <- catchOutput c4
  assertEqual "Coroutine: abstracting the client computation"
    expected2 actual3
  where
    local = StrictR.local
    ask = StrictR.ask
    runReader = StrictR.runReader

    th3 :: (Member (Yield Int ()) r, Member (StrictR.Reader Int) r) => Eff r ()
    th3 = ay >> ay >> local (+(10::Int)) (ay >> ay)
      where ay = ask >>= yieldInt

    c3 = runTrace $ runReader (loop =<< runC th3) (10::Int)
      where loop (Y x k) = trace (show (x::Int)) >> k () >>= loop
            loop Done    = trace "Done"

    -- The desired result: the coroutine shares the dynamic environment with its
    -- parent; however, when the environment is locally rebound, it becomes
    -- private to coroutine.
    c31 = runTrace $ runReader (loop =<< runC th3) (10::Int)
      where loop (Y x k) = trace (show (x::Int)) >> local (+(1::Int)) (k ()) >>= loop
            loop Done    = trace "Done"

    -- We now make explicit that the client computation, run by th4,
    -- is abstract. We abstract it out of th4
    c4 = runTrace $ runReader (loop =<< runC (th4 client)) (10::Int)
      where loop (Y x k) = trace (show (x::Int)) >> local (+(1::Int)) (k ()) >>= loop
            loop Done    = trace "Done"

            -- cl, client, ay are monomorphic bindings
            th4 cl = cl >> local (+(10::Int)) cl
            client = ay >> ay
            ay     = ask >>= yieldInt

case_Corountines_c5 :: Assertion
case_Corountines_c5 = do
  ((), actual) <- catchOutput c5
  let expected = unlines ["10"
                         ,"11"
                         ,"12"
                         ,"18"
                         ,"18"
                         ,"18"
                         ,"29"
                         ,"29"
                         ,"29"
                         ,"29"
                         ,"29"
                         ,"29"
                         ,"Done"
                         ]
  assertEqual "Corountine: Even more dynamic example"
    expected actual
  where
    local = StrictR.local
    ask = StrictR.ask
    runReader = StrictR.runReader

    c5 = runTrace $ runReader (loop =<< runC (th client)) (10::Int)
      where loop (Y x k) = trace (show (x::Int)) >> local (\y->x+1) (k ()) >>= loop
            loop Done    = trace "Done"

            -- cl, client, ay are monomorphic bindings
            client = ay >> ay >> ay
            ay     = ask >>= yieldInt

            -- There is no polymorphic recursion here
            th cl = do
              cl
              v <- ask
              (if v > (20::Int) then id else local (+(5::Int))) cl
              if v > (20::Int) then return () else local (+(10::Int)) (th cl)

case_Coroutines_c7 :: Assertion
case_Coroutines_c7 = do
  ((), actual) <- catchOutput c7
  let expected = unlines ["1010"
                         ,"1021"
                         ,"1032"
                         ,"1048"
                         ,"1064"
                         ,"1080"
                         ,"1101"
                         ,"1122"
                         ,"1143"
                         ,"1169"
                         ,"1195"
                         ,"1221"
                         ,"1252"
                         ,"1283"
                         ,"1314"
                         ,"1345"
                         ,"1376"
                         ,"1407"
                         ,"Done"
                         ]
  assertEqual "Coroutine: And even more dynamic example"
    expected actual
  where
    local = StrictR.local
    ask = StrictR.ask
    runReader = StrictR.runReader

    c7 = runTrace $
          runReader (runReader (loop =<< runC (th client)) (10::Int)) (1000::Double)
     where loop (Y x k) = trace (show (x::Int)) >>
                          local (\y->fromIntegral (x+1)::Double) (k ()) >>= loop
           loop Done    = trace "Done"

           -- cl, client, ay are monomorphic bindings
           client = ay >> ay >> ay
           ay     = ask >>= \x -> ask >>=
                     \y -> yieldInt (x + round (y::Double))

           -- There is no polymorphic recursion here
           th cl = do
             cl
             v <- ask
             (if v > (20::Int) then id else local (+(5::Int))) cl
             if v > (20::Int) then return () else local (+(10::Int)) (th cl)

case_Coroutines_c7' :: Assertion
case_Coroutines_c7' = do
  ((), actual) <- catchOutput c7'
  let expected = unlines ["1010"
                         ,"1021"
                         ,"1032"
                         ,"1048"
                         ,"1048"
                         ,"1048"
                         ,"1069"
                         ,"1090"
                         ,"1111"
                         ,"1137"
                         ,"1137"
                         ,"1137"
                         ,"1168"
                         ,"1199"
                         ,"1230"
                         ,"1261"
                         ,"1292"
                         ,"1323"
                         ,"Done"
                         ]
  assertEqual "Coroutine: And even more dynamic example"
    expected actual
  where
    local = StrictR.local
    ask = StrictR.ask
    runReader = StrictR.runReader

    c7' = runTrace $
          runReader (runReader (loop =<< runC (th client)) (10::Int)) (1000::Double)
     where loop (Y x k) = trace (show (x::Int)) >>
                          local (\y->fromIntegral (x+1)::Double) (k ()) >>= loop
           loop Done    = trace "Done"

           -- cl, client, ay are monomorphic bindings
           client = ay >> ay >> ay
           ay     = ask >>= \x -> ask >>=
                     \y -> yieldInt (x + round (y::Double))

           -- There is no polymorphic recursion here
           th cl = do
             cl
             v <- ask
             (if v > (20::Int) then id else local (+(5::Double))) cl
             if v > (20::Int) then return () else local (+(10::Int)) (th cl)

-- }}}

-- {{{ Lift

case_Lift_tl1r :: Assertion
case_Lift_tl1r = do
  ((), output) <- catchOutput tl1r
  assertEqual "Test tl1r" (showLn input) output
  where
    input = (5::Int)
    -- tl1r :: IO ()
    tl1r = runLift (StrictR.runReader tl1 input)
      where
        tl1 = StrictR.ask >>= \(x::Int) -> lift . print $ x

case_Lift_tMd' :: Assertion
case_Lift_tMd' = do
  actual <- catchOutput tMd'
  let expected = (output, (showLines input))
  assertEqual "Test mapMdebug using Lift" expected actual
  where
    input = [1..5]
    val = (10::Int)
    output = map (+ val) input

    tMd' = runLift $ StrictR.runReader (mapMdebug' f input) val
      where f x = StrictR.ask `add` return x

    -- Re-implemenation of mapMdebug using Lifting
    -- The signature is inferred
    mapMdebug'  :: (Show a, SetMember Lift (Lift IO) r) =>
                   (a -> Eff r b) -> [a] -> Eff r [b]
    mapMdebug' f [] = return []
    mapMdebug' f (h:t) = do
      lift $ print h
      h' <- f h
      t' <- mapMdebug' f t
      return (h':t')

-- }}}

-- {{{ Trace

case_Trace_tdup :: Assertion
case_Trace_tdup = do
  ((), actual) <- catchOutput tdup
  assertEqual "Trace: duplicate layers"
    (unlines ["Asked: 20", "Asked: 10"]) actual
  where
    tdup = runTrace $ StrictR.runReader m (10::Int)
     where
     m = do
         StrictR.runReader tr (20::Int)
         tr
     tr = do
          v <- StrictR.ask
          trace $ "Asked: " ++ show (v::Int)

case_Trace_tMd :: Assertion
case_Trace_tMd = do
  actual <- catchOutput tMd
  assertEqual "Trace: higher-order effectful function"
    (map (+ val) input, unlines $ map (("mapMdebug: " ++) . show) input) actual
  where
    val = (10::Int)
    input = [1..5]
    tMd = runTrace $ StrictR.runReader (mapMdebug f input) val
      where
        f x = StrictR.ask `add` return x

        -- Higher-order effectful function
        -- The inferred type shows that the Trace affect is added to the effects
        -- of r
        mapMdebug:: (Show a, Member Trace r) =>
                    (a -> Eff r b) -> [a] -> Eff r [b]
        mapMdebug f [] = return []
        mapMdebug f (h:t) = do
          trace $ "mapMdebug: " ++ show h
          h' <- f h
          t' <- mapMdebug f t
          return (h':t')

-- }}}

-- {{{ Fresh

case_Fresh_tfresh' :: Assertion
case_Fresh_tfresh' = do
  ((), actual) <- catchOutput tfresh'
  assertEqual "Fresh: test"
    (unlines ["Fresh 0", "Fresh 1"]) actual
  where
    tfresh' = runTrace $ flip runFresh' 0 $ do
      n <- fresh
      trace $ "Fresh " ++ show n
      n <- fresh
      trace $ "Fresh " ++ show n

-- }}}

-- {{{ LazyState

case_LazierState_ex1 :: Assertion
case_LazierState_ex1 =
  let actual = run $ LazierS.runStateLazy 0 lex1
  in
    assertEqual "LazyState: ex1"
    ((), 1::Int) actual
  where
    lex1 = do
      LazierS.onDemand lex1
      LazierS.lput (1::Int)

case_LazierState_ex3 :: Assertion
case_LazierState_ex3 =
  let (x,s) = run $ LazierS.runStateLazy (undefined::[Int]) lex3
  in assertEqual "LazyState: ex3"
     ((),[1,1,1,1,1]) (x,take 5 s)
  where
    lex3 = do
      LazierS.onDemand lex3
      LazierS.lmodify ((1::Int):)

-- a bit more interesting
case_LazierState_ex4 =
  let (x,s) = run $ LazierS.runStateLazy [] lex4
  in assertEqual "LazyState: ex4"
     expect (take 7 $ x,take 5 $ s)
  where
    expect = ([3,2,3,2,3,2,3],[3,2,3,2,3])
    lex4 :: Eff '[LazierS.LazyState [Int]] [Int]
    lex4 = do
      LazierS.lmodify ((0::Int):)
      LazierS.onDemand lex4
      LazierS.lmodify ((1::Int):)
      LazierS.onDemand (LazierS.onDemand lex4 :: Eff '[LazierS.LazyState [Int]] [Int])
      LazierS.lmodify ((2::Int):)
      LazierS.lmodify ((3::Int):)
      LazierS.lget


-- Edward's example plus exceptions
case_LazierState_ex5 :: Assertion
case_LazierState_ex5 =
  let
    -- the annotations below are needed for assertEqual
    ex5Run :: Either [Int] () = fst . run . LazierS.runStateLazy (undefined::[Int]) . runExc $ lex5
    ex51Run :: Either [Int] ((), [Int]) = run . runExc . LazierS.runStateLazy (undefined::[Int]) $ lex5
  in
    assertEqual "LazyState ex5" (Left ones) ex5Run
    >> assertEqual "LazyState ex51" (Left ones) ex51Run
  where
    ones = take 5 $ repeat (1::Int)
    lex31 :: Member (LazierS.LazyState [Int]) r => Eff r ()
    lex31 = do
      LazierS.onDemand (lex31 :: Eff '[LazierS.LazyState [Int]] ())
      LazierS.lmodify ((1::Int):)

    lex5 = do
      lex31
      x <- LazierS.lget
      throwExc ((take 5 x)::[Int])

case_LazierState_st :: Assertion
case_LazierState_st = let
  stF :: ((Int,Int,Int),Int) = run $ LazierS.runStateLazy (0::Int) st
  stB0 :: ((Int,Int,Int),Int) = LazierS.runStateBack0 st
  stB :: ((Int,Int,Int),Int) = LazierS.runStateBack st
  in
    assertEqual "LazyState stF" ((0,1,3),4) stF
    >> assertEqual "LazyState stB0" ((1,2,4),1) stB0
    >> assertEqual "LazyState stB" ((1,2,4),1) stB
  where
    st = do
      x <- LazierS.lget
      LazierS.lput (1::Int)
      LazierS.lput (1::Int)
      y <- LazierS.lget
      LazierS.lput (2::Int)
      LazierS.lput (10::Int)
      LazierS.lput (3::Int)
      z <- LazierS.lget
      LazierS.lput (4::Int)
      return (x,y,z)

case_LazierState_ones :: Assertion
case_LazierState_ones =
  let ones :: [Int] = snd $ LazierS.runStateBack $ do
        s <- LazierS.lget
        LazierS.lput ((1::Int):s)
  in
    assertEqual "LazyState ones" [1,1,1,1,1] (take 5 ones)

-- }}}

-- {{{ Cut

case_Cut_tcut :: Assertion
case_Cut_tcut =
  let tcut1r = run . makeChoice $ call tcut1
      tcut2r = run . makeChoice $ call tcut2
      tcut3r = run . makeChoice $ call tcut3
      tcut4r = run . makeChoice $ call tcut4
  in
    assertEqual "Cut: tcut1" [1,2] tcut1r
    >> assertEqual "Cut: nested call: tcut2" [1,2,5] tcut2r
    >> assertEqual "Cut: nested call: tcut3" [1,2,1,2,5] tcut3r
    >> assertEqual "Cut: nested call: tcut4" [1,2,1,2,5] tcut4r
  where
    -- signature is inferred
    -- tcut1 :: (Member Choose r, Member (Exc CutFalse) r) => Eff r Int
    tcut1 = (return (1::Int) `mplus'` return 2) `mplus'`
            ((cutfalse `mplus'` return 4) `mplus'`
             return 5)
    -- Here we see nested call. It poses no problems...
    tcut2 = return (1::Int) `mplus'`
            call (return 2 `mplus'` (cutfalse `mplus'` return 3) `mplus'`
                  return 4)
            `mplus'` return 5
    tcut3 = call tcut1 `mplus'` call (tcut2 `mplus'` cutfalse)
    tcut4 = call tcut1 `mplus'`  (tcut2 `mplus'` cutfalse)

-- }}}