spectacle-1.0.0: src/Language/Spectacle/Syntax/Logic.hs
-- | Quantifiers and logic.
--
-- @since 1.0.0
module Language.Spectacle.Syntax.Logic
( Logic (Logic),
Effect (Complement, Conjunct, Disjunct),
complement,
conjunct,
disjunct,
implies,
iff,
runLogic,
)
where
import Control.Applicative (Alternative ((<|>)), Applicative (liftA2), empty)
import Data.Coerce (coerce)
import Data.Void (absurd)
import Data.Functor.Loom (hoist, runLoom, (~>~))
import Language.Spectacle.Exception.RuntimeException (RuntimeException)
import Language.Spectacle.Lang
( Lang (Op, Pure, Scoped),
Member,
Members,
decomposeOp,
decomposeS,
scope,
)
import Language.Spectacle.Syntax.Error (Error, catchE)
import Language.Spectacle.Syntax.Logic.Internal (Effect (Complement, Conjunct, Disjunct), Logic (Logic))
import Language.Spectacle.Syntax.NonDet (NonDet)
-- ---------------------------------------------------------------------------------------------------------------------
-- | Logical negation. The 'complement' operator is equivalent to 'not' for simple expressions, but
-- can be used to negate quantifiers and the other logical operators in spectacle.
--
-- @since 1.0.0
complement :: Member Logic effs => Lang ctx effs Bool -> Lang ctx effs Bool
complement m = scope (Complement m)
{-# INLINE complement #-}
-- | Boolean conjunction.
--
-- @since 1.0.0
conjunct :: Member Logic effs => Lang ctx effs Bool -> Lang ctx effs Bool -> Lang ctx effs Bool
conjunct m n = scope (Conjunct m n)
{-# INLINE conjunct #-}
-- | Boolean disjunction.
--
-- @since 1.0.0
disjunct :: Member Logic effs => Lang ctx effs Bool -> Lang ctx effs Bool -> Lang ctx effs Bool
disjunct m n = scope (Disjunct m n)
{-# INLINE disjunct #-}
-- | Logical implication.
--
-- @since 1.0.0
implies :: Member Logic effs => Lang ctx effs Bool -> Lang ctx effs Bool -> Lang ctx effs Bool
implies m n = disjunct (complement m) n
{-# INLINE implies #-}
-- | If and only if.
--
-- @since 1.0.0
iff :: Member Logic effs => Lang ctx effs Bool -> Lang ctx effs Bool -> Lang ctx effs Bool
iff m n = conjunct (implies m n) (implies n m)
{-# INLINE iff #-}
-- | Discharge a 'Logic' effect.
--
-- @since 1.0.0
runLogic ::
forall ctx effs.
Members '[Error RuntimeException, NonDet] effs =>
Lang ctx (Logic ': effs) Bool ->
Lang ctx effs Bool
runLogic = \case
Pure x -> pure x
Op op k -> case decomposeOp op of
Left other -> Op other (runLogic . k)
Right bottom -> absurd (coerce bottom)
Scoped scoped loom -> case decomposeS scoped of
Left other -> Scoped other loom'
Right (Complement m) -> runLoom loom' (fmap not m)
Right (Conjunct m n) -> liftA2 (&&) (runLoom loom' m) (runLoom loom' n)
Right (Disjunct m n) ->
(runLoom loom' m `catchE` \(_ :: RuntimeException) -> empty)
<|> (runLoom loom' n `catchE` \(_ :: RuntimeException) -> empty)
where
loom' = loom ~>~ hoist runLogic