Efficient lambda encodings for Mendler-style coinductive types in Cedille
Abstract
In the calculus of dependent lambda eliminations (CDLE), it is possible to define inductive datatypes via lambda encodings that feature constant-time destructors and a course-of-values induction scheme. This paper begins to address the missing derivations for the dual, coinductive types. Our derivation utilizes new methods within CDLE, as there are seemingly fundamental difficulties in adapting previous known approaches for deriving inductive types. The lambda encodings we present implementing coinductive types feature constant-time constructors and a course-of-values corecursion scheme. Coinductive type families are also supported, enabling proofs for many standard coinductive properties such as stream bisimulation. All work is mechanically verified by the Cedille tool, an implementation of CDLE.
1 Introduction
Inductive (algebraic) datatypes, such as natural numbers and lists, serve the crucial role in functional languages of structuring the definitions of functions over (necessarily) finite objects. To each such datatype is associated an eliminator, a combinator for defining provably terminating functions over such objects, justifying structurally recursive definitions. Less familiar is the notion of coinductive (coalgebraic) datatypes, which serve to structure the definitions of functions generating (possibly) infinite objects. An example of a codatatype is the type of streams, which are infinite lists of some element type. Analogously, associated with each coinductive type is a generator, a combinator for giving definitions of such objects that are provably productive (that is, all finite observations made of these objects are defined).
Usually, inductive and coinductive types are built-in primitives to functional languages. Though they are sometimes declared via the same syntactic mechanisms (as in Haskell), in total functional languages and especially in implementations of dependent type theories they are better kept separate. As an example of such theory, the calculus of constructions [8] (CC) is extended by the calculus of inductive constructions [40] (CIC) with primitive inductive datatypes each with an associated induction principle; syntactically, they are given by a declaration listing the constructors generating elements of the datatype and used by pattern matching and recursion. CIC can be further extended with primitive coinductive types, and an elegant syntactic proposal for them comes from Abel et al. [2]: giving these by a declaration listing their destructors and generating them via copattern matching. The elegance of this proposal lies in its close fit with the semantic account of coinductive types developed by Hagino [15].
The calculus of dependent lambda eliminations [31, 33] (CDLE) is a compact Curry-style type theory with no primitive notion of (co)inductive datatypes. Instead, datatypes are encoded as lambda expressions. Historically, lambda encodings have languished due to several difficulties, which CDLE overcomes. Geuvers [13] showed the impossibility of deriving induction for them in second-order dependent type theory; Firsov and Stump [11] demonstrated how to generically derive the induction principle for them in CDLE. Parigot [28] showed that the Church-style of encoding has no better than linear-time data accessors (such as predecessor for natural numbers); Firsov et al. [9] showed how to use the induction principle for a Mendler-style of encoding [25] to define efficient (constant-time) data accessors. Furthermore, Firsov et al. [10] show how to further augment this with course-of-values induction, an expressive scheme wherein the inductive hypothesis can be invoked on subdata at unbounded depth.
The present paper partially addresses the gap in the foregoing account of datatypes in CDLE by presenting a generic derivation of lambda-encoded coinductive data, giving analogous results for efficiency (for constructors) and expressivity (using course-of-values coiteration). What we do not show here, however, is a “true coinduction” principle in the sense of “bisimilarity implying equality” – indeed, a negative result concerning Lambek’s lemma (a consequence of coinduction) indicates coinduction in this sense is not possible for our encoding. However, our derived codatatypes support indexed coiteration, which we argue suffices for implementing many standard examples of coinductive reasoning such as showing that two streams are bisimilar. Since efficiency for Mendler-encoded data came as a consequence of induction in [9], lacking true coinduction we cannot directly apply their method. Instead, we use (monotone) recursive types to achieve efficiency (a technique deployed for similar effect in e.g., [14, 23, 19, 20]), a feature which is itself derivable within CDLE.
Contributions
Summarizing, in this paper we:
-
•
generically derive Mendler-style lambda encodings for coinductive datatypes in CDLE using derivable monotone recursive types;
-
•
show that codata so derived forms an adequate basis for functional programming due to the expressive course-of-values corecursion scheme it supports for generating codata and their efficient codata constructors (in the sense that the run-time overhead of every use of a constructor is constant in the number of observations made on the codata);
-
•
show that our derived codata supports a proof principle in the form of indexed course-of-values corecursion, which suffices for proving many standard examples of coinductive properties such as relational properties over corresponding elements of two streams;
-
•
and describe a negative result concerning Lambek’s lemma that indicates the impossibility of coinduction in the sense of “bisimilarity implying equality” (with respect to CDLE’s built-in equality) for this encoding; this in turn explains the use of monotone recursive types, as the lack of such a coinduction principle raises fundamental difficulties in adapting for this encoding previous approaches for deriving efficient encodings for inductive types in CDLE.
The remainder of this paper is organized as follows: in Section 2 we review CDLE and Mendler-style corecursion schemes; in Section 3 we detail the generic derivation of coinductive data, discussing its strengths and the above-mentioned negative result; in Section 4 we give concrete examples of programming with streams using expressive corecursion schemes supported by our derived codatatypes; in Section 5 we argue that the indexed coiteration scheme they support enables proofs of many properties of interest for coinductive types such as streams; in Section 6 we discuss related and future work; and we conclude in Section 7. All code found in listings can be found at https://github.com/cedille/cedille-developments and is checkable by the Cedille tool, and implementation of CDLE available at https://github.com/cedille/cedille/releases.
2 Preliminaries
2.1 CDLE
We begin with a review of the CDLE, the type theory of Cedille. CDLE is an extension of impredicative Curry-style (i.e., extrinsically typed) CC that overcomes some traditional short-comings of lambda encodings in type theory (e.g., underivability of induction for them in second-order dependent type theory [13]) while maintaining a compact formal description: Cedille Core, a minimal specification of CDLE, can be implemented in 1K Haskell LoC [34]. CDLE accomplishes this by adding three new type constructs: equality of untyped terms (); the dependent intersections () of Kopylov [17]; and the implicit (erased) products () of Miquel [26]. The pure term language of CDLE is the just the untyped -calculus; to make type checking algorithmic, terms in Cedille are given with typing annotations, and definitional equality of terms is modulo erasure of annotations. Figures 1 and 12 (Appendix A) gives the typing and erasure rules for the fragment of CDLE relevant to this paper. In particular, the details for dependent intersections are given in the appendix as they are not required understanding the main results of this paper – they are used in Appendix B to implement the derived type constructors of Section 2.2. A complete reference for the syntax, typing, and erasure, along with some meta-theoretic results, can be found in [33].
Equality
is the type of proofs that the erasures of and ( and , resp.) are equal. It is introduced with , which proves for any whose free variables are declared in the typing context. The term erases to , similar to the lambda encoding for Leibniz equality in CC. Combined with definitional equality, proves for terms and whose erasures are -convertible.
We eliminate equality proofs with , a substitution principle, and with , which provides the principle of explosion for a certain contradictory equation. For substitution with , if the expected type of the expression is , and proves , then is checked against the type . The expression erases to , making equality in Cedille proof-irrelevant (in contrast to theories like CIC and MLTT). For , if proves that the lambda encoding of the Boolean for true is equal to the encoding for false, has type , for any type . The expression erases to , so again the proof of equality is computationally irrelevant. As a convenience, the Cedille tool implements for the Böhm-out algorithm [7] so that it may be used on proofs for any closed and normalizing terms and whose erasures are -inconvertible.
Implicit product
is the type of dependent functions with an erased (computationally irrelevant) argument of type and a result of type . They are introduces with an abstraction where we require that does not occur free in the erasure of , permitting the erasure of to be . Because of this restriction on , erased arguments play no computational role in the body and thus exist solely for the purposes of typing.
Terms having an implicit product type are eliminated with erased application: if has type , then the erased application has type and erases to . When does not occur free in the type , we write for , similar to for non-dependent non-implicit products.
Inherited type constructs from CC.
Figure 1 omits typing and erasure rules for the term and type constructs of CC. In terms, all type abstractions (so, is used for introducing polymorphic terms as well as for functions with erased term arguments) erase to , and all term-to-type applications erase to . In types, also quantifies over types, is a (non-implicit) dependent product, and introduces a type-level function. In code listings, we omit type arguments and annotations when Cedille can infer these.
2.2 Derived constructs
Our derivation of coinductive datatypes makes use of several constructions which are themselves derived within CDLE. However, due to space restrictions we do not detail their definitions, instead choosing to present them axiomatically with type inference rules and (when appropriate) rules for definitional equality. These include familiar non-recursive datatypes – Pair for pairs and Unit for the singleton type (Figure 3) – all derivable following a similar approach as described by Stump [32]. Some of the derived type constructors are more exotic (Figure 2): Cast for zero-cost type coercions, Mono for internalized monotonicity witnesses of type schemes, and a recursive type former Rec. We describe this latter kind, whose complete derivation in Cedille is listed in Appendix B (and in the code repository for this paper), and are described at length by Jenkins and Stump in [16]. In order to support indexed coinductive types, the definitions in Figure 2 and through Section 3 make use of an indexing type .
Cast, zero-cost type coercions.
For type families and of kind , is the type of generalized identity functions in CDLE; its formation, introduction, and elimination rules are given in Figure 2(a). This family of types was first introduced by Firsov et al. in [9], wherein it is called Id, and only the non-indexed variant is given (for the related notion of zero-cost coercion in Haskell, see [6]). Since CDLE is Curry-style, an identity function from to might exists even if and are inconvertible types. Terms of type are introduced with (that is, with both arguments erased), where has type and (of type ) is a proof that behaves extensionally like the identity function (there is no need to write the equality as , as equality for terms is modulo erasure). Casts are eliminated with elimCast: if has type and has type , then is a function from to , which (crucially) is definitionally equal to (indicated by the notation in the figure). Since elimCast takes its cast argument erased, Cast is also proof-irrelevant. Constructs castRefl and castTrans give us that casts form a preorder on type families (there can be at most one identity function between any two type families) and can be defined in terms of intrCast and elimCast. Both castRefl and castTrans are definitionally equal to as well.
Mono, internalized positivity.
Given , (Figure 2(b)) is the type of proofs that the type scheme is monotonic. Monotonicity for type schemes (as opposed to syntactic positivity) as presented here resemble the work of Matthes [22, 20], in which monotonicity is given by “terms of functorial strength”. Terms of type are introduced by intrMono, which takes as an argument some which (similar to the more familiar morphism mapping of a functor in category theory), for all type families and of kind , transforms an (erased) cast from to to a cast from to . A witness of positivity of type can be eliminated with to a function of type , where has type (for some type families and ) and has type . The entire expression is definitionally equal to (notice that all arguments to elimMono are erased).
Rec, a recursive type former.
Given , (Figure 2(c)) is a fixedpoint of (strictly speaking, it is the least fixedpoint in the pre-order on type schemes induced by Cast). It is well-known that unrestricted formation and use of recursive types is unsound when interpreting type theories under the Curry-Howard isomorphism, and so accordingly the introduction (roll) and elimination (unroll) of terms with recursive types requires evidence that the type scheme whose fixedpoint was taken is monotonic. If has type and has type , then is a function taking some term of type and producing a term of type . The situation is symmetric for unroll.
In type theories with primitive iso-recursive types, roll and unroll must form an isomorphism. Usually, definitional equality is extended with the -law for Rec, meaning reduces to , with the -law possibly holding only meta-theoretically. The derived recursive types of CDLE differ from this in that both roll and unroll are each themselves definitionally equivalent to ; in this respect, recursive types in CDLE are akin to equi-recursive types as the terms and are definitionally equivalent (for of type ), as well as the terms and (for of type ).
2.3 Coalgebras and coiteration schemes
In category theory, coinductive datatypes are understood as final -coalgebras [15]. Our generic derivation rests upon this understanding, but in order to support efficient constructors and course-of-values corecursion we find it convenient to have our semantic account begin with Mendler-style -coalgebras. The dual notion, the Mendler-style -algebra, is discussed in more depth by Uustalu and Vene [36] and by Vene [38]; the following categorical account is a straightforward adaptation of this description for -coalgebras.
Definition 2.1 (Ordinary -coalgebras).
Assuming is a functor, the usual definition of an -coalgebra is a pair where is an object (e.g., a type) called the carrier of the algebra and is a morphism (e.g., a function) from to called the action of the coalgebra.
Translated to type theory, the actions of -coalgebras are expressed by the family of types
Definition 2.2.
(Mendler-style -coalgebras) A Mendler-style -coalgebra is a pair where the carrier is an object and the action is a natural transformation (e.g., a polymorphic function) that, for every object in , maps elements of (the set of morphisms from to ) to elements of .
Translated to type theory, the actions of Mendler -coalgebras are expressed by the family of types:
In functional programming, the operational reading of the type of an action of an ordinary -coalgebra is that it is a function taking some “seed value” of type and producing some “structure” of type . For an action of a Mendler -coalgebra polymorphic in , it instead produces a structure of type given both a seed value of type and a function for transforming such seed values to values of type .
Definition 2.3 (Final Mendler -coalgebras).
A final Mendler-style -coalgebra is one such that for every other Mendler-style -coalgebra , there exists a unique morphism (called the Mendler anamorphism of ) such that
where subscripts on natural transformations indicating component selection (e.g., polymorphic instantiation) and denotes the functorial lifting of to a morphism from to . This condition can be alternatively be stated as saying that, for any Mendler -coalgebras , is the unique morphism making the diagram below commute:
In type theory, the carrier of the final Mendler -coalgebra can be given as:
or encoded as if existentials and products are unavailable.
Intuitively, is the generic destructor for the coinductive datatype with pattern functor , and for any Mendler -coalgebra , is a generator for . The equations express how to compute the observations for codata generated with : these are given by simply calling with the generator , or equivalently by first calling with a trivial generator , then mapping over the resulting structure with the generator .
Mendler-style coiteration
The Mendler-style anamorphism translates to a coiteration scheme for codata, whose typing and computation rules are given below (with below corresponds to above).
Read operationally, the type of suggests that it will, from a seed value of type and function for making coiterative calls (where is universally quantified over), construct an -collection of subdata, corresponding to one additional observation that can later be made on the codata being generated.
Mendler-style corecursion
The Mendler-style corecursion scheme (categorically, the apomorphism) can be described by equipping the coalgebra action with an additional argument of type , supporting an alternative method for constructing codata – a way of “short-cutting” coiteration by injecting codata directly into the abstracted type rather than generating it from values of type . We expect that the type argument of will always be instantiated to , and thus that can be given for this additional argument:
Mendler-style course-of-values coiteration
The Mendler-style course-of-values coiteration scheme (categorically, the futumorphism) can be given by equipping with an argument of type , another alternative to coiteration for constructing codata that takes an -collection of subdata. This additional argument enables to construct an arbitrary number of observations for the codata being generated in addition to those produced by coiteration; put another way, the result that produces may have some sub-component built using this additional argument from another expression, which itself would be later accessed by making (at least) two observations on the generated codata. We expect that (that is, the generic codata constructor) can be given for this argument:
A categorical account of the corecursion scheme for ordinary -coalgebras is given by Geuvers [12], and an account of the recursion scheme and course-of-values iteration scheme (duals to the corecursion scheme and course-of-values coiteration scheme) for Mendler-style -algebras is given by Vene [38]. For understanding the derivation of coinductive data in the next section, which supports a combination of the corecursion and course-of-values coiteration schemes which we call course-of-values corecursion, the above type-theoretic account suffices.
3 Generic lambda encoding for codata
Figures 4, 5, and 6 give the complete derivation of coinductive data in Cedille. This derivation is generic, in that it works for any monotone type scheme of kind (where monotonicity is expressed as evidence of type ). , , and are all module parameters to the derivation, and the curly braces around indicate that it is an erased module parameter (as suggested by the type inference and definitional equal rules in Figure 2(b), the type of monotonicity witnesses is proof-irrelevant). We walk through code listings of these figures in detail.
import utils. module nu (I: ) (F: (I ) I ) {cm: Mono I F}. CoAlgM : (I ) (I ) = X: I . C: I . R: I . Cast I C R ( i: I. F R i R i) ch: ( i: I. X i R i). i: I. X i F R i. NuF : (I ) I = C: I . i: I. Y: I . ( X: I . X i CoAlgM C X Y i) Y i. Nu : I = Rec I NuF.
CoAlgM.
In Figure 4, our variant Mendler-style -coalgebra,
CoAlgM, generalizes the description given in Section
2.3 to type families X: I .
CoAlgM takes an additional type family argument C, and
describes a family of polymorphic functions whose arguments facilitate a
combined course-of-values corecursion scheme for codata.
The variable C stands in for occurrences of the generic codatatype
Nu – which itself recursively defined in terms of CoAlgM.
In the body of the definition, the first additional argument is a cast
from C to the quantified R, enabling corecursion
by allowing us to produce (for any i
of type I
) some
term of type R i from some pre-existing codata of type C i,
rather than via ch.
The second argument is an abstract constructor: given some collection of
subdata of type F R i (for any i), it builds a value of type
R i.
This additional argument enables course-of-values
coiteration: for each single step of codata generation, an arbitrary number of
observations that will be made of the codata can be constructed.
The remaining arguments are the same as for ordinary Mendler-style -coalgebras:
ch is the handle for making coiterative calls, and the
argument of type X i is the value from which we are to coiteratively construct the
result of type F R i.
NuF and Nu.
Type family NuF is defined using the standard type for lambda encodings of existentials and products, and its body is more naturally read as X: I . X i CoAlgM C X. This is similar to the standard definition of the greatest fixpoint of F (Section 2.3, see also [39]). NuF is parameterized by a type family C: I standing in for recursive reference to a fixpoint of itself. That fixpoint is Nu, defined as Rec I NuF.
monoCoAlgM : X: I . Mono I ( C: I . i: I. CoAlgM C X) = X. intrMono -( C1. C2. c. intrCast -( i. coa. R. c’. coa -(castTrans -c -c’)) -( i. coa. )) . monoNuF : Mono I NuF = <..> nuRoll : i: I. NuF Nu i Nu i = i. roll -monoNuF -i . nuUnroll : i: I. Nu i NuF Nu i = i. unroll -monoNuF -i .
monoCoAlgM, monoNuF, nuRoll, and nuUnroll
As discussed earlier, recursive types must be restricted in some fashion to positive (monotonic) type schemes only. The term monoNuF in Figure 5 (definition omitted, indicated by <..>) proves that the type scheme NuF is positive; it uses evidence monoCoAlgM that CoAlgM is positive in its first argument. To show that a type scheme is positive (Mono, Section 2.2), we use intrMono and may then assume two type families C1 and C2 and a cast c between them. Aside from monoCoAlgM, in code listings we omit monotonicity proofs as once the general principle is understood these can be quite tedious to work through.
For monoCoAlgM, the goal is to show there is a cast from
CoAlgM C1 X to CoAlgM C2 X (for any X of kind I ).
This is done with intrCast (also in Section 2.2), whose
first argument is simply a function of this type and whose second is a proof that
this function is extensionally equal to .
For the functional argument, it is simply a matter of deriving a Cast I
C1 R (needed for the assumed coa of type CoAlgM C1 R) from the given
c’ of type Cast C2 R and c of type Cast C1 C2, which we have by
castTrans, the composition operator for casts.
This new cast is given to coa as an erased argument, so the function
simply erases to coa. coa
, and therefore the proof that this is
extensionally equal to is trivial ( coa.
),
since it is intensionally equal to it.
With these proofs that NuF is a positive type scheme, we can define the rolling and unrolling operations nuRoll and nuUnroll for the recursive type family Nu.
unfoldM : X: I . CoAlgM Nu X i: I. X i Nu i = X. coa. i. x. nuRoll -i ( Y. f. f x coa) . inM : i: I. F Nu i Nu i = unfoldM (F Nu) ( R. c. v. ch. i. x. elimMono -cm -c -i x) . outM : i: I. Nu i F Nu i = i. co. nuUnroll -i co (F Nu) ( X. x. coa. coa -(castRefl I Nu) inM (unfoldM coa) -i x) .
unfoldM and inM.
We now discuss the definitions given in Figure 6.
Function unfoldM is the generator for our Mendler-style codata.
For any type family X of kind I , given some coa
of
type CoAlgM Nu X
and some x
of type X i
(for arbitrary
i
of type I
), we generate a term of
type Nu i by first invoking nuRoll, which obligates us to
produce some argument of type NuF Nu i.
Unfolding the definition of NuF, we provide for this an encoded
existential: a function
polymorphic over a type family Y of kind I taking
another function f of type X: I . X i CoAlgM Nu X Y i
and applying f to the given x and coa.
Our generic constructor inM is defined in terms of unfoldM.
The coalgebra given to unfoldM ignores its argument ch and instead casts (using
cm, the module parameter that proves monotonicity of F, and
c, the coalgebra’s assumed Cast I Nu R) the
assumed x
of type F (Nu F)
to the type F R.
Avoidance of ch, the coalgebra’s handle for coiterative calls, ensures
that inM is efficient – future observations of the codata
constructed with inM will not needlessly rebuild the sub-components of
the codata with which it was constructed.
This discussion is made more concrete in Section 3.1.
outM.
Finally, outM is our generic destructor for codata.
Given some co
of type Nu i
, we use the unrolling
operation for recursive type Nu on co to produce a term of
type NuF Nu i. Unfolding the definition of NuF, we provide
the resulting expression a function which assumes a type family X of
kind I , a term x
of type X i
, and a coalgebra
coa
of type CoAlgM (Nu F) X.
In the body of this given function, we eliminate coa by instantiating its
type argument to Nu F and giving it a cast
from Nu F to Nu F (using castRefl), the constructor
inM, a handle for making coiterative calls unfoldM coa, and
the assumed x.
We can show in Cedille that the computation rules expected of our derived
codatatype Nu and generator unfoldM (Section
2.3) hold by -equivalence (the cast argument, corresponding
to in the computation rule for the corecursion scheme, is
erased; inM corresponds to ):
reduce : X: I . coa: CoAlgM Nu X. i: I. x: X i. { outM (unfoldM coa x) coa inM (unfoldM coa) x } = X. coa. i. x. .
3.1 Lambek’s lemma
In following sections, we shall demonstrate that our generic coinductive datatype is adequate for both expressive functional programming and for giving proofs of many standard coinductive properties such as bisimulation. Our justification for the first claim rests both on the expressivity allowed to programmers in defining functions producing codata (using a course-of-values corecursion scheme) and the efficiency of the functions so defined. This efficiency is due to our constructor inM being an operation incurring run-time overhead that is constant in the number of observations (destructions) made on the codata. In Cedille, we can show directly that destructing an arbitrary constructed value produces the original collection of subdata from which the value was constructed in a constant number of -reductions.
lambek1 : i: I. xs: F Nu i. {outM (inM xs) xs} = i. xs. .
The name of this proof comes from Lambek’s lemma [18], which proves that (the actions of) final coalgebras are isomorphisms. Specifically, for any functor , Lambek’s lemma for final (ordinary) -coalgebra states that there exists an inverse such that and . Lambek’s lemma is a consequence of the uniqueness of the anamorphism, which is itself a category-theoretic expression of coinduction: the usual definition of is given by the generation of codata using the anamorphism of to coiteratively rebuild its observations. Our derivation of Mendler-style coinductive data restricts in such a way that functorial lifting is only defined for casts ( is assumed to be Mono, not a functor), and our alternative definition for (inM) avoids this needless rebuilding, and so one direction of Lambek’s lemma holds trivially.
Proving the Lambek’s lemma in the form would require functional extensionality, but this in itself does not necessitate that the equality implied to hold for corresponding elements in images of and must be extensional. Indeed, for derivations of inductive datatypes in Cedille both directions of Lambek’s lemma need only intensional equality. We now show that proof of the other direction of Lambek’s lemma for our derived coinductive data appears to truly require an extensional equality type by giving a counter-example in Figure 7 with respect to Cedille’s currently intensional equality type.
import utils. import nu. module lambek. TF : (Unit ) (Unit ) = X: Unit . X. monoTF : Mono Unit TF = <..> T : Unit = Nu Unit TF monoTF. TCoAlgM : = X: . CoAlgM Unit TF monoTF T ( _: Unit. X). tcoa : TCoAlgM Unit = R. c. v. ch. i. x. ch -i x. t : T unit = unfoldM -monoTF ( _: Unit. Unit) tcoa -unit unit . noLambek2 : {inM (outM t) t} X: . X = eq. X. X - eq.
TF, tcoa, and t.
Type scheme TF is the signature for T, and simply maps any type family X of kind Unit to itself (recall that Unit is the unitary type, Figure 3); our generic development requires that every signature be indexed, so we provide Unit as a “dummy”). TF is obviously monotonic, so the definition of the proof of this fact monoTF is omitted (indicated by <..>). This given, we define coinductive datatype T as the greatest fixpoint of TF, and for convenience define the type family TCoAlgM of TF-coalgebras. Term t of type T unit is generated via unfoldM using unit as the “seed” and a TF-coalgebra tcoa which simply makes a corecursive call with ch of type i: Unit. Unit R i to produce a result of type TF R i (convertible with the return type R i of the expression ch -i x).
noLambek2.
To see why the final proof noLambek2 holds, it is useful to rewrite the two sides of the assumed equation eq to -equivalent expressions:
{ g. g ( f. f unit tcoa) ( v. ch. x. x) f. f unit tcoa }
The right-hand side of the equation (corresponding to t) is a lambda
encoding of a pair consisting of a seed value unit and generator
tcoa.
However, the pair on the left-hand side has a seed value which itself is the
very same pair on the right-hand side, and a generator which simply returns the
seed value as-is.
Though we understand that these two expressions are extensionally equal (that
is, treated as black-boxed terms of type T unit
they produce the same observations), in Cedille
the current built-in equality type is intensional.
Indeed, (Section 2.1) makes it anti-extensional,
though we know of no fundamental reason why CDLE could not be extended with a more
general extensional equality type.
Additionally (and as suggested by a reviewer for the draft version of this
paper), we conjecture that a reformulation of Lambek’s lemma in terms of the
equivalence relation for existential types given by Reynold’s relational
parametricity (such as undertaken by Pitts in [29]) should be provable.
In CDLE, dependent intersections can be used to equip lambda encodings with
a proof principle for parametricity in precisely the same way used to equip
encodings of inductive types with an induction principle [32]; we
leave this as future work.
As we will see in Section 5, the foregoing negative result does not impact the ability to give proofs for many standard coinductive properties for this encoding of codata, such as showing stream bisimilarity or other relational properties; for many use cases, indexed coiteration suffices. What this result indicates is currently impossible for our encoding is proving “true coinduction” in the sense of bisimilarity implying equality (since if this we had this, we would be able to prove Lambek’s lemma).
4 Functional programming with streams
In this section, we give a few examples of programming with lambda-encoded codata (specifically, streams), emphasizing the expressivity the course-of-values corecursion scheme available to programmers. After defining the stream codatatype by instantiating the parameters of our generic development (Figure 8), we show how to define: the mapping of a function over the elements of a stream, an example of coiteration; the mapping of a function over just the head element of a stream, an example of corecursion; and the pairwise exchange of elements of a stream, an example of course-of-values coiteration. These example functions, given in Figure 9, also appear in Vene’s thesis [38], though there they are given using the traditional account of coinductive types as final -algebras (not final Mendler-style ones).
import utils. module examples/streamf (A: ). StreamF : (Unit ) Unit = R: Unit . u: Unit. Pair A (R u). monoStreamF : Mono Unit StreamF = <..> import nu/nu Unit StreamF -monoStreamF. Stream : = Nu unit. StreamCoAlg : = X: . CoAlgM ( _: Unit. Stream) ( _: Unit. X). head : Stream A = xs. fst (outM -unit xs) . tail : Stream Stream = xs. snd (outM -unit xs) . unfoldStream : X: . StreamCoAlg X X Stream = <..>
Definition of streams.
In Figure 8, StreamF is the signature for streams of elements of type A (where A is a module parameter; it is implicitly quantified over in all definitions in the figure), defined in terms of Pair (Figure 3); the definition of StreamF is more recognizable in the form . Term monoStreamF is a witness to the fact that StreamF is positive (definition omitted, indicated by <..>).
The import of module nu/nu instantiates that module’s parameters with index type Unit, signature functor StreamF, and positivity proof monoStreamF. Type Stream is the greatest fixpoint of StreamF with a fixed index of unit. Destructors head and tail are defined in terms of the generic destructor out and pair projections fst and snd. The generator unfoldStream has a first argument of type StreamCoAlg X, which specializes CoAlgM such that the type families for the codatatype and carrier ignore their Unit indices. The body of the definition of unfoldStream is omitted, as it is somewhat cluttered by type annotations to replace the assumed i: Unit with the constant unit in the type of the coalgebra given to unfoldM. We define this function so that the following examples may use it, and not be themselves so cluttered.
map : A: . B: . (A B) Stream A Stream B = A. B. f. unfoldStream B (Stream A) ( R. c. v. map. i. xs. intrPair (f (head xs)) (map -i (tail xs))) . mapHd : A: . (A A) Stream A Stream A = A. f. unfoldStream A (Stream A) ( R. c. v. mapHd. i. xs. intrPair (f (head xs)) (elimCast -c -i (tail xs))) . exch : A: . Stream A Stream A = A. unfoldStream A (Stream A) ( R. c. v. exch. i. xs. [hd1 : A = head (tail xs)] - [hd2 : A = head xs] - [tl2 : R i = exch -i (tail (tail xs))] - intrPair hd1 (v -i (intrPair hd2 tl))) .
map.
Our first function map is an example of coiteration over
streams.
First, note that this and following functions occur in a different module than
the definition of Stream, so now the element type must be given
explicitly both for Stream and unfoldStream.
With a function f of type A B, in the body of map
we generate using unfoldStream a stream with elements of type
B from: a coalgebra wherein map (of type
i: Unit. Stream A R i
) can be used for corecursive calls; and a seed
value xs
of type Stream A
.
In the body of the given coalgebra, we construct a StreamF A by
supplying for the head f (head xs) and for the tail map -i
(tail xs) (of type R i
).
In a high-level surface language supporting copatterns [2], the definition of map might look like:
head (map f xs) = f (head xs) tail (map f xs) = map f (tail xs)
mapHd.
Function mapHd is an example of corecursion.
Recall that the corecursion scheme supports an alternative for generating
codata: it may be given directly in a coalgebra, rather than being
generated from coiteration.
This mirrors the situation for iteration (where predecessors must be
iteratively rebuilt) and recursion (where they are available
“for free”) for inductive types.
In the definition, with function f of type A A, we produce
the desired stream by providing for its head f (head xs) and for its
tail tail xs, after coercing its type to the desired R i using
the given cast c from Stream A to R i
.
Using a high-level surface language, the definition of mapHd might look like:
head (mapHd f xs) = f (head xs) tail (mapHd f xs) = tail xs
For comparison, the standard definition of corecursion from coiteration requires “cotupling” (using a sum type as the generating value of the codata). In the same high-level pseudo-code, mapHd defined in this way would be more tedious and error-prone, and result in run-time overhead linear in the number of observations made on the codata:
mapHd’ f xs = go (in1 xs) where go : Sum (Stream A) (Stream A) Stream A head (go (in1 ys)) = f (head ys) head (go (in2 ys)) = head ys tail (go (in1 ys)) = go (in2 (tail ys)) tail (go (in2 ys)) = go (in2 (tail ys))
exch.
Our final example exch, a function which swaps the positions of
every two elements of a stream, demonstrates the course-of-values coiteration scheme,
wherein each step of
generating codata may describe an arbitrary number of the observations
made of that codata
(pairwise exchange only requires the construction of one additional
observation at each step).
In the coalgebra given in the definition of exch, we make three local
definitions (using Cedille’s syntax , to be read
; we use an additional space to
distinguish hyphens used for local definitions
from hyphens used in erased applications).
These local definitions are: hd1, the second
element of the stream xs; hd2, the first element; and
tl2, the exchange of elements of the second tail of xs.
These definitions are then incorporated into the produced stream, where in the
tail we use the abstract constructor v
of type
i: Unit. StreamF A R i R i
.
In a higher-level language, exch can be written using nested copatterns (because each step of generation produces a static number of future observations; course-of-values coiteration permits this number to be dynamically computed at each step):
head (exch xs) = head (tail xs) head (tail (exch xs)) = head xs tail (tail (exch xs)) = exch (tail (tail xs))
5 Coinductive proofs of properties of streams
In this section, we argue that the indexed coiteration scheme enjoyed by
our derived codatatypes is sufficient for giving proofs of many standard
coinductive properties.
We use as an example generalized relations on streams: the lifting of
some relation Rel: A A
(over an element type A:
) to a
relation StreamRel over streams in which corresponding elements are
related; instantiation of Rel
to an equivalence relation produces the
relation of stream bisimilarity (up to equivalence of elements).
The definition of StreamRel is given in Figure
10 (note again the use of module parameters, in particular the
import of streamf allowing us to refer by Stream to streams
of elements of type A).
For proofs, we show in Figure 11 that if Rel is reflexive,
symmetric, or transitive, then so too is the relation StreamRel.
import utils. module examples/streamrelf (A: ) (Rel: A A ). import streamf A. StreamRelF : (Pair Stream Stream ) Pair Stream Stream = R: Pair Stream Stream . p: Pair Stream Stream. [xs = fst p] - [ys = snd p] - Pair (Rel (head xs) (head ys)) (R (intrPair (tail xs) (tail ys))) . monoStreamRelF : Mono (Pair Stream Stream) StreamRelF = <..> import nu/nu (Pair Stream Stream) StreamRelF -monoStreamRelF. StreamRel : Stream Stream = xs: Stream. ys: Stream. Nu (intrPair xs ys). headRel : xs: Stream. ys: Stream. StreamRel xs ys Rel (head xs) (head ys) = <..> tailRel : xs: Stream. ys: Stream. StreamRel xs ys StreamRel (tail xs) (tail ys) = <..> unfoldStreamRel : X: Stream Stream . ( R: Stream Stream . ( xs: Stream. ys: Stream. X xs ys R xs ys) xs: Stream. ys: Stream. X xs ys StreamRelF ( p: Pair Stream Stream. R (fst p) (snd p)) (intrPair xs ys)) xs: Stream. ys: Stream. X xs ys StreamRel xs ys = <..>
StreamRelF and StreamRel.
The signature for generalized stream relations, StreamRelF, takes a
type family R: Pair Stream Stream
(standing in for recursive
occurrences of the greatest fixpoint of StreamRelF itself) and a pair
p: Pair Stream Stream
of the two streams to be related, and in the
body is defined as the type of pairs of proofs that the first elements of the two streams
are related by Rel and proofs that the tails of the streams are related by R.
This type scheme is positive in R, as proven by monoStreamRelF
(definition omitted).
We instantiate the generic codata derivation with these definitions and define
StreamRel as the greatest fixpoint of StreamRelF.
Destructors headRel and tailRel are given (their definitions
are similar to head and tail for streams and so are omitted), as
well as the simplified generator unfoldStreamRel: the properties we
shall prove using StreamRel require only indexed coiteration, so
unfoldStreamRel forgoes the facilities for more advanced generation
schemes (namely, indexed course-of-values coiteration) in order to simplify their proofs.
Reflexive : A: . (A A ) = A: . Rel: A A . x: A. Rel x x. Symmetric : A: . (A A ) = A: . Rel: A A . x: A. y: A. Rel x y Rel y x . Transitive : A: . (A A ) = A: . Rel: A A . x: A. y: A. z: A. Rel x y Rel y z Rel x z . strRefl : Reflexive A Rel Reflexive Stream StreamRel = refl. xs. unfoldStreamRel ( xs: Stream. ys: Stream. {xs ys}) ( R. ch. xs. ys. g. intrPair ( g - (refl -(head ys))) (ch -(tail xs) -(tail ys) ( g - ))) -xs -xs . strSym : Symmetric A Rel Symmetric Stream StreamRel = sym. xs. ys. rel. unfoldStreamRel ( ys: Stream. xs: Stream. StreamRel xs ys) ( R. ch. ys. xs. g. intrPair (sym -(head xs) -(head ys) (headRel -xs -ys g)) (ch -(tail ys) -(tail xs) (tailRel -xs -ys g))) -ys -xs rel . strTra : Transitive A Rel Transitive Stream StreamRel = tra. xs. ys. zs. rel1. rel2. [X : Stream Stream = xs: Stream. zs: Stream. Y: . ( ys: Stream. StreamRel xs ys StreamRel ys zs Y) Y] - unfoldStreamRel X ( R. ch. xs. zs. g. g ( ys. rel1. rel2. [hd : Rel (head xs) (head zs) = tra -(head xs) -(head ys) -(head zs) (headRel -xs -ys rel1) (headRel -ys -zs rel2)] - [tl : R (tail xs) (tail zs) = ch -(tail xs) -(tail zs) ( Y. f. f -(tail ys) (tailRel -xs -ys rel1) (tailRel -ys -zs rel2))] - intrPair hd tl)) -xs -zs ( Y. f. f -ys rel1 rel2) .
strRefl.
To prove reflexivity of StreamRel assuming reflexivity of Rel,
we use for the generator the type family of proofs that two streams are equal.
In the body of the coalgebra given to
unfoldStreamRel we have the following obligations: for the head we must
prove Rel (head xs) (head ys), given by rewriting (with , Figure
1) by the assumed g: {xs ys}
and
using the assumption that Rel is reflexive; for the tail we must prove
R (tail xs) (tail ys), given by providing the coinductive hypothesis
ch with a proof that {tail xs tail ys}.
strSym.
To prove symmetry of StreamRel assuming symmetry of Rel, we
use for the generator the type family, over streams ys and xs,
of proofs that xs is related to ys.
In the body of the coalgebra given to unfoldStreamRel we have the
following obligations: for the head we
must prove Rel (head ys) (head xs), given by invoking the proof
sym that Rel is symmetric on a proof of Rel (head
xs) (head ys) which we extract from the head of the assumption
g: StreamRel xs ys
; for the tail we must prove R (tail ys) (tail xs), given by
providing the coinductive hypothesis ch with a proof of
StreamRel (tail xs) (tail ys), extracted from the tail of g.
strTra.
To prove transitivity of StreamRel assuming transitivity of Rel, we use for the generator the type family (locally named X) over streams xs and zs of proofs that there exists a ys: Stream such that StreamRel xs ys and StreamRel ys zs. In the body of the coalgebra given to unfoldStreamRel, we first unpack the existential g to access these assumptions directly. For the head (locally named hd) we must prove Rel (head xs) (head zs), given by invoking the proof tra that Rel is transitive on proofs that Rel (head xs) (head ys) and that Rel (head ys) (head zs). For the tail (locally named tl) we must prove R (tail xs) (tail zs), given by invoking the coinductive hypothesis ch on a proof that their exists some stream ys’ such that StreamRel (tail xs) ys’ and StreamRel ys’ (tail zs); tail ys is such a stream, with the required proofs extracted from the tails of the proofs that StreamRel xs ys and StreamRel ys zs.
6 Related & Future Work
Previous work on lambda encodings of datatypes in Cedille have focused on inductive datatypes. In [11], Firsov and Stump generically derive the induction principle for Church- and Mendler-style lambda encodings; this work was extended by Firsov et al. [9] to equip datatypes encoded in the Mendler-style with constant-time destructors, and by Firsov et al. [10] to support course-of-values induction for them. Our negative result (Section 3.1) appears to be a real difficulty in adapting the techniques described in [9, 11] for efficient constructors of Mendler-style coinductive types, as the type coercion enabling an efficient recursion scheme used by their (dependent version of a) Mendler-style algebra comes as a consequence of induction, which itself implies Lambek’s lemma.
To address this difficulty, we use monotone recursive types in which the introduction and elimination forms require certain monotonicity witnesses. Monotone recursive types of this sort were extensively studied by Matthes [20, 21] as a way of guaranteeing strong normalization for extensions of System F that support efficient recursion schemes for inductive datatypes. These appear to be true extensions: in [30], Spławski and Urzyczyn give evidence suggesting there can be no efficiency-preserving translation of the recursion scheme for datatypes in System F. In contrast, monotone recursive types with the desired computational behavior are derivable within CDLE (shown by Jenkins and Stump in [16]), meaning no true extensions of CDLE are required (and thus no new meta-theory is needed). We can therefore define a variant Mendler-style coalgebra equipped with a zero-cost type coercion whose codomain is the type codatatype being recursively defined, enabling an efficient corecursion scheme and a constant-time codata constructor (Figure 6).
The use of recursive types for constant-time destructors for lambda encodings of inductive types goes back (at least) to Parigot [27] (see also [19, 23]). In [14], Geuvers analyzed the Church, Scott, and Parigot (alternatively “Church-Scott”) method of lambda encodings for both inductive and coinductive data using a category- and type-theoretic account developed in [12], and he similarly uses recursive types to guarantee efficient codata constructors for the Scott and Parigot encoding. In comparison to the present work, Parigot-encoded codata supports the corecursion scheme by requiring recursive occurrences of the codatatype in the type of its constructors to be embedded in a coproduct type, which requires an additional case-analysis to access. This translates to run-time overhead linear in the number of observations made on codata generated from coiteration alone. Additionally, [14] does not treat indexed and course-of-values coiteration.
The present work leans upon the account of Mendler-style (co)inductive datatypes in type theory by Mendler [25] and in category theory by Uustalu and Vene [36, 37]. The examples of functional programming with codata via different generation schemes (Section 4) come directly from Vene’s thesis [38] (in which solutions were given using ordinary final -coalgebras). In [37], Uustalu and Vene advocated for the Mendler-style approach as being a more semantic approach to termination checking of total functional programs. We agree whole-heartedly, and consider as interesting future work the design of a high-level language with copatterns using the Mendler-style approach as the basis for productivity checking, similar to the use of sized types by Abel et al. [3]. Indeed, Mendler-style recursion schemes were reported by Barthe et al. [5] as the inspiration for type-based termination checking with sized types.
7 Conclusion
In this paper, we have derived coinductive types generically using a Mendler-style of lambda encoding in CDLE, an impredicative Curry-style pure type system. Codata so derived enjoy direct support for schemes of generation that are both expressive and efficient: the corecursion scheme is facilitated by a constant-time type coercion between the concrete and abstract type of the codata being generated, and the course-of-values coiteration scheme is facilitated by a codata constructor with run-time overhead that is constant in the number of observations made. Indexed coiteration is also supported, which we demonstrate with examples of proofs of some standard coinductive properties of streams. We also showed of our encoding of coinductive datatypes that while the equation given by one direction of Lambek’s lemma (which states that final -coalgebras are isomorphisms) holds by definitional equality, showing the opposite direction (and thus, coinduction in the sense of “bisimilarity implying equality”) appears to require extending CDLE with an extensional equality type.
References
- [1]
- [2] Andreas Abel, Brigitte Pientka, David Thibodeau & Anton Setzer (2013): Copatterns: Programming Infinite Structures by Observations. SIGPLAN Not. 48(1), p. 27–38, 10.1145/2480359.2429075.
- [3] Andreas M. Abel & Brigitte Pientka (2013): Wellfounded Recursion with Copatterns: A Unified Approach to Termination and Productivity. In: Proceedings of the 18th ACM SIGPLAN International Conference on Functional Programming, ICFP ’13, Association for Computing Machinery, New York, NY, USA, p. 185–196, 10.1145/2500365.2500591.
- [4] Stuart F. Allen, Mark Bickford, Robert L. Constable, Richard Eaton, Christoph Kreitz, Lori Lorigo & E. Moran (2006): Innovations in computational type theory using Nuprl. J. Applied Logic 4(4), pp. 428–469, 10.1016/j.jal.2005.10.005.
- [5] Gilles Barthe, Maria João Frade, Eduardo Giménez, Luís Pinto & Tarmo Uustalu (2004): Type-based termination of recursive definitions. Mathematical Structures in Computer Science 14(1), pp. 97–141, 10.1017/S0960129503004122.
- [6] Joachim Breitner, Richard A. Eisenberg, Simon Peyton Jones & Stephanie Weirich (2016): Safe zero-cost coercions for Haskell. J. Funct. Program. 26, p. e15, 10.1017/S0956796816000150.
- [7] C. Böhm and M. Dezani-Ciancaglini and P. Peretti and S.Ronchi Della Rocca (1979): A discrimination algorithm inside --calculus. Theoretical Computer Science 8(3), pp. 271 – 291, 10.1016/0304-3975(79)90014-8.
- [8] Thierry Coquand & Gérard Huet (1988): The calculus of constructions. Information and Computation 76(2/3), pp. 95 – 120, 10.1016/0890-5401(88)90005-3.
- [9] Denis Firsov, Richard Blair & Aaron Stump (2018): Efficient Mendler-Style Lambda-Encodings in Cedille. In Jeremy Avigad & Assia Mahboubi, editors: Interactive Theorem Proving - 9th International Conference, ITP 2018, Held as Part of the Federated Logic Conference, FloC 2018, Oxford, UK, July 9-12, 2018, Proceedings, Lecture Notes in Computer Science 10895, Springer International Publishing, Cham, pp. 235–252, 10.1007/978-3-319-94821-8_14.
- [10] Denis Firsov, Larry Diehl, Christopher Jenkins & Aaron Stump (2018): Course-of-Value Induction in Cedille. Available at https://arxiv.org/abs/1811.11961. (manuscript).
- [11] Denis Firsov & Aaron Stump (2018): Generic Derivation of Induction for Impredicative Encodings in Cedille. In: Proceedings of the 7th ACM SIGPLAN International Conference on Certified Programs and Proofs, CPP 2018, Association for Computing Machinery, New York, NY, USA, p. 215–227, 10.1145/3167087. Available at https://doi-org.proxy.lib.uiowa.edu/10.1145/3167087.
- [12] Herman Geuvers (1992): Inductive and Coinductive types with Iteration and Recursion. In B. Nordström, K. Pettersson & G. Plotkin, editors: Informal Proceedings of the Workshop on Types for Proofs and Programs, TYPES ’92, Dept of Computing Science, Chalmers Univ. of Tehnology and Göteborg Univ., pp. 193–217. Available at http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.35.9758.
- [13] Herman Geuvers (2001): Induction Is Not Derivable in Second Order Dependent Type Theory. In Samson Abramsky, editor: International Conference on Typed Lambda Calculi and Applications, Springer, Berlin, Heidelberg, pp. 166–181, 10.1007/3-540-45413-616.
- [14] Herman Geuvers (2014): The Church-Scott representation of inductive and coinductive data. Available at http://citeseerx.ist.psu.edu/viewdoc/citations?doi=10.1.1.705.2662. (manuscript).
- [15] Tatsuya Hagino (1987): A categorical programming language. Ph.D. thesis, The University of Edinburgh.
- [16] Christopher Jenkins & Aaron Stump (2020): Monotone recursive types and recursive data representations in Cedille. Available at https://arxiv.org/abs/2001.02828. (manuscript).
- [17] Alexei Kopylov (2003): Dependent Intersection: A New Way of Defining Records in Type Theory. In: Proceedings of 18th IEEE Symposium on Logic in Computer Science (LICS 2003), 22-25 June 2003, Ottawa, Canada, LICS ’03, IEEE Computer Society, pp. 86–95, 10.1109/LICS.2003.1210048.
- [18] Joachim Lambek (1968): A Fixpoint Theorem for Complete Categories. Mathematische Zeitschrift 103(2), pp. 151–161, 10.1007/bf01110627.
- [19] Daniel Leivant (1989): Contracting proofs to programs. In P. Odifreddi, editor: Logic and Computer Science, pp. 279–327, 10.1184/R1/6604463.v1.
- [20] Ralph Matthes (1998): Extensions of System F by Iteration and Primitive Recursion on Monotone Inductive Types. Ph.D. thesis, Ludwig-Maximilians-Universität München.
- [21] Ralph Matthes (1998): Monotone Fixed-Point Types and Strong Normalization. In Georg Gottlob, Etienne Grandjean & Katrin Seyr, editors: Computer Science Logic, 12th International Workshop, CSL ’98, Annual Conference of the EACSL, Brno, Czech Republic, August 24-28, 1998, Proceedings, Lecture Notes in Computer Science 1584, Springer, pp. 298–312, 10.1007/10703163_20.
- [22] Ralph Matthes (1999): Monotone (co)inductive types and positive fixed-point types. RAIRO - Theoretical Informatics and Applications 33(4-5), p. 309–328, 10.1051/ita:1999120.
- [23] N. P. Mendler (1987): Recursive Types and Type Constraints in Second-Order Lambda Calculus. In: Proceedings of the Symposium on Logic in Computer Science, (LICS ’87), IEEE Computer Society, Los Alamitos, CA, pp. 30–36.
- [24] N. P. Mendler (1991): Predictive type universes and primitive recursion. In: Proceedings Sixth Annual IEEE Symposium on Logic in Computer Science, LICS ’91, pp. 173–184, 10.1109/LICS.1991.151642.
- [25] Nax Paul Mendler (1991): Inductive types and type constraints in the second-order lambda calculus. Annals of Pure and Applied Logic 51(1), pp. 159 – 172, 10.1016/0168-0072(91)90069-X.
- [26] Alexandre Miquel (2001): The Implicit Calculus of Constructions: Extending Pure Type Systems with an Intersection Type Binder and Subtyping. In: Proceedings of the 5th International Conference on Typed Lambda Calculi and Applications, TLCA’01, Springer-Verlag, Berlin, Heidelberg, p. 344–359, 10.1007/3-540-45413-6_27. Available at http://dl.acm.org/citation.cfm?id=1754621.1754650.
- [27] Michel Parigot (1988): Programming with Proofs: A Second Order Type Theory. In Harald Ganzinger, editor: ESOP ’88, 2nd European Symposium on Programming, Nancy, France, March 21-24, 1988, Proceedings, Lecture Notes in Computer Science 300, Springer, Berlin, Heidelberg, pp. 145–159, 10.1007/3-540-19027-9_10.
- [28] Michel Parigot (1989): On the Representation of Data in Lambda-Calculus. In Egon Börger, Hans Kleine Büning & Michael M. Richter, editors: CSL ’89, 3rd Workshop on Computer Science Logic, Kaiserslautern, Germany, October 2-6, 1989, Proceedings, Lecture Notes in Computer Science 440, Springer, Berlin, Heidelberg, pp. 309–321, 10.1007/3-540-52753-2_47.
- [29] Andrew M. Pitts (1998): Existential Types: Logical Relations and Operational Equivalence. In Kim Guldstrand Larsen, Sven Skyum & Glynn Winskel, editors: Automata, Languages and Programming, 25th International Colloquium, ICALP’98, Aalborg, Denmark, July 13-17, 1998, Proceedings, Lecture Notes in Computer Science 1443, Springer, pp. 309–326, 10.1007/BFb0055063.
- [30] Zdzislaw Splawski & Pawel Urzyczyn (1999): Type Fixpoints: Iteration vs. Recursion. In Didier Rémy & Peter Lee, editors: Proceedings of the fourth ACM SIGPLAN International Conference on Functional Programming (ICFP ’99), Paris, France, September 27-29, 1999, ACM, pp. 102–113, 10.1145/317636.317789.
- [31] Aaron Stump (2017): The calculus of dependent lambda eliminations. J. Funct. Program. 27, p. e14, 10.1017/S0956796817000053.
- [32] Aaron Stump (2018): From realizability to induction via dependent intersection. Ann. Pure Appl. Logic 169(7), pp. 637–655, 10.1016/j.apal.2018.03.002.
- [33] Aaron Stump (2018): Syntax and Semantics of Cedille. Available at http://arxiv.org/abs/1806.04709.
- [34] Aaron Stump (2018): Syntax and Typing for Cedille Core. Available at http://arxiv.org/abs/1811.01318.
- [35] Alfred Tarski (1955): A lattice-theoretical fixpoint theorem and its applications. Pacific Journal of Mathematics 5(2), pp. 285–309, 10.2140/pjm.1955.5.285.
- [36] Tarmo Uustalu & Varmo Vene (1999): Mendler-style Inductive Types, Categorically. Nordic J. of Computing 6(3), pp. 343–361. Available at http://dl.acm.org/citation.cfm?id=774455.774462.
- [37] Tarmo Uustalu & Varmo Vene (2000): Coding Recursion a la Mendler (Extended Abstract). In: Proc. of the 2nd Workshop on Generic Programming, WGP 2000, Technical Report UU-CS-2000-19, Dept. of Computer Science, Utrecht University, pp. 69–85.
- [38] Varmo Vene (2000): Categorical programming with inductive and coinductive types. Ph.D. thesis, University of Tartu.
- [39] Philip Wadler (1990): Recursive Types for Free! Available at https://homepages.inf.ed.ac.uk/wadler/papers/free-rectypes/free-rectypes.txt. Unpublished.
- [40] Benjamin Werner (1994): Une théorie des constructions inductives. Ph.D. thesis, Université Paris-Diderot.
Appendix A CDLE: additional term constructors
The code listings in Appendix B detailing the derivation of type coercions and monotone recursive rely on additional term constructs for dependent intersections and an additional eliminator for the equality type.
Dependent intersection
is the type of terms which can be assigned both type and . In the annotated language, the introduction form is , where has type and has type . This is conceptually similar to the introduction rule of a dependent pair, except that is additionally required to be definitionally equal to . This allows the erasure of to be simply .
Dependent intersections are eliminated with projections and : if has type , then has type and erases to and has type and also erases to . This can be seen choosing to “view” as either having type or .
Type coercions by equalities
The expression (erasing to ) has type if has type and proves that is equal to . This is similar to the direct computation rule of NuPRL (see Section 2.2 of Allen et al. [4]).
Appendix B Derived type constructors
module utils/cast (I: ). Cast : (I ) (I ) = A: I . B: I . f: i: I. A i B i. {f x. x}. intrCast : A: I . B: I . f: i: I. A i B i. ( i: I. a: A i. {f -i a a}) Cast A B = A. B. f. eq. [ i. a. (eq -i a) - (f -i a) {a} , ]. elimCast : A: I . B: I . Cast A B i: I. A i B i = A. B. c. c.2 - c.1 { x. x}. -- underscore for anonymous definitions _ : { intrCast x. x } = . _ : { elimCast x. x } = . castRefl : A: I . Cast A A = A. intrCast -( _. x. x) -( _. _. ). castTrans : A: I . B: I . C: I . Cast A B Cast B C Cast A C = A. B. C. c1. c2. intrCast -( i. a. elimCast -c2 -i (elimCast -c1 -i a)) -( _. _. ). _ : { castRefl x. x } = . _ : { castTrans x. x } = . Mono : ((I ) I ) = F: (I ) I . A: I . B: I . Cast A B Cast (F A) (F B). intrMono : F: (I ) I . ( A: I . B: I . Cast A B Cast (F A) (F B)) Mono F = F. m. A. B. c. intrCast -(elimCast -(m -c)) -( i. a. ) . elimMono : F: (I ) I . A: I . B: I . Mono F Cast A B i: I. F A i F B i = F. A. B. cm. c. i. f. elimCast -(cm -c) -i f. _ : { intrMono x. x } = . _ : { elimMono x. x } = .
B.1 Cast
For an indexing type I:
(given as a module parameter) and type families
A: I
and B: I
, type Cast A B
is defined as the
dependent intersection type of functions f: i: I. A i B i
and proofs that f
is equal to the identity function.
intrCast
takes an argument f: i: I. A i B i
and
a proof that f
behaves extensionally like an identity function
( i: I. a: A i. {f a a}
).
In the body of its definition, we introduce a dependent intersection.
For the first argument, we assume a: A i
and use to cast a
to
the type B i
of the expression f -i a
with the equality
eq -i a
proving these two expressions are equal.
By the erasure of , the resulting expression erases to a. a
, so the
proof that this is equal to the identity function (needed for the second
component of the dependent intersection) is trivial, given by .
As also erases to x. x
, the two components of the intersection are
definitionally equal, as required.
elimCast
takes an erased cast argument of type
Cast A B
and produces a function of type i: I. A i B i.
Its definition simply uses to cast x. x
to the type of c.1
by
the proof c.2
that { c.1 x. x }
.
This means that elimCast
itself erases to x. x
.
castRefl and castTrans
are straight-forward: there is always a cast between two definitionally equal types, and the composition of identity functions produces an identity function.
B.2 Mono
For an indexed type scheme F: (I ) I
, we define Mono F
to be the property that any type coercion between indexed type families
A: I
and B: I
can be lifted to a coercion between the
type families F A
and F B
.
intrMono
is defined the way it is to make the axiomatic presentation given in Figure 2(b) intelligible. Its type signature is equal to the type:
F: (I ) I . Mono F Mono F
So, intrMono takes an erased proof cm that there is an F-lifting for any type coercion, and an erased type coercion c: Cast A B, and constructs a new cast from family F A to family F B. As all arguments are erased and the definition uses intrCast, intrMono is definitionally equal to x. x.
elimMono
takes an erased proof cm: Mono F
and an
erased c: Cast A B
and produces a function of type i: I. F A i
F B i by eliminating the cast cm -c of type
Cast (F A) (F B)
on the assumed f: F A i
.
As the definition uses elimCast, elimMono is definitionally
equal to f. f.
module utils/rec (I: ) (F: (I ) I ). import top. import cast I. Rec : I = i: I. X: I . Cast (F X) X X i. recFold : X: I . Cast (F X) X Cast Rec X = X. c. intrCast -( i. x. x -c) -( i. x. ). recIn : Mono F Cast (F Rec) Rec = im. [f : i: I. F Rec i Rec i = i. xs. X. c. elimCast -c -i (elimMono -im -(recFold -c) -i xs)] - intrCast -f -( i. xs. ) . recOut : Mono F Cast Rec (F Rec) = im. [f : i: I. Rec i F Rec i = i. x. x (F Rec) -(im -(recIn -im))] - intrCast -f -( i. x. ) . roll : Mono F i: I. F Rec i Rec i = im. elimCast -(recIn -im) . unroll : Mono F i: I. Rec i F Rec i = im. elimCast -(recOut -im). _ : {roll x. x} = . _ : {unroll x. x} = .
B.3 Rec
The definition of Rec, the recursive type former, is described in
[16] in detail and follows the proof of Tarski’s least fixed-point theorem for
monotone functions over a complete lattice [35].
Interpreting impredicative quantification as set intersection, read the
definition of Rec
as the intersection over all type families
X: I
of all types X i
such that there exists a type coercion
from families F X
to X
.
recFold
takes an erased cast c
from F X to
X and produces a cast from Rec
to X
by simply giving
c
to the assumed x: Rec as an erased argument.
recIn
assumes that F
is monotonic and produces a
cast from families F Rec
to Rec
.
The function f
implementing this cast assumes xs: F Rec i
and to
produce an expression of type Rec
assumes c: Cast (F X) X
, and
first coerces the type of xs
to F X i
(by using recFold
and the monotonicity witness to lift this to a type coercion from F Rec i
to F X i
), then uses c
to coerce this to type X i
.
x
recOut
assumes that F
is monotonic and produces a cast from families
Rec
to F Rec
.
The function f
implementing this cast assumes x: Rec i
and gives
this a type coercion from families F (F Rec)
to F Rec
by
combining recIn
with the assumption of monotonicity.
roll and unroll
are simply defined by eliminating the casts recIn
and recOut
,
resp., so it is immediate that these both erased to x. x
.