README

#Logic

The logical core of HOL: the trusted kernel, Boolean connectives, and derived equality rules.

Depends on: foundation (for Subst), types (for Type, TypeSubst), terms (for Term, TermSubst)

#Architecture

This package contains three layers:

NamespaceFileRole
Kernelkernel.mbtLCF kernel: abstract Thm type + 10 primitive inference rules
BoolSyntaxbool_syntax.mbtConnectives and quantifiers defined from equality
Equalequal.mbtDerived equality rules (sym, trans, apTerm, ...)

#Kernel (LCF Architecture)

The kernel implements the LCF (Logic for Computable Functions) architecture: the type Thm can only be constructed through the inference rules below. Because MoonBit enforces module boundaries, no outside code can fabricate a Thm value -- every theorem is the result of a valid chain of inferences.

///|
struct Thm {
hyps : Array[Term] // hypotheses (assumptions)
concl : Term // conclusion
}

A theorem { hyps: [h1, ..., hn], concl: phi } asserts: under hypotheses h1, ..., hn, the conclusion phi holds.

#Primitive Inference Rules

refl t |- t = t assume phi phi |- phi eqMp (A1 |- phi = psi) (A2 |- phi) A1 U A2 |- psi absThm v (A |- t = u) A |- (\v. t) = (\v. u) appThm (A1 |- f = g) (A2 |- x = y) A1 U A2 |- f x = g y deductAntisym (A1 |- phi) (A2 |- psi) (A1\{psi}) U (A2\{phi}) |- phi = psi termSubst sigma (A |- phi) A[sigma] |- phi[sigma] typeSubst sigma (A |- phi) A[sigma] |- phi[sigma] betaConv (\v. t) u |- (\v. t) u = t[u/v] defineConst c t |- c = t

Plus defineTypeOp (type definition principle) and new_axiom (adds an axiom -- use sparingly!).

#Examples

///|
test "kernel: refl, assume, eqMp" {
@types.Type::reset_table()
@terms.Term::reset_table()
let p = @terms.mk_var("p", @types.bool_ty())

// |- p = p
let th_eq = @logic.Kernel::refl(p)
assert_eq(th_eq.hyps.length(), 0)

// p |- p
let th_p = @logic.Kernel::assume_(p)
assert_eq(th_p.hyps.length(), 1)

// eqMp (|- p = p) (p |- p) => p |- p
let th = @logic.Kernel::eqMp(th_eq, th_p)
assert_true(th.concl == p)
}

///|
test "kernel: betaConv reduces a lambda application" {
@types.Type::reset_table()
@terms.Term::reset_table()
let a = @types.mk_var("'a")
let x = @terms.mk_var("x", a)
let y = @terms.mk_var("y", a)

// |- (\x. x) y = y
let th = @logic.Kernel::betaConv(@terms.Term::mk_abs(x, x), y)
let (_, rhs) = @terms.Term::dest_eq(th.concl)
assert_true(rhs == y)
}

///|
test "kernel: defineConst registers a constant" {
@types.Type::reset_table()
@terms.Term::reset_table()
let a = @types.mk_var("'a")
let x = @terms.mk_var("x", a)
let body = @terms.Term::mk_abs(x, x)

// |- my_id = (\x. x)
let th = @logic.Kernel::defineConst("my_id", body)
assert_eq(th.hyps.length(), 0)
let (lhs, rhs) = @terms.Term::dest_eq(th.concl)
assert_true(lhs is Const(_, _))
assert_true(rhs.aconv(body))
}

#BoolSyntax (Connectives and Quantifiers)

In the HOL Light approach, all connectives and quantifiers are defined in terms of the single primitive = : 'a -> 'a -> bool:

ConnectiveDefinition
T(\p. p) = (\p. p)
/\\p q. (\f. f p q) = (\f. f T T)
==>\p q. (p /\ q) = p
forall\P. P = (\x. T)
exist\P. forall q. (forall x. P x ==> q) ==> q
\/\p q. forall r. (p ==> r) ==> (q ==> r) ==> r
Fforall p. p
~\p. p ==> F

Each connective has a uniform triple of functions:

  • mk_* -- builds the formula
  • dest_* -- takes it apart
  • is_* -- tests whether a term has that form

///|
test "BoolSyntax: implication and negation" {
@types.Type::reset_table()
@terms.Term::reset_table()
let p = @terms.mk_var("p", @types.bool_ty())
let q = @terms.mk_var("q", @types.bool_ty())

// Build p ==> q
let imp = @logic.BoolSyntax::mk_imp(p, q)
assert_true(@logic.BoolSyntax::is_imp(imp))
let (ant, conseq) = @logic.BoolSyntax::dest_imp(imp)
assert_true(ant == p)
assert_true(conseq == q)

// Build ~p, then dest_neg returns the inner term
let neg = @logic.BoolSyntax::mk_neg(p)
assert_true(@logic.BoolSyntax::is_neg(neg))
assert_true(@logic.BoolSyntax::dest_neg(neg) == p)
}

///|
test "BoolSyntax: quantifiers are applied to abstractions" {
@types.Type::reset_table()
@terms.Term::reset_table()
let bool_ty = @types.bool_ty()
let x = @terms.mk_var("x", bool_ty)
let p = @terms.mk_var("p", bool_ty)

// forall x. x ==> p is App(forall_inst, Abs(x, x ==> p))
let body = @logic.BoolSyntax::mk_imp(x, p)
let fa = @logic.BoolSyntax::mk_forall(x, body)
assert_true(fa is App(_, _))
assert_true(fa.type_of() == bool_ty)

// exist x. x ==> p
let ex = @logic.BoolSyntax::mk_exists(x, body)
assert_true(ex is App(_, _))
}

#Equal (Derived Equality Rules)

Derived inference rules built entirely from kernel primitives. Because they compose only sound primitives, they are sound by construction -- no additional trust is needed beyond the kernel.

RuleGivenConclusion
symA \|- s = tA \|- t = s
transA1 \|- s = t, A2 \|- t = uA1 U A2 \|- s = u
apTermf, A \|- x = yA \|- f x = f y
apThmx, A \|- f = gA \|- f x = g x
mkBinopop, A1 \|- l1 = l2, A2 \|- r1 = r2A1 U A2 \|- op l1 r1 = op l2 r2
alphat1, t2 (alpha-convertible)\|- t1 = t2

///|
test "Equal: sym and trans" {
@types.Type::reset_table()
@terms.Term::reset_table()
let a = @types.mk_var("'a")
let x = @terms.mk_var("x", a)
let y = @terms.mk_var("y", a)
let z = @terms.mk_var("z", a)

// x = y |- x = y
let th_xy = @logic.Kernel::assume_(@terms.Term::mk_eq(x, y))

// sym: x = y |- y = x
let sym_th = @logic.Equal::sym(th_xy)
let (lhs, rhs) = @terms.Term::dest_eq(sym_th.concl)
assert_true(lhs == y)
assert_true(rhs == x)

// trans: {x = y, y = z} |- x = z
let th_yz = @logic.Kernel::assume_(@terms.Term::mk_eq(y, z))
let trans_th = @logic.Equal::trans(th_xy, th_yz)
let (tl, tr) = @terms.Term::dest_eq(trans_th.concl)
assert_true(tl == x)
assert_true(tr == z)
}

///|
test "Equal: alpha produces a theorem from alpha-convertible terms" {
@types.Type::reset_table()
@terms.Term::reset_table()
let a = @types.mk_var("'a")
let x = @terms.mk_var("x", a)
let y = @terms.mk_var("y", a)

// \x. x and \y. y are alpha-equivalent
let t1 = @terms.Term::mk_abs(x, x)
let t2 = @terms.Term::mk_abs(y, y)

// |- (\x. x) = (\y. y)
let th = @logic.Equal::alpha(t1, t2)
assert_eq(th.hyps.length(), 0)
assert_true(@terms.Term::is_eq(th.concl))
}

#
BoolSyntax

pub enum BoolSyntax {
}

Namespace for Boolean connective and quantifier operations in HOL.

#
BoolSyntax::conjunction

Return the conjunction constant /\ : bool -> bool -> bool.

#
BoolSyntax::dest_conj

Decompose a conjunction p /\ q into its two conjuncts.

#
BoolSyntax::dest_disj

Decompose a disjunction p \/ q into its two disjuncts.

#
BoolSyntax::dest_eq

Decompose an equation l = r into its left and right sides.

#
BoolSyntax::dest_ex_unique

Decompose a unique existence ?! x. body into the bound variable and body.

#
BoolSyntax::dest_exists

Decompose an existential quantification exist x. body into the bound variable and body.

#
BoolSyntax::dest_forall

Decompose a universal quantification forall x. body into the bound variable and body.

#
BoolSyntax::dest_imp

Decompose an implication p ==> q into its antecedent and consequent.

#
BoolSyntax::dest_neg

BUG FIX: The SML original returns fm (the whole negation ~ p) here, but that is inconsistent with every other destructor in this file: dest_eq(= l r) returns (l, r) -- the inner operands dest_imp(==> p q) returns (p, q) -- the inner operands dest_conj(/\ p q) returns (p, q) -- the inner operands By analogy, dest_neg(~ p) should return p (the inner term), not ~ p. The mk/dest roundtrip property also requires dest_neg(mk_neg(p)) == p. Decompose a negation ~ p, returning the inner term p.

#
BoolSyntax::disjunction

Return the disjunction constant \/ : bool -> bool -> bool.

#
BoolSyntax::equality

Return the polymorphic equality constant = : 'a -> 'a -> bool.

#
BoolSyntax::ex_unique

Return the unique existence constant ?! : ('a -> bool) -> bool.

#
BoolSyntax::existential

Return the existential quantifier constant exist : ('a -> bool) -> bool.

#
BoolSyntax::falsum

Return the falsity constant F : bool.

#
BoolSyntax::implication

Return the implication constant ==> : bool -> bool -> bool.

#
BoolSyntax::is_conj

fn BoolSyntax::is_conj(fm :
Term
) -> Bool

Test whether a term is a conjunction p /\ q.

#
BoolSyntax::is_disj

fn BoolSyntax::is_disj(fm :
Term
) -> Bool

Test whether a term is a disjunction p \/ q.

#
BoolSyntax::is_eq

Test whether a term is an equation l = r.

#
BoolSyntax::is_ex_unique

fn BoolSyntax::is_ex_unique(fm :
Term
) -> Bool

Test whether a term is a unique existence quantification ?! x. body.

#
BoolSyntax::is_exists

fn BoolSyntax::is_exists(fm :
Term
) -> Bool

Test whether a term is an existential quantification exist x. body.

#
BoolSyntax::is_forall

fn BoolSyntax::is_forall(fm :
Term
) -> Bool

Test whether a term is a universal quantification forall x. body.

#
BoolSyntax::is_imp

fn BoolSyntax::is_imp(fm :
Term
) -> Bool

Test whether a term is an implication p ==> q.

#
BoolSyntax::is_ind

fn BoolSyntax::is_ind(tm :
Term
) -> Bool

Test whether a term has the individual type ind.

#
BoolSyntax::is_neg

fn BoolSyntax::is_neg(fm :
Term
) -> Bool

Test whether a term is a negation ~ p.

#
BoolSyntax::mk_conj

Construct a conjunction p /\ q, checking that both arguments have Boolean type.

#
BoolSyntax::mk_disj

Construct a disjunction p \/ q, checking that both arguments have Boolean type.

#
BoolSyntax::mk_ex_unique

Construct a unique existence quantification ?! x. body.

#
BoolSyntax::mk_exists

Construct an existential quantification exist x. body.

#
BoolSyntax::mk_forall

Construct a universal quantification forall x. body.

#
BoolSyntax::mk_imp

Construct an implication p ==> q, checking that both arguments have Boolean type.

#
BoolSyntax::mk_neg

Construct a negation ~ p, checking that the argument has Boolean type.

#
BoolSyntax::negation

Return the negation constant ~ : bool -> bool.

#
BoolSyntax::universal

Return the universal quantifier constant forall : ('a -> bool) -> bool.

#
BoolSyntax::verum

Return the truth constant T : bool.

#
Equal

pub enum Equal {
}

Namespace for derived equality rules built from kernel primitives.

#
Equal::alpha

Alpha-conversion: derive |- t1 = t2 when t1 and t2 are alpha-convertible.

#
Equal::apTerm

fn Equal::apTerm(tm :
Term
, th : Thm) -> Thm raise

Apply a term to both sides: from A |- x = y, derive A |- f x = f y.

#
Equal::apThm

fn Equal::apThm(tm :
Term
, th : Thm) -> Thm raise

Apply an argument to both sides: from A |- f = g, derive A |- f x = g x.

#
Equal::mkBinop

fn Equal::mkBinop(binop :
Term
, lth : Thm, rth : Thm) -> Thm raise

Binary operator congruence: from A1 |- l1 = l2 and A2 |- r1 = r2, derive A1 U A2 |- b l1 r1 = b l2 r2.

#
Equal::sym

fn Equal::sym(th : Thm) -> Thm raise

Symmetry: from A |- s = t, derive A |- t = s.

#
Equal::trans

fn Equal::trans(th1 : Thm, th2 : Thm) -> Thm raise

Transitivity: from A1 |- s = t and A2 |- t = u, derive A1 U A2 |- s = u.

#
Equal::trans_fast

fn Equal::trans_fast(th1 : Thm, th2 : Thm) -> Thm raise

Transitivity (fast version): from A1 |- s = t and A2 |- t = u, derive A1 U A2 |- s = u.

#
Kernel

pub enum Kernel {
}

Namespace for the HOL Light LCF kernel inference rules.

#
Kernel::absThm

fn Kernel::absThm(v :
Term
, th : Thm) -> Thm raise

Abstraction: from A |- t = u, derive A |- (\v. t) = (\v. u).

#
Kernel::appThm

fn Kernel::appThm(th1 : Thm, th2 : Thm) -> Thm raise

Congruence: from A1 |- f = g and A2 |- x = y, derive A1 U A2 |- f x = g y.

#
Kernel::assume_

Assumption: phi |- phi.

#
Kernel::betaConv

Beta reduction: |- (\v. t) u = t[u/v].

#
Kernel::deductAntisym

fn Kernel::deductAntisym(th1 : Thm, th2 : Thm) -> Thm

Deduction antisymmetry: from A1 |- phi and A2 |- psi, derive (A1\{psi}) U (A2\{phi}) |- phi = psi.

#
Kernel::defineConst

fn Kernel::defineConst(c : String, tm :
Term
) -> Thm

Definitional extension: register constant c and return |- c = t.

#
Kernel::defineTypeOp

fn Kernel::defineTypeOp(name~ : String, abs~ : String, rep~ : String, tyvars~ : Array[String], tyax : Thm) -> (Thm, Thm)

Type definition: given |- P witness, register a new type operator and return |- abs(rep a) = a and |- P r = (rep(abs r) = r).

#
Kernel::eqMp

fn Kernel::eqMp(th1 : Thm, th2 : Thm) -> Thm raise

Equality modus ponens: from A1 |- phi = psi and A2 |- phi, derive A1 U A2 |- psi.

#
Kernel::new_axiom

fn Kernel::new_axiom(fm :
Term
) -> Thm

Add an axiom |- phi. Use sparingly -- every axiom is a potential source of inconsistency.

#
Kernel::refl

Reflexivity: |- t = t.

#
Kernel::reset_axioms

fn Kernel::reset_axioms() -> Unit

Clear all registered axioms.

#
Kernel::termSubst

Term substitution: from A |- phi, derive A[sigma] |- phi[sigma].

#
Kernel::typeSubst

Type substitution: from A |- phi, derive A[sigma] |- phi[sigma].

#
Thm

A proven theorem, consisting of a set of hypotheses and a conclusion (hyps |- concl).
impl Show for Thm

Powered by MoonBit

Site sourceReport issuePackagesBuild queueSkillsStatistics

© 2026 mooncakes.io