{-# LANGUAGE AllowAmbiguousTypes #-} {-# LANGUAGE MultiParamTypeClasses #-} module AmbiguousSpec where import Control.Monad.IO.Class (liftIO) import Data.Functor.Identity import Data.Monoid import Polysemy import Polysemy.Embed (runEmbedded) import Polysemy.Error (Error, runError) import Polysemy.State import Polysemy.Tagged (Tagged, tag) import Test.Hspec import Test.ShouldNotTypecheck class MPTC a b where mptc :: a -> b instance MPTC Bool Int where mptc _ = 1000 uniquelyInt :: Members '[State Int , State String] r => Sem r () uniquelyInt = put 10 uniquelyA :: forall a b r. (Num a, Members '[State a, State b] r) => Sem r () uniquelyA = put 10 uniquelyString :: Members '[State Int , State String] r => Sem r () uniquelyString = put mempty uniquelyB :: (MPTC Bool b, Members '[State String, State b] r) => Sem r () uniquelyB = put $ mptc False uniquelyIO :: Members '[Embed IO, Embed Identity] r => Sem r () uniquelyIO = embed $ liftIO $ pure () uniquelyState' :: ∀ r . Members [Error (), State ()] r => Sem r () uniquelyState' = pure () uniquelyState :: ∀ r . Member (Tagged () (State ())) r => Sem r (Either () ()) uniquelyState = runError (tag @() uniquelyState') -- uniquelyState = runError (tag @() (uniquelyState' @(State () : Error () : r))) spec :: Spec spec = describe "example" $ do it "should run uniquelyInt" $ do let z = run . runState 0 . runState "hello" $ uniquelyInt z `shouldBe` (10, ("hello", ())) it "should run uniquelyA" $ do let z = run . runState 0 . runState "hello" $ uniquelyA @Int @String z `shouldBe` (10, ("hello", ())) it "should run uniquelyB" $ do let z = run . runState 0 . runState "hello" $ uniquelyB @Int z `shouldBe` (1000, ("hello", ())) it "should run uniquelyString" $ do let z = run . runState 0 . runState "hello" $ uniquelyString z `shouldBe` (0, ("", ())) it "should run uniquelyIO" $ do z <- runM . runEmbedded @Identity (pure . runIdentity) $ uniquelyIO z `shouldBe` ()