Lectures onType Theory
Chapter 80
Chapter 80Core route

Universes of Datatype Descriptions and Generic Programs

Natural numbers, lists, trees, and vectors have different constructors, yet their maps and folds repeat the same recursion: preserve constant data, recurse at recursive positions, and follow sums and products. Writing that recursion once requires data that describes a datatype without being the datatype.

A regular description sublanguage

We begin with a finite-branching regular normal form. It is the fragment on which the first map, fold, traversal, and fusion calculations are carried out; the exact indexed Morris–Altenkirch–Ghani universe is added only after those programs are in hand. Fix a small sort type I:Ui. The direct attempt is to call every operator Φ:(IUi)(IUi) a datatype description. A generic map would then have to turn each family h:(j:I)X(j)Y(j) into a map Φ(X)(j)Φ(Y)(j), but an arbitrary Φ need not provide such an action. More seriously, Φ(X)(j):=X(j)A places the recursive family to the left of an arrow. Its fixed point would evade the strict-positivity condition of remark 28.36.

The repair records only the operations through which a recursive position may be reached. An empty constructor needs a unit code, stored data needs a constant code, and a recursive field needs a named index. Alternative constructors force sums; several fields force products. A tag whose value determines the remaining fields forces a dependent sum.

Definition 80.1 — Regular description codes

A regular description is a code built by the constructors one,K(A),X(j),D+E,D×E,sigma(A,F), where A:Ui, j:I, D,E:Desci(I), and F:ADesci(I). The codes inhabit Desci(I):Ui+1; the successor level prevents a universe from containing its own code type.

This is a book-normalized, finite-branching sublanguage, not yet the full Morris–Altenkirch–Ghani indexed universe. Sums, products, and constants are convenient derived regular codes; sigma stores a finite tag whose branch is selected before recursion. The principal indexed syntax and its equality-supporting subfragment impose additional constraints on these regular codes.

Definition 80.2 — Interpretation of descriptions

For a family X:IUi and a code D:Desci(I), define the type [[D]](X):Ui by recursion on D: [[one]](X):=1,[[K(A)]](X):=A,[[X(j)]](X):=X(j),[[D+E]](X):=[[D]](X)+[[E]](X),[[D×E]](X):=[[D]](X)×[[E]](X),[[sigma(A,F)]](X):=a:A[[F(a)]](X).

The recursive family occurs only as the argument of X(j) and then covariantly through sums, products, and Sigma types. A code such as X(j)A cannot be formed: the grammar has no constructor that places X(j) to the left of a function arrow. This syntactic absence is the strict-positivity check.

The chapter extends T0 by one instance of the indexed-family schema from chapter 31. No equality, universe, or context rule is replaced.

Definition 80.3 — Fixed points of descriptions

For a code family D:IDesci(I), the following formation and introduction rules define its least fixed point:

Its nonrecursive destructor and computation equation are outj:Mu(D)(j)[[D(j)]](Mu(D)),outj(rollj(u))u. Consequently a path p:rollj(u)=rollj(v) gives apoutj(p):u=v. This is the only constructor-injectivity fact used below.

The rule names Mu-form and Mu-intro are local to description fixed points. They are not the iso-recursive-type rules with similarly named mnemonics in appendix A; their indexed code-family premises distinguish the two signatures.

The regular case takes I:=1. Suppressing its unique index, define DN:=one+X(),DList(A):=one+(K(A)×X()),DTree(A):=K(A)+(X()×X()). Under roll, the left and right summands give the familiar constructors. For example, nil:=roll(inl()),cons(a,xs):=roll(inr((a,xs))). For trees, put leaf(a):=roll(inl(a)),fork(l,r):=roll(inr((l,r))). The ladder has so far used unit, constants, recursive positions, alternatives, and products. To force the remaining code, let a Boolean tag choose the arity: DTagged:=sigma(2,b.case(b;false.one;true.X()×X())). Its two layers are (false,) and (true,(l,r)). The tag is retained in the layer, and its value determines whether zero or two recursive children must be supplied.

Definition 80.4 — Description induction

For P:j:IMu(D)(j)Uk, define AllE(P,u) by recursion on E: Allone(P,):=1,AllK(A)(P,a):=1,AllX(j)(P,x):=P(j,x),AllE+G(P,inl(u)):=AllE(P,u),AllE+G(P,inr(v)):=AllG(P,v),AllE×G(P,(u,v)):=AllE(P,u)×AllG(P,v),Allsigma(A,F)(P,(a,u)):=AllF(a)(P,u). For r:(j:I)(x:Mu(D)(j))P(j,x), define callsE(P,r,u):AllE(P,u) by the same recursion: use at unit and constant codes, use rj(x) at X(j), follow a sum injection or Sigma tag, and pair the two recursive results at a product.

If s:j:Iu:[[D(j)]](Mu(D))AllD(j)(P,u)P(j,rollj(u)), then the elimination rule gives indD(P,s;j,t):P(j,t), with the computation equation indD(P,s;j,rollj(u))sj(u,callsD(j)(P,indD(P,s),u)).

For l,r:Mu(DTree(A)), product introduction gives (l,r):[[X()×X()]](Mu(DTree(A))). Sum introduction and Mu-intro therefore derive fork(l,r):Mu(DTree(A)). At this constructor, (80.1) replaces the two All components by the two recursive calls. If instead x:Mu(D)(j) is a variable, neither outj(x) nor indD(P,s;j,x) has a root computation rule; both are neutral.

Exercise 80.1

★☆☆ Expand the interpretations of the vector code at zero and at suc(n). Write the two resulting constructor types and match them with definition 78.1. Then take P(n,xs):=N and write the two methods of a length-counting use of indDVec(A).

Generic action, folds, and traversals

The interpretation is functorial in its recursive family. For a family of maps hj:X(j)Y(j), define u:[[D]](X)  mapD(h,u):[[D]](Y). by recursion on D. It is the identity on one and constants, applies hj at X(j), preserves the chosen summand, maps both product components, and preserves the Sigma tag while mapping its remainder.

Lemma 80.5 — Interpretation functor laws

Let X,Y,Z:IUi, let fj:X(j)Y(j) and gj:Y(j)Z(j), and let D:Desci(I). For u:[[D]](X): mapD(λj.λx.x,u)=u,mapD(λj.λx.gj(fj(x)),u)=mapD(g,mapD(f,u)). The laws are stated at the argument u; they assert no identity between functions.

Proof of Lemma 80.5 — Interpretation functor laws

Proof. Induct on D. The unit, constant, and recursive-position cases reduce to reflexivity or beta computation at fj,gj; the laws assume no equations about those maps. A sum retains its injection and uses the induction hypothesis on its payload. A product uses the two induction hypotheses and function congruence for pairing. For sigma(A,F), fix a:A; the induction hypothesis for F(a) proves the second component while the first component remains a. These cases exhaust the code grammar. ◻

Lemma 80.6 — Congruence of description action

Let h,h:(j:I)X(j)Y(j), and suppose pj,x:hj(x)=hj(x) for every j:I and x:X(j). Then, for every D:Desci(I) and u:[[D]](X), mapD(h,u)=mapD(h,u).

Proof of Lemma 80.6 — Congruence of description action

Proof. Induct on D. The recursive-position case is pj,u. Unit and constant codes give reflexivity. A sum follows its injection, a product applies the two induction hypotheses under pairing, and a Sigma code fixes its tag a:A and applies the induction hypothesis for the branch F(a). ◻

Lemma 80.7 — Layer-local congruence

Let h,h:(j:I)X(j)Y(j). For u:[[D]](X), an inhabitant of AllD(λj.λx.IdY(j)(hj(x),hj(x)),u) determines an identification mapD(h,u)=mapD(h,u).

Proof of Lemma 80.7 — Layer-local congruence

Proof. Induct on D. At X(j) use the supplied path. Unit and constants give reflexivity. Sums follow their injection, products use both components of the All witness, and Sigma codes retain their tag and use the branch induction hypothesis. ◻

Definition 80.8 — Generic fold

An D-algebra on X:IUi is a family αj:[[D(j)]](X)X(j). For E:Desci(I), u:[[E]](Mu(D)), and q:AllE(λj.λt.X(j),u), define refillE(u,q):[[E]](X) by the equations refillone(,),refillK(A)(a,)a,refillX(j)(x,q)q,refillE+G(inl(u),q)inl(refillE(u,q)),refillE+G(inr(v),q)inr(refillG(v,q)),refillE×G((u,v),(qE,qG))(refillE(u,qE),refillG(v,qG)),refillsigma(A,H)((a,u),q)(a,refillH(a)(u,q)). Define foldD(α)j:Mu(D)(j)X(j) by description induction, using the method αj(refillD(j)(u,q)).

Lemma 80.9 — Fold calculation

Let D:IDesci(I), let α be a D-algebra on X:IUi, let j:I, and let u:[[D(j)]](Mu(D)). The generic fold satisfies the propositional calculation rule foldD(α)j(rollj(u))=αj(mapD(j)(foldD(α))(u)).

Proof of Lemma 80.9 — Fold calculation

Proof. Equation (80.1) reduces the left side to αj(refillD(j)(u,q)), where q contains the recursive fold results. Induction on D(j) proves refillD(j)(u,q)=mapD(j)(foldD(α),u). The unit, constant, and recursive-position cases are reflexivity. Sums and Sigma codes follow the selected branch, and products use both induction hypotheses under pairing. Applying αj to this path gives (80.2). ◻

For lists, the algebra sends the left summand to a chosen z:X and the right summand (a,x) to c(a,x). Expanding (80.2) gives fold(z,c,nil)=z,fold(z,c,cons(a,xs))=c(a,fold(z,c,xs)). Thus the familiar fold equations are consequences of the code interpretation, not separate axioms.

Construction 80.10 — A syntactic unfold

For A:Ui, let Seed(A):Ui have constructors stop:ASeed(A),split:Seed(A)Seed(A)Seed(A). Define γ:Seed(A)[[DTree(A)]](Seed(A)) by γ(stop(a)):=inl(a),γ(split(l,r)):=inr((l,r)). Structural recursion on the seed defines ana(γ,s):=roll(mapDTree(A)(ana(γ,),γ(s))). Put s12:=split(stop(1),stop(2)). Then ana(γ,s12)roll(inr((ana(γ,stop(1)),ana(γ,stop(2)))))fork(leaf(1),leaf(2)). Equation (80.3) is a terminating program on an inductive seed; it does not assert a final-coalgebra or unrestricted corecursion principle.

Theorem 80.11 — Fold fusion

Let α be a D-algebra on X, let β be a D-algebra on Y, and let hj:X(j)Y(j). Suppose that for every j:I and u:[[D(j)]](X), hj(αj(u))=βj(mapD(j)(h,u)). Then, for every t:Mu(D)(j), hj(foldD(α)j(t))=foldD(β)j(t).

Proof of Theorem 80.11 — Fold fusion

Proof. Apply description induction to t=rollj(u). Write f:=foldD(α) and g:=foldD(β). The induction hypotheses form H:AllD(j)(λk.λx.IdY(k)(hk(fk(x)),gk(x)),u). The constructor case is the annotated calculation below. Write (F) for (80.2), (A) for the algebra hypothesis, (M) for map composition from lemma 80.5, and (H) for lemma 80.7 instantiated by the induction hypotheses. hj(fj(rollj(u)))=(F)hj(αj(mapD(j)(f,u)))=(A)βj(mapD(j)(h,mapD(j)(f,u)))=(M),backwardsβj(mapD(j)(λk.λx.hk(fk(x)),u))=(H)βj(mapD(j)(g,u))=(F),backwardsgj(rollj(u)). ◻

Construction 80.12 — Generic size

For g:(j:I)X(j)N and u:[[D]](X), define sumD(g,u):N by recursion on D: constants and unit contribute zero, a recursive position x:X(j) contributes gj(x), a sum follows its injection, a product adds its contributions, and a Sigma follows its tag. The algebra αj(u):=suc(sumD(j)(λk.λn.n,u)) therefore gives sizeD:Mu(D)(j)N by definition 80.8. For a list, expansion gives size(nil)=suc(0),size(cons(a,xs))=suc(size(xs)). This size counts constructor nodes; it does not count constant payloads.

Lemma 80.13 — An immediate recursive child is smaller

Define xEu by recursion on E: it holds by reflexivity at a recursive-position code, follows the selected summand or Sigma branch, and follows either component of a product. If xD(j)u, then suc(sizeD(x))sizeD(rollj(u)).

Proof of Lemma 80.13 — An immediate recursive child is smaller

Proof. Induct on the derivation of xD(j)u. At a recursive-position code, the fold calculation expands the right side to suc(sizeD(x)). Sums and Sigma codes reduce to the selected branch. At a product, the right side is one plus the sum of the contributions of both components, so the induction hypothesis is preserved by addition of the nonnegative contribution of the other component. These are all clauses of the structurally defined relation. ◻

Definition 80.14 — Applicative traversal interface

An applicative traversal interface consists of a type operator F:UiUi and, for A,B:Ui, operations pureA:AF(A),applyA,B:F(AB)F(A)F(B). Write mapF(f,x):=apply(pure(f),x). For f,g:AB, the interface carries the extensional action law ((a:A)f(a)=g(a))(x:F(A))mapF(f,x)=mapF(g,x). This law concerns the action on one given x; it does not assert f=g.

For A,B,C:Ui, put c:(BC)(AB)AC,c(f,g,x):=f(g(x)), and, for u:F(BC) and v:F(AB), put q(u,v):=apply(apply(pure(c),u),v). The four pointwise laws are apply(pure(idA),x)=x,apply(q(u,v),w)=apply(u,apply(v,w)),apply(pure(f),pure(a))=pure(f(a)),apply(u,pure(a))=apply(pure(λf.f(a)),u). Here x,w:F(A) in the first two equations. The third equation quantifies over f:AB and a:A; the fourth quantifies over u:F(AB) and a:A. The bound variable f:AB in the final right-hand side is the argument of the pure evaluation function. Equation (80.5) gives identity for mapF. Its composition law uses (80.6) followed by two instances of (80.7); these instances reduce the two pure functions to pure(λx.f(g(x))).

Definition 80.15 — Description traversal

Let F be an applicative traversal interface, let X,Y:IUi, and let D:Desci(I). Recursion on D defines traverseD:(j:IX(j)F(Y(j)))[[D]](X)F([[D]](Y)). The defining clauses are traverseone(f,):=pure(),traverseK(A)(f,a):=pure(a),traverseX(j)(f,x):=fj(x),traverseD+E(f,inl(u)):=mapF(inl,traverseD(f,u)),traverseD+E(f,inr(v)):=mapF(inr,traverseE(f,v)),uF:=traverseD(f,u),vF:=traverseE(f,v),traverseD×E(f,(u,v)):=apply(mapF(λx.λy.(x,y),uF),vF),wF:=traverseG(a)(f,u),traversesigma(A,G)(f,(a,u)):=mapF(λv.(a,v),wF).

Definition 80.16 — Composite applicative

For applicative interfaces F and G, put Comp(F,G)(A):=F(G(A)). Its operations are pureComp(F,G)(a):=pureF(pureG(a)),applyComp(F,G)(u,v):=applyF(applyF(pureF(applyG),u),v).

Lemma 80.17 — Composite applicative laws

Let F and G be applicative traversal interfaces. Assume function extensionality for types in Ui: A,B:Uif,g:AB((a:A)f(a)=g(a))f=g. The operations of definition 80.16 satisfy (80.5)(80.8) and (80.4).

Proof of Lemma 80.17 — Composite applicative laws

Proof. For composition, expand every composite application and use (80.6) for F. Both sides then have the outer forms obtained from lift3F(k,u,v,w):=applyF(applyF(applyF(pureF(k),u),v),w). They are lift3F(kL,u,v,w) and lift3F(kR,u,v,w). For uG:G(BC), vG:G(AB), and wG:G(A), the two pure functions satisfy kL(uG,vG,wG):=applyG(qG(uG,vG),wG),kR(uG,vG,wG):=applyG(uG,applyG(vG,wG)). Equation (80.6) for G gives their pointwise equality. Three applications of (80.9) give kL=kR. Congruence under pureF and the three outer applications therefore gives the composite composition law. The extensionality hypothesis is essential at this step: the unary action law (80.4) does not turn equality after three arguments into equality of the intervening function values.

For identity, put eG(x):=applyG(pureG(id),x). Expansion gives the annotated calculation applyComp(F,G)(pureComp(F,G)(id),u)=(80.7)forFmapF(eG,u)=(80.4)forFand(80.5)forGmapF(id,u)=(80.5)forFu. Homomorphism reduces by two instances of (80.7) for F and one for G. Interchange expands to two outer F actions; one application of (80.9) turns the pointwise G interchange law into the equality between their pure functions, after which (80.8) for F closes the calculation.

Finally, the homomorphism law for F gives mapComp(F,G)(r,u)=mapF(mapG(r),u). Pointwise equality r(a)=s(a) gives mapG(r,x)=mapG(s,x) by extensional action for G. Extensional action for F then proves the displayed law for the composite. ◻

Definition 80.18 — Identity applicative

Put Id(A):=A, pureId(a):=a, and applyId(f,a):=f(a). Its four applicative laws are judgmental beta equalities. Its extensional action is the supplied pointwise path evaluated at a.

Proposition 80.19 — Traversal laws

Let X,Y,Z:IUi, let D:Desci(I), and let u:[[D]](X). For fj:X(j)Y(j) and the identity applicative of definition 80.18, traverseD(f,u)=mapD(f,u). Let F,G be applicative interfaces and assume (80.9). Put H:=Comp(F,G) and give H the composite applicative structure. Suppose fj:X(j)F(Y(j)),gj:Y(j)G(Z(j)). Put hj(x):=mapF(gj,fj(x)) and v:=traverseDF(f,u). Then traverseDH(h,u)=mapF(traverseDG(g),v).

Proof of Proposition 80.19 — Traversal laws

Proof. Induct on D. For identity, unit, constant, and recursive-position codes reduce by beta computation. Sum and Sigma codes apply their induction hypothesis under the corresponding constructor. At a product, the identity applicative reduces the traversal clause to (traverseD(f,u1),traverseE(f,u2)). The two induction hypotheses give the two components required by the product clause of mapD×E.

For composition, abbreviate lift2F(k,x,y):=applyF(mapF(k,x),y). Let A0,A1,B0,B1,C:Ui, let x:F(A0) and y:F(B0), and let r:A0G(A1), s:B0G(B1), and k:A1B1C. Expansion of the composite application, followed by the composition and homomorphism laws for F, gives lift2Comp(F,G)(k,mapF(r,x),mapF(s,y))=mapF(λp.lift2G(k,r(pr1(p)),s(pr2(p))),lift2F(pair,x,y)). The only equality between functions in this expansion is obtained from (80.9); its pointwise components are the composition laws for F and G proved in lemma 80.17. In the product case, apply (80.10) with k:=pair. The two induction hypotheses give the pointwise replacements for r and s, and (80.4) for F applies them under the outer action. Sum and Sigma codes are the unary instance of the same calculation. Unit, constant, and recursive-position codes reduce to the applicative identity, homomorphism, and composition equations. The code induction has now treated all constructors. ◻

Construction 80.20 — A traversal that visits every node

Define the writer applicative by W(A):=N×A,pureW(a):=(0,a), and applyW((m,f),(n,a)):=(m+n,f(a)). The unit and associativity equations for addition prove the four applicative laws. Extensional action applies the pointwise path to the second component and preserves the writer count. The product clause of traversal performs visible work on a list layer. Put T(f,u):=traverseDList(A)W(f,u). If f(x)=(n,y), then T(f,inr((a,x)))mapW(inr,applyW(mapW(λa.λy.(a,y),(0,a)),(n,y)))(n,inr((a,y))). Thus the constant field contributes no effect and the recursive child’s count is retained.

Lift this layer traversal to the fixed point by a fold. For u:[[D(j)]](λk.W(Mu(D)(k))), put pj(u):=traverseD(j)W(λk.λz.z,u),αjW(u):=(suc(pr1(pj(u))),rollj(pr2(pj(u)))). Then visitD:=foldD(αW) has type visitD,j:Mu(D)(j)W(Mu(D)(j)). For the list cons(1,cons(2,nil)), the three fold equations give visitDList(N)(cons(1,cons(2,nil)))=(3,cons(1,cons(2,nil))). The result 3 counts two cons constructors and the terminal nil constructor.

Exercise 80.2

★★☆ Encode list map as a fold over DList(A), then expand (80.2) at nil and cons. Prove the identity-map law by list induction or by theorem 80.11, stating the algebra equation used in the fusion proof.

The indexed strictly-positive-family universe

The regular codes above name recursive sorts directly. A genuinely indexed description must also say how constructor data constrain the result index and how recursive arguments are indexed. We now freeze the Morris–Altenkirch–Ghani universe rather than pretending that an arbitrary family of regular codes is already its full syntax.

Let I=(I1,,In) be a telescope of input-family index types and let O:Ui be the output index. An indexed strictly positive type code T:ISPT(I) has input variables vz(i) and vs(T), the codes 0 and 1, dependent index aggregation Σf(F,o) and Πf(F,o), and the fixed-point code μ(F,o). Here f:OO, F:OISPT(I), and o:O for Σ and Π, while a family description is SPF(I,O):=OISPT(I). The syntax is indexed by the complete input telescope; the output index is an argument to a family code, not a meta-level case split hidden from the universe.

For an environment ρ assigning a family to each input variable, the interpretation selects that family at vz/vs and has [[0]]ρ:=0,[[1]]ρ:=1,[[Σf(F,o)]]ρ:=o:OIdO(f(o),o)×[[F(o)]]ρ,[[Πf(F,o)]]ρ:=o:OIdO(f(o),o)[[F(o)]]ρ,[[μ(F,o)]]ρ:=Mu(λo.[[F(o)]]ρ,)(o). The last line extends ρ by the family being defined before interpreting recursive variables. Its introduction equation is syntactic: outo(rollo(u))u. The Σf code chooses one witness whose index maps to o; the Πf code stores data at every such witness. These are distinct codes, not abbreviations for ordinary sum and function types outside the universe.

Finite regular sums and products compile into this universe. A Boolean Σ over the constant map into the unique output yields binary choice; finite Π yields a product. A constant payload is a nonrecursive input field, and a regular recursive position is a variable followed by μ. This recovers the Benke–Dybjer–Jansson regular presentation as a bounded derived normal form: it does not replace the principal indexed syntax.

The vector family is now an internal code. Its zero constructor chooses the result index 0 and carries unit; its cons constructor chooses n:N, stores a:A, requests one recursive child at n, and returns suc(n). In the earlier regular notation its two fibers normalize to DVec(A)(0):=one,DVec(A)(suc(n)):=K(A)×X(n). Thus roll0() and rollsuc(n)((a,xs)) have exactly the constructor indices of definition 78.1. The displayed normal forms are derived from the index equality stored by Σf; they are not an assertion that every indexed family is an external case split.

Map, fold, traversal, and induction extend by structural recursion on ISPT. At Σf they preserve the witness and its index equality, and at Πf they act pointwise. At μ they use the recursive map or fold supplied by the extended environment. The identity/composition and fusion proofs add one congruence case for each constructor. We do not identify these codes with containers: shapes and positions begin in the next chapter.

Exercise 80.3

★★☆ Expand the Σf interpretation for the vector cons constructor at output m:N. Show that a witness n contributes only when suc(n)=m, and specialize the calculation to m=suc(k).

Induction, equality, and elaboration

Generic equality needs more than strict positivity. The full MAG universe contains Πf, hence possibly infinite branching, and its Σf index witnesses need not have decidable equality. We therefore prove equality only for a smaller finite regular fragment, exactly as the source does.

Definition 80.21 — Regular equality codes

The grammar EqDesci(I) contains one,K(A,dA),X(j),D+E,D×E. where dA decides equality on every stored constant type. It contains no sigma, Πf, or arbitrary index-witness code. Tags are the syntactic left/right constructors, so they are decidable by inspection. Erasure :EqDesci(I)Desci(I) forgets the stored decisions.

Lemma 80.22 — Decidable equality gives local UIP

Let d:a,b:AIdA(a,b)+¬IdA(a,b). Then uipA:a,b:Ap,q:IdA(a,b)IdIdA(a,b)(p,q).

Proof of Lemma 80.22 — Decidable equality gives local UIP

Proof. Fix a,b:A and put Ea,b:=IdA(a,b). Define an endomap fa,b:Ea,bEa,b by eliminating d(a,b): fa,b(p):=rif d(a,b)inl(r),fa,b(p):=rec0(n(p))if d(a,b)inr(n). This endomap is weakly constant. In the positive case both outputs are r; in the negative case n(p):0 eliminates to an identification between the outputs. Write the resulting path as κa,b(p,q):fa,b(p)=fa,b(q).

Path induction on p:Ea,b gives p=fa,a(refl)1fa,b(p). The reflexivity case is the inverse law from theorem 30.20. For p,q:Ea,b, write (P) for (80.12) and (K) for apλr.fa,a(refl)1r(κa,b(p,q)). Then p=(P)fa,a(refl)1fa,b(p)=(K)fa,a(refl)1fa,b(q)=(P),backwardsq. Thus the supplied decision procedure gives UIP at this occurrence; no ambient UIP axiom is used. ◻

Theorem 80.23 — Generic decidable equality

Let D:IEqDesci(I). Equality on Mu(D)(j) is decidable for every j:I.

Proof of Theorem 80.23 — Generic decidable equality

Proof. Define a bounded comparison by ordinary natural-number recursion with the fuel bound in its domain. Here m>n abbreviates suc(n)m: DecEqA(x,y):=IdA(x,y)+¬IdA(x,y). eqN:j:It,u:Mu(D)(j)N>sizeD(t)+sizeD(u)DecEqMu(D)(j)(t,u). At zero, the bound implies 0>sizeD(t)+sizeD(u) and eliminates to the required decision. Thus the zero clause does not manufacture a disequality. At a successor, compare the two layers obtained by outj. The completeness invariant is N>sizeD(t)+sizeD(u). For arbitrary t,u:Mu(D)(j), invoke it initially at N:=sizeD(t)+sizeD(u)+suc(0). The successor clause uses a second recursion on the code. Unit layers agree. Constant positions use the decision stored in K(A,dA). A sum first compares its injections; equal injections recurse on their payloads, and unequal injections use disjointness. Products compare the left components and then the right components, combining positive paths by pair congruence and transporting a negative component decision backwards along the corresponding projection. Recursive positions invoke eqpred(N) on the selected proper subtrees. If t,u are those children, two applications of lemma 80.13, followed by addition, give sizeD(t)+sizeD(u)+2sizeD(t)+sizeD(u). Hence (80.14) implies pred(N)>sizeD(t)+sizeD(u), the completeness hypothesis for the recursive call.

The definition is accepted because it is structurally recursive on N; the size inequality proves completeness rather than termination. For soundness, congruence of rollj turns an accepted layer path into a path between terms. For completeness, apply apoutj() to any path between the two rolls, obtaining a layer path, and then recurse on the code. Products split their path into component paths. Sums use constructor disjointness and injectivity. No dependent tag witness or function-space constructor occurs in this grammar; adding either requires a new coherence or finite-enumerability argument. In particular, lemma 80.22 is not used to enlarge the accepted code fragment. ◻

Let dN be the usual decision procedure on natural numbers and let DTree(N)= be the EqDesc code obtained from the tree code by storing dN at its constant position. Its first nontrivial tests expose the sum tag before any recursive call: eq3(leaf(1),leaf(1);π2<3)inl(refl),eq5(fork(leaf(1),leaf(2)),leaf(1);π4<5)inr(ntag). Here π2<3 and π4<5 are the canonical natural-order witnesses, and ntag is obtained by disjointness of inl and inr; the rejected comparison does not inspect either subtree.

Construction 80.24 — Elaborating a surface declaration

Consider the declaration dataTree(A)whereleaf:ATree(A),fork:Tree(A)Tree(A)Tree(A). Elaboration first assigns one sum tag per constructor. The leaf arguments give K(A); the two fork arguments give X()×X(). Hence the result is DTree(A) defined in section 80.1. Constructor elaboration produces the definitions of leaf and fork given there. The positivity checker rejects an argument Tree(A)A because no code constructor translates a recursive occurrence on the left of an arrow. The elaborator therefore produces either a code and constructor translations or the structured diagnostic NegativeRecursiveOccurrence. This diagnostic reports the rejected class but does not retain a source-location path through the surface type.

Theorem 80.25 — Soundness of the displayed elaboration

Suppose a surface declaration has well-formed parameters, every constructor result is the declared family at a well-formed index, and the argument elaborator accepts every recursive occurrence as strictly positive. If it returns a description D and constructor translations c, then D is well formed in the selected description universe and every ci has the source constructor type with the declared family replaced by Mu(D). In particular the displayed Tree declaration elaborates to DTree(A) and both generated constructors are well typed.

Proof of Theorem 80.25 — Soundness of the displayed elaboration

Proof. Induct on the constructor list, and inside one constructor induct on its argument telescope. A nonrecursive argument B contributes K(B) after its source formation derivation establishes B:Ui. A recursive argument at index j contributes X(j) after the result-index premise establishes j:I. Products concatenate fields and sums concatenate constructors, preserving well-formedness by the code formation rules. The strict-positivity premise excludes the only unsupported case, a recursive occurrence to the left of an arrow. Finally Mu-intro wraps each interpreted layer. Unfolding the interpretation gives the original constructor telescope, with recursive occurrences replaced by Mu(D), so each generated ci has the claimed type. The Tree calculation is the two code clauses displayed in construction 80.24.

This is the local specialization of the soundness shape in Dagand–McBride, Theorem 4 ['EDM12]: their conclusion is validity of the generated declaration after successful elaboration. We have proved it only for the surface grammar and target codes displayed here; no completeness or equivalence with a host language’s inductive declarations follows. ◻

The elaborated code, its fold, its induction principle, and its equality test compose in one bounded derivation: the Tree declaration elaborates to a code, its fold comes from (80.2), its induction principle is definition 80.4, and its equality test uses theorem 80.23 when A has decidable equality. No container representation or row-polymorphic extensibility has entered the argument. Extensible generic datatypes additionally assume decidable labels, row membership, and coherent row permutation; those assumptions are not rules of Desci(I).

Exercise 80.4

★★★ Extend the code grammar by pi(A,F) with interpretation a:A[[F(a)]](X). Prove that the recursive family remains in positive position. State the universe level of the code and interpretation. Then identify the extra hypotheses needed for generic traversal and decidable equality; in particular, explain why decidable equality of each codomain does not decide equality of functions when A is infinite.

A binding description

Ordinary descriptions treat every recursive child at the same scope. A lambda body instead lives in a context extended by the bound variable. A regular recursive-position code cannot distinguish these two cases. Binding descriptions repair exactly that defect by storing the telescope introduced above each recursive child.

Let S:Ui be a type of object sorts, let Ctx(S) be lists of sorts, and let ext(Ξ,Γ) denote the context obtained by placing the finite telescope Ξ before Γ: ext([],Γ):=Γ,ext(B::Ξ,Γ):=B::ext(Ξ,Γ).

Definition 80.26 — Binding descriptions and free syntax

A binding description over S has codes bone,bK(A),Eb+G,Eb×G,bsigma(A,H),rec(Ξ,B), where A:Ui, B:S, and Ξ:Ctx(S). For a scoped family X:(Γ:Ctx(S))SUi, interpretation at Γ is the regular unit, constant, sum, product, and Sigma interpretation, with the recursive clause ElΓ(rec(Ξ,B),X):=X(ext(Ξ,Γ),B).

Fix a constructor code F:SBDesci(S) and a variable family V:(Γ:Ctx(S))SUi. The free syntax TmF(V;Γ,A) has constructors var:V(Γ,A)TmF(V;Γ,A),con:ElΓ(F(A),TmF(V;,))TmF(V;Γ,A). Its induction principle has one method for variables and, at a constructor, one induction hypothesis for every rec(Ξ,B) position. Thus the code, rather than an operation defined later, determines where the context is extended.

Suppose X is scoped over Γ and Y over Δ. A family hΞ,B:X(ext(Ξ,Γ),B)Y(ext(Ξ,Δ),B) induces bmapE(h):ElΓ(E,X)ElΔ(E,Y) by recursion on E. It applies hΞ,B at rec(Ξ,B) and follows the remaining code constructors.

Lemma 80.27 — Binding-layer action laws

Let E:BDesci(S), and let X,Y,Z be scoped families over Γ,Δ,Θ. Let hΞ,B:X(ext(Ξ,Γ),B)Y(ext(Ξ,Δ),B),kΞ,B:Y(ext(Ξ,Δ),B)Z(ext(Ξ,Θ),B). For u:ElΓ(E,X), binding-layer action satisfies bmapE(λΞ.λB.id,u)=u,bmapE(k,bmapE(h,u))=bmapE(λΞ.λB.λx.kΞ,B(hΞ,B(x)),u). If h has the type of h and pΞ,B,x:hΞ,B(x)=hΞ,B(x) at every recursive position, then bmapE(h,u)=bmapE(h,u).

Proof of Lemma 80.27 — Binding-layer action laws

Proof. Induct on E. At rec(Ξ,B) the three claims are respectively the identity equation, composition equation, and pointwise path. Unit and constants give reflexivity. Sums follow their injection, products use the two induction hypotheses, and Sigma codes retain their tag and use the branch induction hypothesis. These cases prove all three laws. ◻

Take V(Γ,A):=Var(Γ,A), the intrinsically typed de Bruijn variables. For B:S, their newest and older constructors have types vz:Var(B::Γ,B),vs:Var(Γ,A)Var(B::Γ,A). Define Ren(Γ,Δ):=(A:S)Var(Γ,A)Var(Δ,A),SubF(Γ,Δ):=(A:S)Var(Γ,A)TmF(V;Δ,A). A renaming lifts through a telescope Ξ. For one new sort B, let wkBΓ(x):=vs(x) and define lift[B](ρ)(vz):=vz,lift[B](ρ)(vs(x)):=vs(ρ(x)). Put lift[](ρ):=ρ,liftB::Ξ(ρ):=lift[B](liftΞ(ρ)).

Free-syntax induction defines generic renaming: renF(ρ,var(x)):=var(ρ(x)),renF(ρ,con(u)):=con(bmapF(A)(λΞ.λB.renF(liftΞ(ρ)),u)). A substitution lift may now use this renaming. Define lift[B](σ)(vz):=var(vz),lift[B](σ)(vs(x)):=renF(wkBΔ,σ(x)). Put lift[](σ):=σ,liftB::Ξ(σ):=lift[B](liftΞ(σ)). Thus every newly introduced variable is fixed, and every older image is weakened through the whole telescope. Free-syntax induction then defines generic substitution: subF(σ,var(x)):=σ(x),subF(σ,con(u)):=con(bmapF(A)(λΞ.λB.subF(liftΞ(σ)),u)). Every context change occurs at the recursive-position clause that names its telescope.

Theorem 80.28 — Generic renaming and substitution laws

Let F:SBDesci(S) be any binding signature. Define environment composition pointwise at the following types. For ρ:Ren(Γ,Δ) and τ:Ren(Δ,Θ), define rcomp(τ,ρ):Ren(Γ,Θ). For σ:SubF(Γ,Δ) and τ:SubF(Δ,Θ), define τσ:SubF(Γ,Θ). The two mixed composites have types rsub(σ,ρ):SubF(Γ,Θ)(ρ:Ren(Γ,Δ),σ:SubF(Δ,Θ)),rpost(ρ,σ):SubF(Γ,Θ)(σ:SubF(Γ,Δ),ρ:Ren(Δ,Θ)). Their values at a variable are rcomp(τ,ρ)(x):=τ(ρ(x)),(τσ)(x):=subF(τ,σ(x)),rsub(σ,ρ)(x):=σ(ρ(x)),rpost(ρ,σ)(x):=renF(ρ,σ(x)). For every t:TmF(V;Γ,A), renaming identity holds. If ρ:Ren(Γ,Δ) and τ:Ren(Δ,Θ), renaming composition holds: renF(id,t)=t,renF(τ,renF(ρ,t))=renF(rcomp(τ,ρ),t). Substitution identity holds. If σ:SubF(Γ,Δ) and τ:SubF(Δ,Θ), substitution composition holds: subF(var,t)=t,subF(τ,subF(σ,t))=subF(τσ,t). For ρ:Ren(Γ,Δ) and σ:SubF(Δ,Θ), substitution after renaming is subF(σ,renF(ρ,t))=subF(rsub(σ,ρ),t). For σ:SubF(Γ,Δ) and ρ:Ren(Δ,Θ), renaming after substitution is renF(ρ,subF(σ,t))=subF(rpost(ρ,σ),t). Finally, if ρ,ρ:Ren(Γ,Δ) satisfy pA,x:ρA(x)=ρA(x) for every A:S and x:Var(Γ,A), then renF(ρ,t)=renF(ρ,t). If σ,σ:SubF(Γ,Δ) satisfy qA,x:σA(x)=σA(x) pointwise, then subF(σ,t)=subF(σ,t).

Proof of Theorem 80.28 — Generic renaming and substitution laws

Proof. The proof is staged because lift compatibility for substitution composition uses both mixed laws on arbitrary substitution images.

First prove renaming congruence, identity, and composition by free-syntax induction. Variable cases are the corresponding environment paths. For a constructor con(u), the recursive-position premises of lemma 80.27 are the induction hypotheses. Induction on Ξ and variable elimination give liftΞ(id)id,liftΞ(rcomp(τ,ρ))rcomp(liftΞ(τ),liftΞ(ρ)), where ee means pointwise equality. A newest variable gives reflexivity; an older variable reduces the second line to vs(τ(ρ(x)))=vs(τ(ρ(x))).

Next prove substitution congruence and identity by the same syntax induction. The older-variable clause of the lifted identity is renF(wkBΓ,var(x))var(vs(x)). Prove substitution after renaming next. At an older variable, preservation of rsub is definitional. Abbreviate σ^:=lift[B](σ) and ρ^:=lift[B](ρ). Then rsub(σ^,ρ^)(vs(x))renF(wkBΘ,σ(ρ(x)))lift[B](rsub(σ,ρ))(vs(x)).

Renaming after substitution follows by syntax induction using the renaming composition law already proved. Its older-variable lift comparison is rpost(lift[B](ρ),lift[B](σ))(vs(x))=definitionrenF(lift[B](ρ),renF(wkBΔ,σ(x)))=renaming compositionrenF(rcomp(lift[B](ρ),wkBΔ),σ(x))=variable elimination and renaming congruencerenF(rcomp(wkBΘ,ρ),σ(x))=renaming composition, backwardsrenF(wkBΘ,renF(ρ,σ(x)))=definitionlift[B](rpost(ρ,σ))(vs(x)).

It remains to prove substitution composition. The newest-variable lift case is reflexivity. At an older variable, the two mixed laws give (lift[B](τ)lift[B](σ))(vs(x))=substitution after renamingsubF(rsub(lift[B](τ),wkBΔ),σ(x))=pointwise lift equationsubF(rpost(wkBΘ,τ),σ(x))=renaming after substitution, backwardsrenF(wkBΘ,subF(τ,σ(x)))=definitionlift[B](τσ)(vs(x)). Induction on Ξ iterates each one-sort comparison. Binding-layer congruence converts the resulting pointwise lift paths into constructor paths. At no stage is an equality between environment functions used. ◻

Let wkBΓ:Ren(Γ,B::Γ) be the inclusion. For τ:SubF(Γ,Δ) and t:TmF(V;Γ,A), the final equation of theorem 80.28, followed by the substitution-after-renaming equation, gives the naturality law renF(wkBΔ,subF(τ,t))=subF(lift[B](τ),renF(wkBΓ,t)). Indeed, both sides are substitutions into t. On a variable x their environments reduce to renF(wkBΔ,τ(x)); generic environment congruence proves the displayed equality. The same derivation applies in an extended context because the theorem quantifies over arbitrary source and target contexts.

For simply typed lambda terms, take S:=Ty, generated by o and arrows, and define Fλ(A):=bsigma(Ty,B.rec([],BA)b×rec([],B))b+Abs(A),Abs(o):=bK(0),Abs(BC):=rec([B],C). Write Tm(Γ,A) for TmFλ(Var;Γ,A). The two constructor summands are app and lam. The generic equations become ren(ρ,app(t,u))app(ren(ρ,t),ren(ρ,u)),ren(ρ,lam(t))lam(ren(lift[B](ρ),t)),sub(σ,lam(t))lam(sub(lift[B](σ),t)).

The four concrete representations in chapter 23 instantiate different boundaries of this theorem. Typed de Bruijn syntax is the displayed Var instance. Locally nameless syntax changes the variable family but retains telescope lifting. PHOAS replaces environments by a parametric host family, so its exclusion of exotic terms still requires the parametricity theorem proved there. Contextual syntax exposes Γ in open terms and substitutions. The generic theorem gives the common scope-safe traversal and fusion argument; it does not give LF adequacy, nominal freshness, or PHOAS parametricity.

Exercise 80.5

★★☆ Prove the final equation of theorem 80.28 for an abstraction lam(t). Write both lifted environments on an arbitrary newest variable and on an arbitrary older variable before invoking induction on t.

Suggested first pass.

Begin with exercise 80.6, exercise 80.7.

Exercise 80.6

★★☆ For binary trees, define a mirror algebra and a node-counting algebra. Use theorem 80.11 to prove that node count is invariant under mirror. Expand the algebra-commuting hypothesis in the leaf and fork summands.

Exercise 80.7

★★☆ Reconstruct the renaming-after-substitution equation of theorem 80.28 at a constructor con(u). State the recursive-position premise supplied by lemma 80.27. Then specialize it to the lam constructor, where the stored telescope is [B].

Exercise 80.8

★★☆ Run the bounded equality procedure on the two layers (false,) and (true,(l,r)) of DTagged. Identify the first rejected comparison. Then remove the decision procedure for 2 from the Sigma equality data and state the exact algorithmic clause that can no longer be executed.

Exercise 80.9

★★★ Practical project.generic-description-interpreter Implement in Kappa the six constructors of Desci(I), their interpretation for regular codes, roll, generic map, fold, and size. Maintain the invariant that every recursive position is interpreted by the family parameter and no negative occurrence is accepted. Elaborate the displayed Tree declaration, construct fork(leaf(1),leaf(2)), and produce size 3 and the leaf list [1,2]. Represent the Boolean-indexed instance of sigma by a tag-to-code lookup, and test that its false tag accepts a unit payload while its true tag accepts a pair of recursive payloads; both crossed payload shapes must be rejected. The acceptance test must also reject a declaration with constructor argument Tree(A)A, reporting a negative recursive occurrence.

Sources. The principal indexed syntax is the ISPT/SPF universe of Morris, Altenkirch, and Ghani, pp. 6–12 [MAG09]; its separate indexed-regular equality fragment is the one isolated on pp. 18–20. The finite regular normal form and its generic programs reconstruct the source-bounded method of Benke, Dybjer, and Jansson, Universes for Generic Programs and Proofs in Dependent Type Theory [BDJ03]; it does not replace the principal indexed language. The surface elaboration and local soundness theorem follow the judgment shape and proof decomposition of Dagand and McBride, pp. 12–16 ['EDM12]. The binding instance follows the type- and scope-safe description discipline of Allais, Atkey, Chapman, McBride, and McKinna [AAC^+21]. Hubers and Morris add extensible rows to a different generic universe [HM23b]; no row assumption is used in the core theorems here.

Search the book

Type to search the local edition.