Inductive types and the associated recursion and induction functions are the subtlest part of HoTT. There are three levels at which we can implement these:
Nat
. This allows efficiency and simplification for recursion and induction functions, and should be done exactly when these are needed.This note concerns the middle case, with the subtleties of defining induction and recursion.
import $ivy.`io.github.siddhartha-gadgil::provingground-core-jvm:0.1.0`
As the implementation is self-contained and in the core, we just load the jar of the core.
import provingground._, induction._, coarse._
//import RecursiveDefinition._
import BaseConstructorTypes._
Booleans and natural numbers are defined in BaseConstructorTypes, for illustration and testing. These are called SmallBool
and SmallNat
to avoid clashes with the ScalaRep
based implementations.
At present a Constructor
is a constructor function with an associated pattern, with the cons
attribute the function itself. Should eventually rename these.
The Boolean type only uses the constant/identity constructor pattern. This is used to give two Constructors and their associated functions.
val ttC = W.constructor(SmallBool, "true")
val ffC = W.constructor(SmallBool, "false")
val tt : Term = ttC.cons
val ff : Term = ffC.cons
val BoolCons = List(ttC, ffC)
The constructors have type booleans
tt.typ
ff.typ
A recursive definition is specified by data associated to each constructor. This data is of type given by the recDataTyp
method of the constructor pattern, depending on the target type.
In the case of Booleans, this is the target type.
W.recDataTyp(SmallBool, SmallBool)
W.recDataTyp(SmallBool, SmallNat)
W.recDataTyp(SmallBool, HoTT.Type)
res7 == HoTT.Type
The action of a recursion functions is defined recursively, in terms of cases. This is implemented as a diagonal construction, with a method, in the trait RecFunction
definining the recursion function in terms of a function of its own type, and applying this to itself.
import HoTT._
The definitions are based on a double induction, over the structure of the constructor and the number of constructors. The former takes place essentially in constructor patterns, while the latter is implemented in ConstructorSeq.
import ConstructorSeq.recFn
recFn(BoolCons, SmallBool, SmallBool)
val recBoolBool =
recFn(BoolCons, SmallBool, SmallBool).asInstanceOf[Func[Term, Func[Term, Func[Term, Term]]]]
val neg = recBoolBool(ff)(tt)
neg(tt)
ff
neg("x" :: SmallBool)
val x = "x" :: SmallBool
neg(neg(x))
neg(neg(x)) == x // not equal by definition, needs a theorem proved by induction