spectacle-1.0.0: src/Language/Spectacle/Syntax/Quantifier.hs
module Language.Spectacle.Syntax.Quantifier
( Quantifier (Quantifier),
Effect (Forall, Exists),
forall,
exists,
runExceptionalQuantifier,
runQuantifier,
)
where
import Control.Applicative (Alternative (empty), Applicative (liftA2))
import Control.Monad (unless)
import Data.Bool (bool)
import Data.Coerce (coerce)
import Data.Foldable (Foldable (toList))
import Data.Void (absurd)
import Data.Functor.Loom (hoist, runLoom, (~>~))
import Language.Spectacle.Exception.RuntimeException
( QuantifierException (ExistsViolated, ForallViolated),
RuntimeException (QuantifierException),
)
import Language.Spectacle.Lang
( Effect,
Lang (Op, Pure, Scoped),
Members,
decomposeOp,
decomposeS,
)
import Language.Spectacle.Syntax.Error (Error, catchE, throwE)
import Language.Spectacle.Syntax.NonDet (NonDet, foldMapA, msplit, oneOf)
import Language.Spectacle.Syntax.Quantifier.Internal
( Effect (Exists, Forall),
Quantifier (Quantifier),
QuantifierIntro (forallIntro),
existsIntro,
)
-- ---------------------------------------------------------------------------------------------------------------------
-- | Universally quantify over some foldable container @f a@. A nondeterministically chosen element in @f a@ will be
-- returned so long as the given predicate is 'True' for all elements in the container, otherwise a spectacle exception
-- is raised.
--
-- @since 1.0.0
forall :: (Foldable f, QuantifierIntro m) => f a -> (a -> m Bool) -> m Bool
forall xs = forallIntro (toList xs)
{-# INLINE forall #-}
-- | Existential quantification over some foldable constainer @f a@. A nondeterministically chosen element in @f a@
-- which satisfies the given predicate will be returned. If there exists no element in the container that satisfies the
-- predicate then an exception is raised.
--
-- @since 1.0.0
exists :: (Foldable f, QuantifierIntro m) => f a -> (a -> m Bool) -> m Bool
exists xs = existsIntro (toList xs)
{-# INLINE exists #-}
runExceptionalQuantifier ::
Members '[Error RuntimeException, NonDet] effs =>
Lang ctx (Quantifier ': effs) Bool ->
Lang ctx effs Bool
runExceptionalQuantifier = \case
Pure x -> pure x
Op op k -> case decomposeOp op of
Left other -> Op other (runQuantifier . k)
Right bottom -> absurd (coerce bottom)
Scoped scoped loom -> case decomposeS scoped of
Left other -> Scoped other loom'
Right (Forall xs p) -> do
b <- oneOf xs >>= runLoom loom' . p
unless b (throwE (QuantifierException ForallViolated))
return b
Right (Exists [] _) -> throwE (QuantifierException ExistsViolated)
Right (Exists dom p) -> do
let m' = flip foldMapA dom \x -> do
b <- runLoom loom' (p x) `catchE` (\(_ :: RuntimeException) -> empty)
bool empty (pure b) b
msplit m' >>= \case
Just _ -> m'
Nothing -> throwE (QuantifierException ExistsViolated)
where
loom' = loom ~>~ hoist runQuantifier
{-# INLINE runExceptionalQuantifier #-}
runQuantifier :: Members '[NonDet] effs => Lang ctx (Quantifier ': effs) Bool -> Lang ctx effs Bool
runQuantifier = \case
Pure x -> pure x
Op op k -> case decomposeOp op of
Left other -> Op other (runQuantifier . k)
Right bottom -> absurd (coerce bottom)
Scoped scoped loom -> case decomposeS scoped of
Left other -> Scoped other loom'
Right (Forall xs p) -> foldr (liftA2 (&&) . runLoom loom' . p) (pure True) xs
Right (Exists xs p) -> foldr (liftA2 (||) . runLoom loom' . p) (pure False) xs
where
loom' = loom ~>~ hoist runQuantifier
{-# INLINE runQuantifier #-}