spectacle-1.0.0: src/Language/Spectacle/AST/Action.hs
module Language.Spectacle.AST.Action
( -- * Temporal Actions
type Action,
type ActionSyntax,
-- ** Interpreters
runAction,
runExceptionalAction,
rewriteLogic,
applyComplement,
introduceEnv,
)
where
import Data.Either (fromRight)
import Data.Function ((&))
import Data.Hashable (Hashable)
import Data.Kind (Type)
import Data.Set (Set)
import qualified Data.Set as Set
import GHC.TypeLits (Symbol)
import Data.Functor.Loom (hoist, runLoom, (~>~))
import Data.Type.Rec (Ascribe, Rec)
import Data.World (World, makeWorld)
import Language.Spectacle.Exception.RuntimeException
( RuntimeException,
)
import Language.Spectacle.Lang
( EffectK,
Lang (Op, Pure, Scoped),
Member (projectS),
Members,
Op (OHere, OThere),
Scoped (SHere, SThere),
runLang,
)
import Language.Spectacle.RTS.Registers (RuntimeState, emptyRuntimeState, newValues)
import Language.Spectacle.Syntax.Closure
( Closure,
runActionClosure,
)
import Language.Spectacle.Syntax.Env (Env, runEnv)
import Language.Spectacle.Syntax.Error (Error, runError)
import Language.Spectacle.Syntax.Logic
( Effect (Complement, Conjunct, Disjunct),
Logic,
complement,
conjunct,
disjunct,
runLogic,
)
import Language.Spectacle.Syntax.NonDet (NonDet, runNonDetA)
import Language.Spectacle.Syntax.Plain (Plain, runPlain)
import Language.Spectacle.Syntax.Quantifier
( Effect (Exists, Forall),
Quantifier,
exists,
forall,
runExceptionalQuantifier,
runQuantifier,
)
-- ---------------------------------------------------------------------------------------------------------------------
type Action :: [Ascribe Symbol Type] -> Type -> Type
type Action ctx = Lang ctx ActionSyntax
type ActionSyntax :: [EffectK]
type ActionSyntax =
-- NOTE: 'Closure' must be handled before 'Quantifier'. If 'Quantifier' discharged before 'Closure', erroneous values
-- are produced from any 'Closure' nested within a forall/exists.
'[ Logic
, Closure
, Quantifier
, Plain
, NonDet
, Error RuntimeException
]
-- | Completely evaluate a temporal action yielding either a 'RuntimeException' or a collection of new worlds accessible
-- by the action given.
--
-- @since 1.0.0
runExceptionalAction ::
forall ctxt.
Hashable (Rec ctxt) =>
Rec ctxt ->
Action ctxt Bool ->
Either RuntimeException (Set (World ctxt))
runExceptionalAction knowns action = do
states <-
action
& introduceEnv
& rewriteLogic
& runLogic
& runActionClosure
& runExceptionalQuantifier
& runEnv (emptyRuntimeState knowns)
& runPlain knowns
& runNonDetA
& runError
& runLang
return (takeRelatedSet states)
where
takeRelatedSet :: [(RuntimeState ctxt, Bool)] -> Set (World ctxt)
takeRelatedSet = foldMap \(rst, rel) ->
if rel
then Set.singleton (makeWorld (newValues rst))
else Set.empty
{-# INLINE runExceptionalAction #-}
runAction ::
forall ctxt.
Hashable (Rec ctxt) =>
Rec ctxt ->
Action ctxt Bool ->
Set (World ctxt)
runAction knowns action =
let states =
action
& introduceEnv
& rewriteLogic
& runLogic
& runActionClosure
& runQuantifier
& runEnv (emptyRuntimeState knowns)
& runPlain knowns
& runNonDetA
& runError
& runLang
& fromRight []
in takeRelatedSet states
where
takeRelatedSet :: [(RuntimeState ctxt, Bool)] -> Set (World ctxt)
takeRelatedSet = foldMap \(rst, rel) ->
if rel
then Set.singleton (makeWorld (newValues rst))
else Set.empty
{-# INLINE runAction #-}
-- | Traverses the effects in an action, rewriting all logical operators and quantifiers scoped within a negation.
--
-- @since 1.0.0
rewriteLogic :: Members '[Logic, Quantifier, NonDet] effs => Lang ctx effs Bool -> Lang ctx effs Bool
rewriteLogic = \case
Pure x -> pure x
Op op k -> Op op (rewriteLogic . k)
Scoped scoped loom -> case projectS scoped of
Nothing -> Scoped scoped loom'
Just eff
| Complement m <- eff -> runLoom (loom ~>~ hoist applyComplement ~>~ hoist rewriteLogic) m
| otherwise -> Scoped scoped loom'
where
loom' = loom ~>~ hoist rewriteLogic
{-# INLINE rewriteLogic #-}
-- | Reduces logical negation by applying the usual rewrite rules to quantifiers and other logical operators scoped
-- within the negation.
--
-- @since 1.0.0
applyComplement :: Members '[Logic, Quantifier, NonDet] effs => Lang ctx effs Bool -> Lang ctx effs Bool
applyComplement = \case
Pure x -> pure x
Op op k -> Op op (applyComplement . k)
Scoped scoped loom -> case projectS scoped of
Nothing -> case projectS scoped of
Nothing -> Scoped scoped loom'
-- ¬ (∀ x : A → p x) ≡ ∃ x : A → ¬ (p x)
Just (Forall xs p) -> exists xs (fmap not . runLoom loom' . p)
-- ¬ (∃ x : A → p x) ≡ ∀ x : A → ¬ (p x)
Just (Exists xs p) -> forall xs (fmap not . runLoom loom' . p)
-- ¬ ¬ p ≡ p
Just (Complement m) -> runLoom loom' (fmap not m)
-- ¬ (p ∧ q) ≡ ¬ p ∨ ¬ q
Just (Conjunct lhs rhs) ->
let lhs' = runLoom loom' lhs
rhs' = runLoom loom' rhs
in disjunct (complement lhs') (complement rhs')
-- ¬ (p ∨ q) ≡ ¬ p ∧ ¬ q
Just (Disjunct lhs rhs) ->
let lhs' = runLoom loom' lhs
rhs' = runLoom loom' rhs
in conjunct (complement lhs') (complement rhs')
where
loom' = loom ~>~ hoist applyComplement
{-# INLINE applyComplement #-}
-- | Introduces the variable environment to an 'Action' "underneath" the 'Closure' effect.
--
-- @since 1.0.0
introduceEnv ::
Lang ctx (Logic ': Closure ': Quantifier ': effs) a ->
Lang ctx (Logic ': Closure ': Quantifier ': Env ': effs) a
introduceEnv = \case
Pure x -> pure x
Op op k
| OHere op' <- op -> Op (OHere op') k'
| OThere (OHere op') <- op -> Op (OThere (OHere op')) k'
| OThere (OThere (OHere op')) <- op -> Op (OThere (OThere (OHere op'))) k'
| OThere (OThere (OThere op')) <- op -> Op (OThere (OThere (OThere (OThere op')))) k'
where
k' = introduceEnv . k
Scoped scoped loom
| SHere scoped' <- scoped -> Scoped (SHere scoped') loom'
| SThere (SHere scoped') <- scoped -> Scoped (SThere (SHere scoped')) loom'
| SThere (SThere (SHere scoped')) <- scoped -> Scoped (SThere (SThere (SHere scoped'))) loom'
| SThere (SThere (SThere scoped')) <- scoped -> Scoped (SThere (SThere (SThere (SThere scoped')))) loom'
where
loom' = loom ~>~ hoist introduceEnv