{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE OverloadedStrings #-}
module PluginSpec where
import Data.Functor.Identity
import GHC.Exts
import Control.Effect
import Control.Effect.Reader
import Control.Effect.Error
import Control.Effect.State
import Control.Effect.Writer
import Control.Monad
import Test.Hspec
data Count :: Effect where
Increment :: Count m ()
type CountC i = CompositionC
'[ ReinterpretSimpleC Count '[Throw (), State Int]
, ThrowC ()
, StateC Int
]
runCount :: ( Carrier m
, Threaders '[ReaderThreads, ErrorThreads, StateThreads] m p
)
=> CountC i m a
-> m (Int, Maybe a)
runCount =
runState 0
. fmap (either (const Nothing) Just)
. runThrow
. reinterpretSimple (\case
Increment -> do
i <- get
when (i >= 10) $ throw ()
put $! i + 1
)
. runComposition
idState :: Eff (State s) m => m ()
idState = get >>= put
intState :: Eff (State Int) m => m ()
intState = put 10
numState :: Num a => Eff (State a) m => m ()
numState = put 10
strState :: Eff (State String) m => m ()
strState = put "hello"
oStrState :: IsString a => Eff (State a) m => m ()
oStrState = put "hello"
err :: Eff (Error e) m => m Bool
err =
catch
(throw undefined)
(\_ -> pure True)
errState :: Num s => Effs '[Error e, State s] m => m Bool
errState = do
numState
err
lifted :: Monad b => Eff (Embed b) m => m ()
lifted = embed $ pure ()
newtype MyString = MyString String
deriving (IsString, Eq, Show)
spec :: Spec
spec = do
describe "State effect" $ do
describe "get/put" $ do
it "should work in simple cases" $ do
flipShouldBe (True, ()) . run $ runState True idState
it "should, when polymorphic, eliminate the first matching effect" $ do
flipShouldBe (False, (True, ())) . run $ runState False $ runState True idState
it "should, when polymorphic, not eliminate unmatching effects" $ do
flipShouldBe (True, Right @Int ()) . run $ runState True $ runError idState
describe "numbers" $ do
it "should interpret against concrete Int" $ do
flipShouldBe (10, ()) . run $ runState 0 intState
describe "polymorphic Num constraint" $ do
it "should interpret against Int" $ do
flipShouldBe (10 :: Int, ()) . run $ runState 0 numState
it "should interpret against Float" $ do
flipShouldBe (10 :: Float, ()) . run $ runState 0 numState
it "should interpret against Double" $ do
flipShouldBe (10 :: Double, ()) . run $ runState 0 numState
it "should interpret against Integer" $ do
flipShouldBe (10 :: Integer, ()) . run $ runState 0 numState
describe "strings" $ do
it "concrete interpret against concrete String" $ do
flipShouldBe ("hello", ()) . run $ runState "nothing" strState
describe "polymorphic IsString constraint" $ do
it "should interpret against String" $ do
flipShouldBe ("hello" :: String, ()) . run $ runState "nothing" oStrState
it "should interpret against MyString" $ do
flipShouldBe ("hello" :: MyString, ()) . run $ runState "nothing" oStrState
describe "Error effect" $ do
it "should interpret against Int" $ do
flipShouldBe (Right @Int True) . run $ runError err
it "should interpret against Bool" $ do
flipShouldBe (Right @Bool True) . run $ runError err
describe "State/Error effect" $ do
it "should interpret against Int/String" $ do
flipShouldBe (10 :: Int, Right @String True) . run $ runState 0 $ runError errState
it "should interpret against Float/Bool" $ do
flipShouldBe (10 :: Float, Right @Bool True) . run $ runState 0 $ runError errState
describe "Error/State effect" $ do
it "should interpret against String/Int" $ do
flipShouldBe (Right @String (10 :: Int, True)) . run $ runError $ runState 0 errState
it "should interpret against Bool/Float" $ do
flipShouldBe (Right @Bool (10 :: Float, True)) . run $ runError $ runState 0 errState
describe "Tell effect" $ do
it "should unify recursively with tyvars" $ do
flipShouldBe 11 . sum . fst . run . runTell $ do
tell [1]
tell $ replicate 2 5
describe "Embed effect" $ do
it "should interpret against IO" $ do
res <- runM lifted
res `shouldBe` ()
it "should interpret against Identity" $ do
let res = runM lifted
res `shouldBe` Identity ()
flipShouldBe :: (Show a, Eq a) => a -> a -> Expectation
flipShouldBe = flip shouldBe