packages feed

heftia-effects-0.6.0.0: test/Test/Semantics.hs

{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE TemplateHaskell #-}

-- SPDX-License-Identifier: MPL-2.0

module Test.Semantics where

import Control.Applicative ((<|>))
import Control.Effect (type (~>))
import Control.Monad.Hefty (
    Eff,
    Effect,
    interpret,
    runPure,
    (:>),
 )
import Control.Monad.Hefty.Except (runCatch, runThrow)
import Control.Monad.Hefty.NonDet (runChooseH, runNonDet)
import Control.Monad.Hefty.State (evalState)
import Control.Monad.Hefty.Writer (runTell, runWriterHPre)
import Data.Effect.Except (Catch, Throw, catch, throw)
import Data.Effect.NonDet (ChooseH, Empty)
import Data.Effect.State (State, get, put)
import Data.Effect.TH (makeEffectF)
import Data.Effect.Writer (Tell, WriterH, listen, tell)
import Data.Functor (($>))
import Data.Monoid (Sum (Sum))
import Test.Hspec (Spec, describe, it, shouldBe)

spec_State_Except :: Spec
spec_State_Except = describe "State & Except semantics" do
    let action :: (State Bool :> es, Throw () :> es, Catch () :> es) => Eff es Bool
        action = do
            (put True *> throw ()) `catch` \() -> pure ()
            get

    it "evalState . runThrow  $ (put True *> throw) `catch` ()  ==>  Right True" do
        runPure (evalState False . runThrow @() . runCatch @() $ action) `shouldBe` Right True
    it "runThrow  . evalState $ (put True *> throw) `catch` ()  ==>  Right True" do
        runPure (runThrow @() . evalState False . runCatch @() $ action) `shouldBe` Right True

spec_NonDet_Except :: Spec
spec_NonDet_Except = describe "NonDet & Except semantics" do
    let action1
            , action2
                :: (Empty :> es, ChooseH :> es, Throw () :> es, Catch () :> es) => Eff es Bool
        action1 = (pure True <|> throw ()) `catch` \() -> pure False
        action2 = (throw () <|> pure True) `catch` \() -> pure False

    it "runNonDet . runThrow  $ (True <|> throw) `catch` False  ==>  [Right True, Right False]" do
        runPure (runNonDet @[] . runThrow @() . runCatch @() . runChooseH $ action1) `shouldBe` [Right True, Right False]
    it "runThrow  . runNonDet $ (True <|> throw) `catch` False  ==>  Right [True, False]" do
        runPure (runThrow @() . runNonDet @[] . runCatch @() . runChooseH $ action1) `shouldBe` Right [True, False]
    it "runNonDet . runThrow  $ (throw <|> True) `catch` False  ==>  [Right False, Right False]" do
        runPure (runNonDet @[] . runThrow @() . runCatch @() . runChooseH $ action2) `shouldBe` [Right False, Right True]
    it "runThrow  . runNonDet $ (throw <|> True) `catch` False  ==>  Right [False, True]" do
        runPure (runThrow @() . runNonDet @[] . runCatch @() . runChooseH $ action2) `shouldBe` Right [False, True]

spec_NonDet_Writer :: Spec
spec_NonDet_Writer = describe "NonDet & Writer semantics" do
    let action
            :: (Empty :> es, ChooseH :> es, Tell (Sum Int) :> es, WriterH (Sum Int) :> es)
            => Eff es (Sum Int, Bool)
        action = listen $ add 1 *> (add 2 $> True <|> add 3 $> False)
          where
            add = tell . Sum @Int

    it "runNonDet . runTell   $ listen $ add 1 *> (add 2 $> True <|> add 3 $> False)  ==>  [(3, (3, True)), (4, (4, False))]" do
        runPure (runNonDet @[] . runTell @(Sum Int) . runWriterHPre @(Sum Int) . runChooseH $ action) `shouldBe` [(3, (3, True)), (4, (4, False))]
    it "runTell   . runNonDet $ listen $ add 1 *> (add 2 $> True <|> add 3 $> False)  ==>  (6, [(3, True), (4, False)])" do
        runPure (runTell @(Sum Int) . runNonDet @[] . runWriterHPre @(Sum Int) . runChooseH $ action) `shouldBe` (6, [(3, True), (4, False)])

data SomeEff :: Effect where
    SomeAction :: SomeEff f String
makeEffectF ''SomeEff

spec_The_issue_12 :: Spec
spec_The_issue_12 = describe "hasura/eff#12 semantics" do
    let action :: (Catch String :> es, SomeEff :> es) => Eff es String
        action = someAction `catch` \(_ :: String) -> pure "caught"

        runSomeEff :: (Throw String :> es) => Eff (SomeEff ': es) ~> Eff es
        runSomeEff = interpret (\SomeAction -> throw "not caught")

    it "runCatch . interpret (\\SomeAction -> throw \"not caught\") $ someAction `catch` \"caught\"  ==>  Right \"caught\"" do
        runPure (runThrow . runCatch . runSomeEff $ action) `shouldBe` Right "caught"
    it "interpret (\\SomeAction -> throw \"not caught\") . runCatch $ someAction `catch` \"caught\"  ==>  Left \"not caught\"" do
        runPure (runThrow . runSomeEff . runCatch $ action) `shouldBe` Left "not caught"