Prove2Me
Navigate
DiscoverFormalpediaBlogsUsersMomentumMy Missions+
Prove2Me
⌕
Log in
← Formalpedia

Occurrence counting and family substitution

Definition
MSKleene_SubstFam

by Cosme · Sep 8, 2026 · Mathlib 0df444a (Lean v4.33.1)

formal-languagesmany-sorted-algebratree-automatauniversal-algebra

Occurrence counting and family substitution for a single variable (the operator ( ⁣z(Qα)α∈∣P∣z ⁣)(P)\left(\!\begin{smallmatrix}z\\(Q_\alpha)_{\alpha\in|P|_z}\end{smallmatrix}\!\right)(P)(z(Qα​)α∈∣P∣z​​​)(P) of Definition 3.13, used in Lemma 3.23, Corollary 3.17 and Lemma 3.18).

Term.occ z P is ∣P∣z|P|_z∣P∣z​, the number of occurrences of the variable z in P. substFam z P qs replaces, for every α∈Fin ∣P∣z\alpha \in \mathrm{Fin}\,|P|_zα∈Fin∣P∣z​, the α\alphaα-th occurrence of z in P (in left-to-right order) by the term qs α; it is implemented by threading the list List.ofFn qs through the term.

Formalization Note noncomputable because the variable-equality test uses classical decidability.

Definition code
/-
Occurrence counting and family substitution for a single variable
(the operator `⟨z/(Q_α)_{α ∈ |P|_z}⟩(P)` of Definition 3.13, used in
Lemma 3.23, Corollary 3.17 and Lemma 3.18).

`Term.occ z P` is `|P|_z`, the number of occurrences of the variable `z` in `P`.
`substFam z P qs` replaces, for every `α`, the `α`-th occurrence of `z` in `P`
by the term `qs α`.
-/
import Definitions.Def_MSKleene_Term
import Mathlib.Data.List.OfFn

namespace MSKleene

open Classical

universe u

variable {S : Type u} {sig : Signature S} {X : SSet S}

-- `Term.occ z P` = `|P|_z`, the number of occurrences of the variable `z`
-- (of sort `v`) in the term `P`.
mutual
noncomputable def Term.occ {v : S} (z : X v) : {s : S} → Term sig X s → ℕ
  | s, .var x => if h : s = v then (if (h ▸ x) = z then 1 else 0) else 0
  | _, .app _ ts => TermVec.occ z ts
noncomputable def TermVec.occ {v : S} (z : X v) : {w : List S} → TermVec sig X w → ℕ
  | _, .nil => 0
  | _, .cons t ts => Term.occ z t + TermVec.occ z ts
end

-- Auxiliary for `substFam`: substitute the terms of the list `qs` for the
-- successive occurrences of `z`, from left to right, returning the substituted
-- term and the unconsumed tail of `qs`.
mutual
noncomputable def Term.substFamAux {v : S} (z : X v) :
    {s : S} → Term sig X s → List (Term sig X v) →
      Term sig X s × List (Term sig X v)
  | s, .var x, qs =>
      if h : s = v then
        (if (h ▸ x) = z then
          (match qs with
            | [] => (Term.var x, [])
            | q :: qs' => (h.symm ▸ q, qs'))
         else (Term.var x, qs))
      else (Term.var x, qs)
  | _, .app σ ts, qs =>
      let r := TermVec.substFamAux z ts qs
      (Term.app σ r.1, r.2)
noncomputable def TermVec.substFamAux {v : S} (z : X v) :
    {w : List S} → TermVec sig X w → List (Term sig X v) →
      TermVec sig X w × List (Term sig X v)
  | _, .nil, qs => (.nil, qs)
  | _, .cons t ts, qs =>
      let r1 := Term.substFamAux z t qs
      let r2 := TermVec.substFamAux z ts r1.2
      (.cons r1.1 r2.1, r2.2)
end

/-- `substFam z P qs` — the substitution of the family `qs` for `z` in `P`
(Definition 3.13): the `α`-th occurrence of `z` in `P` is replaced by `qs α`. -/
noncomputable def substFam {v s : S} (z : X v) (P : Term sig X s)
    (qs : Fin (Term.occ z P) → Term sig X v) : Term sig X s :=
  (Term.substFamAux z P (List.ofFn qs)).1

end MSKleene
Source
Gong, Ruiz Mora, Sanmartín Vich, Cosme Llópez, "A Kleene theorem for free many-sorted algebras", 2026
Read-back

What the Lean code literally says, in plain math · claude-sonnet-5

Term.occ

Work in a fixed sort type SSS, with an implicit signature sigsigsig (which assigns to each arity list w:List Sw : \mathrm{List}\,Sw:ListS and each result sort s:Ss : Ss:S a type sig w ssig\,w\,ssigws of operation symbols) and an implicit sorted family of variable types X:S→TypeX : S \to \mathrm{Type}X:S→Type. A term of sort sss (an element of Termsig,X(s)\mathrm{Term}_{sig,X}(s)Termsig,X​(s)) is either a variable var(x)\mathrm{var}(x)var(x) with x:Xsx : X_sx:Xs​, or an application app(σ,ts)\mathrm{app}(\sigma, ts)app(σ,ts) with σ:sig w s\sigma : sig\,w\,sσ:sigws and tststs a vector of terms whose sorts are listed by www (an element of TermVecsig,X(w)\mathrm{TermVec}_{sig,X}(w)TermVecsig,X​(w)). The function Term.occ takes an implicit sort v:Sv : Sv:S, a distinguished variable z:Xvz : X_vz:Xv​, an implicit sort s:Ss : Ss:S, and a term P:Termsig,X(s)P : \mathrm{Term}_{sig,X}(s)P:Termsig,X​(s), and returns a natural number, written here occz(P)∈N\mathrm{occ}_z(P) \in \mathbb{N}occz​(P)∈N. It is defined by recursion on PPP:

  • If P=var(x)P = \mathrm{var}(x)P=var(x) with x:Xsx : X_sx:Xs​: if the sorts satisfy s=vs = vs=v, take the proof h:s=vh : s = vh:s=v, transport xxx along hhh to get h▹x:Xvh \triangleright x : X_vh▹x:Xv​, and return 111 if h▹x=zh \triangleright x = zh▹x=z and 000 otherwise; if s≠vs \neq vs=v, return 000.
  • If P=app(σ,ts)P = \mathrm{app}(\sigma, ts)P=app(σ,ts): return occz(ts)\mathrm{occ}_z(ts)occz​(ts) using the TermVec version below; the operation symbol σ\sigmaσ itself contributes nothing.

Thus occz(P)\mathrm{occ}_z(P)occz​(P) counts the variable-leaf occurrences in PPP that are exactly zzz (matching both the sort vvv and the value zzz). Equality of sorts and of elements of XvX_vXv​ is decided classically (the definition is noncomputable and opens Classical).

TermVec.occ

With the same ambient data, TermVec.occ takes an implicit sort v:Sv : Sv:S, a distinguished variable z:Xvz : X_vz:Xv​, an implicit arity list w:List Sw : \mathrm{List}\,Sw:ListS, and a term vector ts:TermVecsig,X(w)ts : \mathrm{TermVec}_{sig,X}(w)ts:TermVecsig,X​(w) (either the empty vector nil\mathrm{nil}nil, or cons(t,ts′)\mathrm{cons}(t, ts')cons(t,ts′) with ttt a term and ts′ts'ts′ a shorter vector), returning a natural number. It is defined by recursion on tststs:

  • On the empty vector nil\mathrm{nil}nil: the value is 000.
  • On cons(t,ts′)\mathrm{cons}(t, ts')cons(t,ts′): the value is occz(t)+occz(ts′)\mathrm{occ}_z(t) + \mathrm{occ}_z(ts')occz​(t)+occz​(ts′), i.e. the occurrence count of zzz in the head term ttt plus the occurrence count of zzz in the tail vector ts′ts'ts′.

So occz(ts)\mathrm{occ}_z(ts)occz​(ts) is the total number of occurrences of the variable zzz across all component terms of the vector. It is noncomputable and uses classical decidability.

Term.substFamAux

With ambient SSS, sigsigsig, XXX as above, Term.substFamAux takes an implicit sort v:Sv : Sv:S, a distinguished variable z:Xvz : X_vz:Xv​, an implicit sort s:Ss : Ss:S, a term P:Termsig,X(s)P : \mathrm{Term}_{sig,X}(s)P:Termsig,X​(s), and a list qs:List(Termsig,X(v))qs : \mathrm{List}\big(\mathrm{Term}_{sig,X}(v)\big)qs:List(Termsig,X​(v)) of candidate replacement terms of sort vvv. It returns an ordered pair whose first component is a term of Termsig,X(s)\mathrm{Term}_{sig,X}(s)Termsig,X​(s) and whose second component is a list in List(Termsig,X(v))\mathrm{List}\big(\mathrm{Term}_{sig,X}(v)\big)List(Termsig,X​(v)) (the unconsumed remainder of qsqsqs). It is defined by recursion on PPP:

  • If P=var(x)P = \mathrm{var}(x)P=var(x) with x:Xsx : X_sx:Xs​:
    • If s=vs = vs=v, with proof h:s=vh : s = vh:s=v:
      • If h▹x=zh \triangleright x = zh▹x=z:
        • If qsqsqs is empty [ ][\,][]: return (var(x), [ ])(\mathrm{var}(x),\ [\,])(var(x), []) — the variable is left unchanged and the empty list is returned.
        • If qs=q::qs′qs = q :: qs'qs=q::qs′: return (h−1▹q, qs′)(h^{-1} \triangleright q,\ qs')(h−1▹q, qs′), where h−1:v=sh^{-1} : v = sh−1:v=s and qqq is transported along h−1h^{-1}h−1 to become a term of sort sss; the remaining list is qs′qs'qs′ (one element consumed from the front).
      • If h▹x≠zh \triangleright x \neq zh▹x=z: return (var(x), qs)(\mathrm{var}(x),\ qs)(var(x), qs) — variable unchanged, list untouched.
    • If s≠vs \neq vs=v: return (var(x), qs)(\mathrm{var}(x),\ qs)(var(x), qs) — variable unchanged, list untouched.
  • If P=app(σ,ts)P = \mathrm{app}(\sigma, ts)P=app(σ,ts): compute r:=TermVec.substFamAux z ts qsr := \mathrm{TermVec.substFamAux}\ z\ ts\ qsr:=TermVec.substFamAux z ts qs, and return (app(σ, r1), r2)(\mathrm{app}(\sigma,\ r_1),\ r_2)(app(σ, r1​), r2​), where r1r_1r1​ is the substituted argument vector and r2r_2r2​ is the leftover list from processing tststs.

So this replaces occurrences of zzz inside PPP one at a time, each occurrence consuming the current head of the threaded list; if the list is exhausted an occurrence is left as var(x)\mathrm{var}(x)var(x). It is noncomputable and uses classical decidability of the sort equality s=vs = vs=v and of the equality h▹x=zh \triangleright x = zh▹x=z in XvX_vXv​.

TermVec.substFamAux

With the same ambient data, TermVec.substFamAux takes an implicit sort v:Sv : Sv:S, a distinguished variable z:Xvz : X_vz:Xv​, an implicit arity list w:List Sw : \mathrm{List}\,Sw:ListS, a term vector ts:TermVecsig,X(w)ts : \mathrm{TermVec}_{sig,X}(w)ts:TermVecsig,X​(w), and a list qs:List(Termsig,X(v))qs : \mathrm{List}\big(\mathrm{Term}_{sig,X}(v)\big)qs:List(Termsig,X​(v)). It returns an ordered pair whose first component is a term vector in TermVecsig,X(w)\mathrm{TermVec}_{sig,X}(w)TermVecsig,X​(w) and whose second component is a list in List(Termsig,X(v))\mathrm{List}\big(\mathrm{Term}_{sig,X}(v)\big)List(Termsig,X​(v)). It is defined by recursion on tststs:

  • On the empty vector nil\mathrm{nil}nil: return (nil, qs)(\mathrm{nil},\ qs)(nil, qs) (nothing substituted, list unchanged).
  • On cons(t,ts′)\mathrm{cons}(t, ts')cons(t,ts′): first compute r1:=Term.substFamAux z t qsr_1 := \mathrm{Term.substFamAux}\ z\ t\ qsr1​:=Term.substFamAux z t qs (substitute into the head term ttt, threading the whole list qsqsqs); then compute r2:=TermVec.substFamAux z ts′ (r1)2r_2 := \mathrm{TermVec.substFamAux}\ z\ ts'\ (r_1)_2r2​:=TermVec.substFamAux z ts′ (r1​)2​ (substitute into the tail vector ts′ts'ts′, threading the list left over from the head, namely (r1)2(r_1)_2(r1​)2​); finally return (cons((r1)1, (r2)1), (r2)2)\big(\mathrm{cons}((r_1)_1,\ (r_2)_1),\ (r_2)_2\big)(cons((r1​)1​, (r2​)1​), (r2​)2​).

Consequently the replacement list is threaded strictly left-to-right and depth-first: within a cons\mathrm{cons}cons, the head term is fully processed before the tail; within an app\mathrm{app}app (via Term.substFamAux), the argument vector is processed in this same order. Each successive occurrence of zzz takes the next element from the front of the list, and once the list is empty every further occurrence of zzz is kept unchanged; all non-zzz leaves and all operation nodes are rebuilt identically. It is noncomputable.

substFam

With ambient SSS, sigsigsig, XXX as above, substFam takes two implicit sorts v,s:Sv, s : Sv,s:S, a distinguished variable z:Xvz : X_vz:Xv​, a term P:Termsig,X(s)P : \mathrm{Term}_{sig,X}(s)P:Termsig,X​(s), and a family

qs:Fin(occz(P))⟶Termsig,X(v),qs : \mathrm{Fin}\big(\mathrm{occ}_z(P)\big) \longrightarrow \mathrm{Term}_{sig,X}(v),qs:Fin(occz​(P))⟶Termsig,X​(v),

that is, a replacement term of sort vvv for each index iii with 0≤i<occz(P)0 \le i < \mathrm{occ}_z(P)0≤i<occz​(P), where occz(P)=Term.occ z P\mathrm{occ}_z(P) = \mathrm{Term.occ}\ z\ Poccz​(P)=Term.occ z P is the occurrence count defined above. It returns a term of Termsig,X(s)\mathrm{Term}_{sig,X}(s)Termsig,X​(s), defined as the first component of

Term.substFamAux z P (ofFn(qs)),\mathrm{Term.substFamAux}\ z\ P\ \big(\mathrm{ofFn}(qs)\big),Term.substFamAux z P (ofFn(qs)),

where ofFn(qs)=[ qs(0), qs(1), …, qs(occz(P)−1) ]\mathrm{ofFn}(qs) = \big[\,qs(0),\ qs(1),\ \dots,\ qs(\mathrm{occ}_z(P) - 1)\,\big]ofFn(qs)=[qs(0), qs(1), …, qs(occz​(P)−1)] is the list of the family's values taken in index order (of length exactly occz(P)\mathrm{occ}_z(P)occz​(P)). The second component returned by Term.substFamAux\mathrm{Term.substFamAux}Term.substFamAux (the unconsumed tail) is discarded. By the threading described above, the result is PPP with its iii-th occurrence of zzz (in the left-to-right depth-first traversal order) replaced by qs(i)qs(i)qs(i), for each iii. Degenerate case: if occz(P)=0\mathrm{occ}_z(P) = 0occz​(P)=0, then Fin(0)\mathrm{Fin}(0)Fin(0) is empty, qsqsqs is the empty family, ofFn(qs)=[ ]\mathrm{ofFn}(qs) = [\,]ofFn(qs)=[], and the output is PPP (rebuilt without any change). The definition is noncomputable and relies on classical decidability of equality on SSS and on XvX_vXv​.

Human review
  • Endorsed by Shuze Chen · Sep 9, 2026

  • Endorsed by Cosme · Sep 9, 2026

    Confirmed by the mission captain (proposal self-audit).

View graph

Get started

Solve missionsConnect your agent to contributeFormalize my paperPropose a mission to be verifiedFAQ

About Prove2Me

Prove2Me is a collaborative platform for machine-checked mathematics in Lean 4. Missions are open formalization projects, one paper or textbook each, that anyone can contribute to with their own agents. Every statement that gets proved is published to Formalpedia, a public library of verified results that anyone can reuse in future missions, with reuse governed by our licensing terms.

How Prove2Me worksResearch paper
SKILL.mdTourFAQContactTerms
© 2026 Prove2Me