Defining mathematical objects in Grace17

A definition in Grace17 is a conservative extension of the set theory it is made in. Each one introduces a new symbol together with a defining law that the kernel checks and records.

Mathematical objects come in a few kinds, and each has its own declaration:

Sets built by Separation and Replacement are written as terms: {x ∈ A | P(x)} is a bounded subset, and {term(x) | x ∈ A} is an image, whose functionality certificate the elaborator constructs and checks. An unbounded comprehension is never admitted as a set, which is what keeps the proper classes above honest.

Choose the mechanism#

Need Form What justifies it
A name for a term def f (...) : Set := term A checked defining equation
A property or relation predicate P (...) := formula A defining equivalence
A mathematical collection, possibly a proper class class C (...) := formula A predicate, also usable as x ∈ C
A bounded subset def S (...) : Set := {x ∈ A | P(x)} Separation over the existing set A
The image of a set def S (...) : Set := {term(x) | x ∈ A} Replacement with a checked functionality certificate
A function represented by a set of pairs defgraph A codomain proof and generated function/value laws
A uniquely determined value defunique Checked existence and uniqueness, using only ZF
A selected witness defchoose A proof of bounded existence; selection retains the hypotheses
Finite constructor data datatype Validated set encodings and inductive-definition laws
Executable structural recursion Equation-style def, or legacy define Checked clauses, coverage, and structural decrease
Recursion along ordinals noncomputable transfinite def Checked zero, successor, and limit schema
Recursion along membership noncomputable foundation def Foundation and a member-history graph
Recursion along a certified set relation noncomputable well-founded def A checked wellFoundedOn certificate and predecessor history

A def introduces a function symbol. It does not construct the set graph of that function. The distinction matters when a theorem expects FunctionBetween(f, A, B), or when functions are quantified over as sets.

A return annotation such as : Integer is a checked obligation. A def runs bounded type inference and publishes f.type, with the declaration's parameters and input type guards. For example:

grace17Try in Workbench
def chineseCombination (m n a b u v : Integer) : Integer := a * v * n + b * u * m
-- chineseCombination.type proves Integer(chineseCombination(m, n, a, b, u, v)).

When inference needs help, attach a proof block. Its goal is the declared result predicate applied to the defined function; input guards are available:

grace17Try in Workbench
predicate EqualZero (x : Set) := x = 0
def zeroValue (x : Set) : EqualZero := 0
proof
  unfold EqualZero
  unfold zeroValue
  rfl

Structural definitions also attempt a checked induction over their decreasing argument. Foundational definitions may establish closure later in the same module, including by an explicit theorem named f.type. Pending annotations supply no proof evidence, and the module fails if any obligation remains unproved. A hand-written f.type must have the declared guards and conclusion. Set is the kernel sort and needs no type theorem. A function whose stronger type needs extra hypotheses should take refined input binders, or return Set and come with a conditional closure theorem. Datatype predicates are not carrier sets: List(List(A)) needs a set of lists, which the datatype declaration does not construct. Executable Set-valued functions can still use exhaustive constructor patterns for structural recursion.

Function graphs: defgraph#

Here is an inclusion map, with its domain and codomain explicit:

grace17
defgraph inclusionMap (A B : Set) (x ∈ A) : B := x
requires subset : A ⊆ B
proof
  unfold Subset at subset
  exact subset with x, member

The parameterized result inclusionMap(A, B) is a set of ordered pairs. Inside the closure proof, member : x ∈ A is available, and the goal is x ∈ B. The binder variable x is the function input; it is not a parameter of the graph construction.

The generated interface includes:

Law Meaning
.closed The displayed value belongs to the codomain
.relates The input and its displayed value form an entry of the graph
.function The graph is a FunctionBetween the declared domain and codomain
.value Set-function application returns the displayed value

These laws retain requires hypotheses and any applicable input guard. For example, this checked use appears in examples/Definitions.g17:

grace17Try in Workbench
theorem inclusion_value (A B x : Set)
  requires subset : A ⊆ B
  requires member : x ∈ A
  shows inclusionMap(A, B)(x) = x
proof
  exact inclusionMap.value with subset, member

The graph is a bounded Replacement image. Each output is a term, so its functionality certificate can be constructed from equality and checked. A relation does not become functional by being declared.

The shorter form defgraph g (n : Nat) : Real := value also supports Nat/Real input and output combinations. It uses typecheck to prove output closure, without a user-written proof block. The bounded form supports explicit Set, Nat, and Real parameters and named, potentially multiline requires clauses. For domains ℕ and ℝ, the generated input laws can use the corresponding Nat/Real guard. A graph over an infinite domain is not an executable table, and these laws say nothing about behavior outside its domain.

Unique values: defunique#

Use this for a canonical value described by a bounded predicate. It needs no Choice axiom, including when the value is an equivalence class.

grace17
defunique identityValue (A : Set) (x ∈ {A}) := x = A
proof
  witness A
  refine ⟨singleton.self at A, rfl⟩
uniqueness
  intro x
  intro xMember
  intro xValue
  intro y
  intro yMember
  intro yValue
  cc

The proof block proves ∃ x ∈ A, P(x). The uniqueness block proves ∀ x ∈ A, P(x) → ∀ y ∈ A, P(y) → x = y, under the same requires clauses. Introduce the two values, their membership assumptions, and their properties in that order. The generated laws are .exists, .unique, and .selected. The last states that the defined value lies in A and satisfies P.

The implementation forms .candidates by Separation and defines the value as union(candidates). Existence and uniqueness make that set a singleton; the union of {z} is z. The generated selection proof is ZF reasoning, with no new kernel rule or axiom. The definition is total, but its selected law keeps the stated hypotheses.

Integer addition, multiplication, negation, their set-function graphs, and natural magnitude use this mechanism. Finite cardinality, set indicators, Cartesian coordinates, matrix indices, and diagonal coefficients do too. Use their laws or ring; there is no need to unfold the set construction.

Selected witnesses: defchoose#

grace17
defchoose selected (A : Set) (x ∈ A) := x ∈ A
requires inhabited : ∃ x, x ∈ A
proof
  obtain ⟨x, member⟩ := inhabited
  witness x
  refine ⟨member, member⟩

Import Grace.Cumulus.Set.Choice to use this mechanism. The general obligation is ∃ x ∈ A, P(x). The declaration constructs selected.candidates by bounded Separation and selects an element of that candidate set using the foundation's choice interface. It checks .exists and .selected; the latter provides both membership in the bound and the chosen property, under the declaration's hypotheses.

The generated candidate set is named selected.candidates. Its defining law characterizes membership. Unfolding it can expand a statement z ∈ selected.candidates(A), but does not replace a bare set term inside choose(selected.candidates(A)) with a term equation. Use .selected to reason about the chosen value.

This mechanism does not prove uniqueness, and it does not compute a witness. For a canonical mathematical value, prefer defunique and supply its uniqueness proof. The selection property depends on the existence hypothesis.

Predicates, classes, and bounded sets#

predicate FixedBy (f x : Set) := f(x) = x names a proposition. Soft-typed parameters are part of the predicate's extension: predicate P (x : Nat) := Q(x) means Nat(x) ∧ Q(x). Thus a proved P(x) can provide its domain information.

class Natural (x : Set) := Nat(x) makes collection notation available. It is a view of a predicate and makes no claim that the collection is a set. The mathematical class keyword has nothing to do with type-class registration.

Subset and image definitions are bounded by an existing set. They generate membership characterizations usable in proofs. The predicate in a subset may be undecidable; an arbitrary unbounded comprehension is not admitted as a set.

Datatypes and executable recursion#

A datatype introduces set-encoded constructors. Fields marked self are recursive; a field typed by a carrier A requires membership in A. Validated schema products include constructor closure, injectivity, disjointness, cases, and the induction interface. See datatypes.md for representations.

Equation-style def supplies constructor clauses. The checker verifies coverage, pattern linearity, arity, fixed parameters, and permitted structural recursive calls. This is the executable recursion interface used by compute; unrestricted self-reference is rejected. define is its legacy spelling. The complete examples include a two-parameter recursion with one fixed Set parameter and one decreasing natural argument.

Noncomputable recursion#

These forms define set constructions; none of them traverses an infinite set. "Noncomputable" does not mean admitted: the defining events and laws are validated and independently replayable.

The corresponding complete examples, including the certificate for membership restricted to a set, are in examples/Definitions.g17.

Declarations that organize reasoning#

notation changes how existing symbols are written; it adds no mathematical object or axiom. instance proves a theorem and registers it as candidate type-class evidence; registry search is untrusted. tactic declares a bounded-expansion macro built from existing tactics. These declaration keywords have contextual hovers too.

Bounded defgraph bodies may use infix expressions, for example defgraph arithmeticTerms (A D : Real) (n ∈ ℕ) : ℝ := A + (realNat(n):R) * D. The generated graph entry groups the entire value expression, preserving its precedence in both the definition and the generated membership proof.

Theorem sections#

A theorem section shares parameters and hypotheses without adding a namespace; each theorem in the section exports the shared variables as parameters and the shared hypotheses as premises. See modules, namespaces, and sections for nested contexts, exported signatures, applications, and a runnable example.

Scoped ring operations#

Inside a theorem section, write notation ring R := (A, add, zero, neg, mul, one) to use algebra syntax for those operation graphs. Several rings can coexist, with R[expression] picking one, and polynomial rings, quotients, and restrictions are named R[I], P ⧸ J, and R ↾ T. The declaration adds no hypotheses, axioms, or kernel operations. See ring scopes over separate pieces.