content: module AlternativeSpec where
  
  import Polysemy
  import Polysemy.NonDet
  import Test.Hspec
  import Control.Applicative
  import Polysemy.Trace
  import Polysemy.Reader
  
  runAlt :: Alternative f => Sem '[NonDet] a -> f a
  runAlt = run . runNonDet
  
  failtrace :: (Member NonDet r, Member Trace r)
            => Sem r ()
  failtrace = pure () <|> trace "trace"
  
  failtrace' :: (Member NonDet r, Member Trace r)
             => Sem r ()
  failtrace' = trace "sim" *> empty <|> trace "salabim"
  
  inHigherOrder :: Members '[NonDet, Trace, Reader ()] r
                => Sem r ()
  inHigherOrder = do
    local (\_ -> ()) $ trace "1" <|> trace "2"
    trace "3"
  
  spec :: Spec
  spec = parallel $ do
    describe "Alternative instance" $ do
      it "should choose the first branch" $ do
        runAlt (pure '1' <|> pure '2') `shouldBe` (Just '1')
      it "should failover" $ do
        runAlt (empty <|> pure '2') `shouldBe` (Just '2')
        runAlt (pure '1' <|> empty) `shouldBe` (Just '1')
  
    describe "runNonDetMaybe" $ do
      it "should skip second branch if the first branch succeeds" $ do
        (run . runNonDetMaybe . runTraceList) failtrace
          `shouldBe` Just ([], ())
        (run . runTraceList . runNonDetMaybe) failtrace
          `shouldBe` ([], Just ())
  
      it "should respect local/global state semantics" $ do
        (run . runNonDetMaybe . runTraceList) failtrace'
          `shouldBe` Just (["salabim"], ())
        (run . runTraceList . runNonDetMaybe) failtrace'
          `shouldBe` (["sim", "salabim"], Just ())
  
    describe "runNonDet" $ do
      it "should have terrible semantics when <|> is used within\
         \ a higher-order action of a later effect (See Issue #246.)" $ do
        (fst . run . runTraceList . runReader () . runNonDet @[]) inHigherOrder
          `shouldBe` ["1","2","3","3"]
        (fst . run . runTraceList . runReader () . runNonDet @[]) inHigherOrder
          `shouldNotBe` ["1","3","2","3"] -- This is what we actually WANT it to be.
        (fst . run . runTraceList . runNonDet @[] . runReader ()) inHigherOrder
          `shouldBe` ["1","3","2","3"]
  