Skip to content

Commit

Permalink
Make NonDet Higher-Order (#174)
Browse files Browse the repository at this point in the history
* Higher-Order NonDet and runNonDetMaybe

* Fixed docs

* Fixed a word in AlternativeSpec docs

* Fixed tests and wrong documentation
  • Loading branch information
KingoftheHomeless authored and isovector committed Jul 15, 2019
1 parent 48b6768 commit d12adcd
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 14 deletions.
5 changes: 1 addition & 4 deletions src/Polysemy/Internal.hs
Original file line number Diff line number Diff line change
Expand Up @@ -211,10 +211,7 @@ instance Monad (Sem f) where
instance (Member NonDet r) => Alternative (Sem r) where
empty = send Empty
{-# INLINE empty #-}
a <|> b = do
send (Choose id) >>= \case
False -> a
True -> b
a <|> b = send (Choose a b)
{-# INLINE (<|>) #-}

-- | @since 0.2.1.0
Expand Down
6 changes: 2 additions & 4 deletions src/Polysemy/Internal/NonDet.hs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,10 @@

module Polysemy.Internal.NonDet where

import Data.Kind


------------------------------------------------------------------------------
-- | An effect corresponding to the 'Control.Applicative.Alternative' typeclass.
data NonDet (m :: Type -> Type) a
data NonDet m a
= Empty
| Choose (Bool -> a)
| Choose (m a) (m a)

37 changes: 32 additions & 5 deletions src/Polysemy/NonDet.hs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ module Polysemy.NonDet

-- * Interpretations
, runNonDet
, runNonDetMaybe
) where

import Control.Applicative
import Control.Monad.Trans.Maybe
import Data.Maybe
import Polysemy.Internal
import Polysemy.Internal.NonDet
Expand Down Expand Up @@ -55,17 +57,42 @@ instance Monad (NonDetC m) where
------------------------------------------------------------------------------
-- | Run a 'NonDet' effect in terms of some underlying 'Alternative' @f@.
runNonDet :: Alternative f => Sem (NonDet ': r) a -> Sem r (f a)
runNonDet (Sem m) = Sem $ \k -> runNonDetC $ m $ \u ->
runNonDet = runNonDetC . runNonDetInC
{-# INLINE runNonDet #-}

runNonDetInC :: Sem (NonDet ': r) a -> NonDetC (Sem r) a
runNonDetInC = usingSem $ \u ->
case decomp u of
Left x -> NonDetC $ \cons nil -> do
z <- k $ weave [()]
z <- liftSem $ weave [()]
(fmap concat . traverse runNonDet)
-- TODO(sandy): Is this the right semantics?
listToMaybe
x
foldr cons nil z
Right (Weaving Empty _ _ _ _) -> empty
Right (Weaving (Choose ek) s _ y _) -> do
z <- pure (ek False) <|> pure (ek True)
pure $ y $ z <$ s
Right (Weaving (Choose left right) s wv ex _) -> fmap ex $
runNonDetInC (wv (left <$ s)) <|> runNonDetInC (wv (right <$ s))
{-# INLINE runNonDetInC #-}

------------------------------------------------------------------------------
-- | Run a 'NonDet' effect in terms of an underlying 'Maybe'
--
-- Unlike 'runNonDet', uses of '<|>' will not execute the
-- second branch at all if the first option succeeds.
runNonDetMaybe :: Sem (NonDet ': r) a -> Sem r (Maybe a)
runNonDetMaybe (Sem sem) = Sem $ \k -> runMaybeT $ sem $ \u ->
case decomp u of
Right (Weaving e s wv ex _) ->
case e of
Empty -> empty
Choose left right ->
MaybeT $ usingSem k $ runMaybeT $ fmap ex $ do
MaybeT (runNonDetMaybe (wv (left <$ s)))
<|> MaybeT (runNonDetMaybe (wv (right <$ s)))
Left x -> MaybeT $
k $ weave (Just ())
(maybe (pure Nothing) runNonDetMaybe)
id
x
{-# INLINE runNonDetMaybe #-}
21 changes: 20 additions & 1 deletion test/AlternativeSpec.hs
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,23 @@ import Polysemy
import Polysemy.NonDet
import Test.Hspec
import Control.Applicative
import Polysemy.Trace

semFail :: Member NonDet r => Maybe Bool -> Sem r Bool
semFail mb = do
Just b <- pure mb
pure b


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"

spec :: Spec
spec = parallel $ do
Expand All @@ -32,3 +39,15 @@ spec = parallel $ do
runAlt (semFail $ Just True) `shouldBe` Just True
runAlt (semFail $ Just False) `shouldBe` [False]

describe "runNonDetMaybe" $ do
it "should skip second branch if the first branch succeeds" $ do
(run . runNonDetMaybe . runTraceAsList) failtrace
`shouldBe` Just ([], ())
(run . runTraceAsList . runNonDetMaybe) failtrace
`shouldBe` ([], Just ())

it "should respect local/global state semantics" $ do
(run . runNonDetMaybe . runTraceAsList) failtrace'
`shouldBe` Just (["salabim"], ())
(run . runTraceAsList . runNonDetMaybe) failtrace'
`shouldBe` (["sim", "salabim"], Just ())

0 comments on commit d12adcd

Please sign in to comment.