rzk-0.11.3: An experimental proof assistant for synthetic ∞-categories
Safe HaskellNone
LanguageHaskell2010

Rzk.TypeCheck.Eval

Description

Entering a scope, evaluation, and the tope solver.

These three are one recursive knot and cannot be separated:

  • entering a binder needs whnfT, to see whether a flat variable is a point of a cube and so brings a discreteness axiom with it;
  • whnfT strips an extension type's restrictions, which asks the solver whether a face's tope holds (checkTope);
  • nfT normalises under a binder and under a tope (localTope);
  • and the solver normalises the topes it reasons about (nfTope).
Synopsis

Variables

infoOfVar :: forall (n :: S) a. (VarInfo n -> a) -> Name n -> TypeCheck n a Source #

Look up a name and project one field of its VarInfo.

valueOfVar :: forall (n :: S). Name n -> TypeCheck n (Maybe (TermT n)) Source #

typeOfVar :: forall (n :: S). Name n -> TypeCheck n (TermT n) Source #

modalityOfVar :: forall (n :: S). Name n -> TypeCheck n TModality Source #

locksOfVar :: forall (n :: S). Name n -> TypeCheck n TModality Source #

isTopLevelVar :: forall (n :: S). Name n -> TypeCheck n Bool Source #

checkDefinedVar :: forall (n :: S). Distinct n => VarIdent -> TypeCheck n () Source #

Is a surface name defined?

typeOfUncomputed :: forall (n :: S). TermT n -> TypeCheck n (TermT n) Source #

typeOf :: forall (n :: S). Distinct n => TermT n -> TypeCheck n (TermT n) Source #

freeVarsDeep :: forall (n :: S). TermT n -> TypeCheck n [Name n] Source #

The free variables of a typed term, including those that occur only in the types of the variables it mentions.

A definition can depend on a section assumption without naming it: through the type of something else it uses. Closing a section has to see that dependency, and it is exactly what distinguishes an implicit assumption from an explicit one.

nubNames :: forall (n :: S). [Name n] -> [Name n] Source #

elemName :: forall (n :: S). Name n -> [Name n] -> Bool Source #

notElemName :: forall (n :: S). Name n -> [Name n] -> Bool Source #

Substitution, in the monad

instantiate :: forall (n :: S). Distinct n => ScopedTermT n -> TermT n -> TypeCheck n (TermT n) Source #

Instantiate a scoped term with an argument: the old substituteT.

Entering a binder

discreteAxiomOf :: forall (n :: S) (l :: S). (Distinct n, DExt n l) => TModality -> TermT n -> Maybe (TermT n) -> NameBinder n l -> TypeCheck n [ModalTope l] Source #

The discreteness axiom a flat cube variable brings with it: a flat point of 2 (or of I) is one of the endpoints. Maintained at binder entry so that entailment does not have to rescan the context on every query.

binderInfo :: forall (n :: S). Binder -> TModality -> TermT n -> Maybe (TermT n) -> Maybe LocationInfo -> VarInfo n Source #

What a binder adds to the context.

underBinder :: forall (n :: S) (l :: S) a. (Distinct n, DExt n l) => NameBinder n l -> Binder -> TModality -> TermT n -> Maybe (TermT n) -> TypeCheck l a -> TypeCheck n a Source #

Run an action under a binder that has already been chosen.

withBinder :: forall (n :: S) a. Distinct n => Binder -> TModality -> TermT n -> (forall (l :: S). (DExt n l, Distinct l) => NameBinder n l -> TypeCheck l a) -> TypeCheck n a Source #

Enter a fresh binder (one the checker invents) and run an action whose result says nothing about the new scope.

withFreshIn :: forall (n :: S) r. Distinct n => Scope n -> (forall (l :: S). (DExt n l, Distinct l) => NameBinder n l -> r) -> r Source #

underScope :: forall (n :: S). Distinct n => Binder -> TModality -> TermT n -> Maybe (TermT n) -> ScopedTermT n -> (forall (l :: S). (DExt n l, Distinct l) => TermT l -> TypeCheck l (TermT l)) -> TypeCheck n (ScopedTermT n) Source #

Open a scoped term under its own binder, run the action on the body, and pack the result back up as a scoped term.

underScope2 :: forall (n :: S). Distinct n => Binder -> TModality -> TermT n -> ScopedTermT n -> ScopedTermT n -> (forall (l :: S). (DExt n l, Distinct l) => TermT l -> TermT l -> TypeCheck l (TermT l, TermT l)) -> TypeCheck n (ScopedTermT n, ScopedTermT n) Source #

Like underScope, for a Π (or a λ over a shape), which binds a tope scope beside the body under what the user wrote as one binder.

inScope :: forall (sig :: Type -> Type -> Type) (n :: S) a. (Bifunctor sig, Distinct n) => Binder -> TModality -> TermT n -> ScopedAST NameBinder sig n -> (forall (l :: S). (DExt n l, Distinct l) => AST NameBinder sig l -> TypeCheck l a) -> TypeCheck n a Source #

Open a scoped term for a computation whose result says nothing about the new scope (a check, or a rendered string).

inScopeWith :: forall (sig :: Type -> Type -> Type) (n :: S) a. (Bifunctor sig, Distinct n) => Binder -> TModality -> TermT n -> Maybe (TermT n) -> ScopedAST NameBinder sig n -> (forall (l :: S). (DExt n l, Distinct l) => AST NameBinder sig l -> TypeCheck l a) -> TypeCheck n a Source #

Like inScope, for a binder that stands for a known value (a let).

openScoped :: forall (sig :: Type -> Type -> Type) (n :: S) (l :: S). (Bifunctor sig, DExt n l) => NameBinder n l -> ScopedAST NameBinder sig n -> TypeCheck l (AST NameBinder sig l) Source #

Open a scoped term with a binder that has just been entered.

The scoped term lives in the enclosing scope, and so may be the codomain of the type a λ is being checked against, or the tope of a shape: all of them are opened under the one binder the λ introduces.

constScope :: forall (n :: S). Distinct n => TermT n -> TypeCheck n (ScopedTermT n) Source #

A scope that does not use its binder: the codomain of a non-dependent function type, say.

elaborateUnder :: forall (n :: S). Distinct n => Binder -> TModality -> TermT n -> Maybe (TermT n) -> ScopedTerm n -> (forall (l :: S). (DExt n l, Distinct l) => NameBinder n l -> Term l -> TypeCheck l (TermT l)) -> TypeCheck n (ScopedTermT n) Source #

Enter the binder of an untyped scope — the body of a λ, or of a let, as the user wrote it — and elaborate it into a typed one.

The binder comes from the term being checked, and the scopes of the type it is checked against are opened under that same binder with openScoped.

checkUnderWith :: forall (n :: S) a. Distinct n => Binder -> TModality -> TermT n -> Maybe (TermT n) -> ScopedTerm n -> (forall (l :: S). (DExt n l, Distinct l) => NameBinder n l -> Term l -> TypeCheck l a) -> TypeCheck n a Source #

Enter the binder of an untyped scope and run a computation under it.

The result may not mention the new scope — but a ScopedAST hides its scope, so the continuation can pack whatever it built with the binder it was given and hand back as many scoped terms as it likes. That is how a λ returns its elaborated body, its shape tope and the type it turned out to have, all at once.

checkUnder :: forall (n :: S) a. Distinct n => Binder -> TModality -> TermT n -> ScopedTerm n -> (forall (l :: S). (DExt n l, Distinct l) => NameBinder n l -> Term l -> TypeCheck l a) -> TypeCheck n a Source #

Modalities

enterModality :: forall (n :: S) b. Distinct n => TModality -> TypeCheck n b -> TypeCheck n b Source #

The tope context

localTope :: forall (n :: S) a. Distinct n => TermT n -> TypeCheck n a -> TypeCheck n a Source #

Assume a tope for the enclosed action.

withRefreshedTopes :: forall (n :: S) a. Distinct n => (Context n -> Context n) -> TypeCheck n a -> TypeCheck n a Source #

Install a deferred saturation cache for the transformed context, and run the action with it.

The pipeline's effects are discharged purely into a thunk: installing costs nothing, holes recorded by the speculative run are discarded, and a pipeline error (a tope guard with a hole in lenient mode, say, which the per-query path would never have evaluated) becomes Nothing, so errors surface exactly where they did before.

inAllSubContexts :: forall (n :: S). Distinct n => TypeCheck n () -> TypeCheck n () -> TypeCheck n () Source #

Run a check in every alternative of a disjunctive tope context.

Equality of topes

eqModalTope :: forall (n :: S). Distinct n => ModalTope n -> ModalTope n -> Bool Source #

nubModalTopes :: forall (n :: S). Distinct n => [ModalTope n] -> [ModalTope n] Source #

elemModalTope :: forall (n :: S). Distinct n => ModalTope n -> [ModalTope n] -> Bool Source #

Entailment

allM :: Monad m => (a -> m Bool) -> [a] -> m Bool Source #

Monadic all that stops at the first failing element.

entailM :: forall (n :: S). Distinct n => [ModalTope n] -> TermT n -> TypeCheck n Bool Source #

saturateForEntailment :: forall (n :: S). Distinct n => [ModalTope n] -> TypeCheck n [[ModalTope n]] Source #

The preprocessing entailM does before searching: dedup, split off the context's disjunctions, and saturate each alternative. Depends only on the given topes (plus the discreteness axioms of the context), not on the goal.

entailSaturatedM :: forall (n :: S). Distinct n => [[ModalTope n]] -> TermT n -> TypeCheck n Bool Source #

Search each saturated alternative for the goal.

entailContextM :: forall (n :: S). Distinct n => TermT n -> TypeCheck n Bool Source #

Entailment against the context's own tope context, using the cached saturation when one was installed. Matching on the payload of SaturationCached is what forces the deferred pipeline, so the cost is paid at the first query under a context, and never for one that is never queried.

Saturation

saturateTopes :: forall (n :: S). Distinct n => [ModalTope n] -> [ModalTope n] Source #

saturateInv :: forall (n :: S). Distinct n => [ModalTope n] -> TypeCheck n [ModalTope n] Source #

saturateBottom :: forall (n :: S). Distinct n => [ModalTope n] -> [ModalTope n] Source #

Ex falso for BOT, lifted across modalities.

A contradiction in the topes that are genuinely available at the identity modality entails BOT, and BOT entails _μ BOT for every modality μ by the absurd rule (this holds for BOT specifically; a general tope φ does NOT give _μ φ, which would need the missing unit id ⇒ μ). Re-asserting _μ BOT at each lock μ where an available tope was hidden lets the contradiction survive the lock: _b BOT is accessible under a _b lock (coe Flat Flat), so mod _b recBOT in a vacuous context is accepted.

A tope counts as available at the identity modality when its variable modality coerces into Id: a _b-modal tope qualifies via the counit (coe Flat Id), but a _#-modal one does not (coe Sharp Id is False) — which is exactly why _# BOT does not leak to plain BOT.

saturateWith :: (a -> [a] -> Bool) -> ([a] -> [a] -> [a]) -> [a] -> [a] Source #

generateTopes :: forall (n :: S). Distinct n => [TermT n] -> [TermT n] -> [TermT n] Source #

isLatticePoint :: forall (n :: S). TermT n -> Bool Source #

Is this cube point a lattice term (a sup or inf)? Used to keep the lattice-specific solver work (equality-to-order generation, the antisymmetry fallback for equality goals) off the equality-heavy non-lattice fragment, where it would only cost time without proving anything new.

generateTopesForPointsM :: forall (n :: S). Distinct n => [TermT n] -> TypeCheck n [TermT n] Source #

allTopePoints :: forall (n :: S). Distinct n => TermT n -> [TermT n] Source #

topePoints :: forall (n :: S). TermT n -> [TermT n] Source #

subPoints :: forall (n :: S). TermT n -> [TermT n] Source #

Simplifying the left-hand side

simplifyLHSwithDisjunctions :: forall (n :: S). Distinct n => [ModalTope n] -> [[ModalTope n]] Source #

Simplify the context, including disjunctions.

Solving the right-hand side

solveRHSM :: forall (n :: S). Distinct n => [ModalTope n] -> TermT n -> TypeCheck n Bool Source #

solveRHS :: forall (n :: S). Distinct n => [TermT n] -> TermT n -> Bool Source #

applyModalityToTopes :: forall (n :: S). TModality -> [ModalTope n] -> [ModalTope n] Source #

Accumulate a modality over a list of topes.

partitionAccessible :: forall (n :: S). [ModalTope n] -> ([ModalTope n], [ModalTope n]) Source #

The checks the rest of the checker calls

checkTope :: forall (n :: S). Distinct n => TermT n -> TypeCheck n Bool Source #

checkTopeEntails :: forall (n :: S). Distinct n => TermT n -> TypeCheck n Bool Source #

checkEntails :: forall (n :: S). Distinct n => TermT n -> TermT n -> TypeCheck n Bool Source #

contextEntails :: forall (n :: S). Distinct n => TermT n -> TypeCheck n () Source #

contextEntailsBottom :: forall (n :: S). Distinct n => TypeCheck n Bool Source #

Is the local tope context contradictory (does it entail ⊥)?

topesEquiv :: forall (n :: S). Distinct n => TermT n -> TermT n -> TypeCheck n Bool Source #

contextEntailsUnion :: forall (n :: S). Distinct n => [TermT n] -> TypeCheck n () Source #

Check that the local tope context is included in (entails) the union of the given topes. This is the COVERAGE obligation of recOR: every point of the context must be covered by some branch guard.

Only coverage is required, not equivalence: branch guards may overhang the context (when splitting with an already-defined shape, say), so we do not require OR(guards) |- context.

checkTopeAgainstContext :: forall (n :: S). Distinct n => String -> TermT n -> TypeCheck n () Source #

Diagnose a recOR branch guard or a restriction face against the local tope context. There are three cases, by how the tope relates to the context:

  • DISJOINT — the tope and a consistent context have empty overlap (their conjunction is ⊥). The face or branch is then vacuous everywhere, so this is a hard error.
  • OVERHANG — the tope is not entailed by the context but still overlaps it. This is allowed and often intentional (splitting or restricting with an already-defined shape, whose faces live on the whole cube rather than being relativised to the context), so we only emit a non-fatal hint.
  • CONTAINED — the tope entails the context: nothing to report.

Restrictions and η

stripTypeRestrictions :: forall (n :: S). TermT n -> TermT n Source #

tryRestriction :: forall (n :: S). Distinct n => TermT n -> TypeCheck n (Maybe (TermT n)) Source #

The term a restriction face pins down, when one of the faces holds.

etaMatch :: forall (n :: S). Distinct n => Maybe (TermT n) -> TermT n -> TermT n -> TypeCheck n (TermT n, TermT n) Source #

Perform at most one η-expansion at the top level, to assist unification.

etaExpand :: forall (n :: S). Distinct n => TermT n -> TypeCheck n (TermT n) Source #

Layers

inCubeLayer :: forall (n :: S). Distinct n => TermT n -> TypeCheck n Bool Source #

inTopeLayer :: forall (n :: S). Distinct n => TermT n -> TypeCheck n Bool Source #

Weak head normal form

memoizeWHNF :: forall (n :: S). Distinct n => TermT n -> TypeCheck n (TermT n) Source #

Memoise a term's WHNF on its top node without reducing the term itself.

The returned term has the same (unreduced) structure, so free-variable and uses detection see exactly what the user wrote, while a later whnfT is O(1) via the cached form. Used when storing a definition's elaborated type and value, where an in-place reduction could otherwise discard or expose a variable occurrence.

whnfT :: forall (n :: S). Distinct n => TermT n -> TypeCheck n (TermT n) Source #

firstMatching :: forall (n :: S). Distinct n => [(TermT n, TermT n)] -> TypeCheck n (Maybe (TermT n)) Source #

The branch of a recOR (or the face of a restriction) whose guard holds.

Application, reducing a whole spine at once

collectAppSpine :: forall (n :: S). TermT n -> (TermT n, [(TypeInfo (TermT n), TermT n)]) Source #

The head of an application spine and its arguments, in application order, each paired with the type annotation of its AppT node (needed to rebuild a neutral application).

applySpine :: forall (n :: S). Distinct n => Scope n -> TermT n -> [(TypeInfo (TermT n), TermT n)] -> TypeCheck n (TermT n) Source #

Apply a function term to a spine of arguments, reducing.

tryDataElimStep Source #

Arguments

:: forall (n :: S). Distinct n 
=> TermT n

the head, in WHNF

-> [(TypeInfo (TermT n), TermT n)]

the collected spine arguments

-> TypeCheck n (Maybe (TermT n)) 

Try to fire a #data ι-rule on an application spine: the head is a generated eliminator, the scrutinee argument is headed by a fully applied constructor of the same datatype. Returns the method applied to the constructor's fields (and any leftover spine arguments), unreduced.

A non-Var head answers Nothing immediately, so the common neutral spine pays one pattern match; a Var head pays one lookupVarInfo.

applyTyped :: forall (n :: S). Distinct n => TermT n -> [TermT n] -> TypeCheck n (TermT n) Source #

Apply a term to arguments left to right, annotating each application node with its actual type. The spine machinery reuses the annotations of existing nodes, which a freshly built ι-redex does not have.

peelLambdas :: forall (i :: S) (n :: S). Distinct n => Scope n -> Substitution TermT i n -> TermT i -> [(TypeInfo (TermT n), TermT n)] -> TypeCheck n (TermT n) Source #

Peel the head's syntactic lambda chain into one substitution, then reduce. subst maps the binders consumed so far to their arguments; body is the current lambda's body, at the scope those binders extended into.

applyNeutral :: forall (n :: S). Distinct n => Scope n -> TermT n -> [(TypeInfo (TermT n), TermT n)] -> TypeCheck n (TermT n) Source #

Apply a non-lambda (already WHNF) function to a spine, one argument at a time: this is the type-directed part of application, unchanged from before.

applyWhnfFun :: forall (n :: S). Distinct n => TypeInfo (TermT n) -> TermT n -> TermT n -> TypeCheck n (TermT n) Source #

Apply a non-lambda function f' (already WHNF) to one argument x. A shape-restricted function contributes a tope side-condition; a function whose return type is restricted refines the application's type; everything else is a neutral application. Extracted verbatim from the old single-argument AppT case.

Normal form of the tope layer

nfSupT :: forall (n :: S). TermT n -> TermT n -> TermT n -> TermT n Source #

nfInfT :: forall (n :: S). TermT n -> TermT n -> TermT n -> TermT n Source #

nfTope :: forall (n :: S). Distinct n => TermT n -> TypeCheck n (TermT n) Source #

Normal form

nfT :: forall (n :: S). Distinct n => TermT n -> TypeCheck n (TermT n) Source #