fused-effects-1.0.0.0: examples/Teletype.hs
{-# LANGUAGE DeriveFunctor, DeriveGeneric, FlexibleInstances, GeneralizedNewtypeDeriving, MultiParamTypeClasses, TypeOperators, UndecidableInstances #-}
module Teletype
( example
, runTeletypeIO
) where
import Prelude hiding (read)
import Control.Algebra
import Control.Carrier.State.Strict
import Control.Carrier.Writer.Strict
import Control.Monad.IO.Class
import GHC.Generics (Generic1)
import Hedgehog
import qualified Hedgehog.Gen as Gen
import qualified Hedgehog.Range as Range
import Test.Tasty
import Test.Tasty.Hedgehog
example :: TestTree
example = testGroup "teletype"
[ testProperty "reads" . property $ do
line <- forAll genLine
run (runTeletypeRet [line] read) === ([], ([], line))
, testProperty "writes" . property $ do
input <- forAll (Gen.list (Range.linear 0 10) genLine)
output <- forAll genLine
run (runTeletypeRet input (write output)) === ([output], (input, ()))
, testProperty "writes multiple things" . property $ do
input <- forAll (Gen.list (Range.linear 0 10) genLine)
output1 <- forAll genLine
output2 <- forAll genLine
run (runTeletypeRet input (write output1 >> write output2)) === ([output1, output2], (input, ()))
] where
genLine = Gen.string (Range.linear 0 20) Gen.unicode
data Teletype m k
= Read (String -> m k)
| Write String (m k)
deriving (Functor, Generic1)
instance HFunctor Teletype
instance Effect Teletype
read :: Has Teletype sig m => m String
read = send (Read pure)
write :: Has Teletype sig m => String -> m ()
write s = send (Write s (pure ()))
runTeletypeIO :: TeletypeIOC m a -> m a
runTeletypeIO = runTeletypeIOC
newtype TeletypeIOC m a = TeletypeIOC { runTeletypeIOC :: m a }
deriving (Applicative, Functor, Monad, MonadIO)
instance (MonadIO m, Algebra sig m) => Algebra (Teletype :+: sig) (TeletypeIOC m) where
alg (L (Read k)) = liftIO getLine >>= k
alg (L (Write s k)) = liftIO (putStrLn s) >> k
alg (R other) = TeletypeIOC (alg (handleCoercible other))
runTeletypeRet :: [String] -> TeletypeRetC m a -> m ([String], ([String], a))
runTeletypeRet i = runWriter . runState i . runTeletypeRetC
newtype TeletypeRetC m a = TeletypeRetC { runTeletypeRetC :: StateC [String] (WriterC [String] m) a }
deriving (Applicative, Functor, Monad)
instance (Algebra sig m, Effect sig) => Algebra (Teletype :+: sig) (TeletypeRetC m) where
alg (L (Read k)) = do
i <- TeletypeRetC get
case i of
[] -> k ""
h:t -> TeletypeRetC (put t) *> k h
alg (L (Write s k)) = TeletypeRetC (tell [s]) *> k
alg (R other) = TeletypeRetC (alg (R (R (handleCoercible other))))