Efficient lambda encodings for Mendler-style coinductive types in Cedille

05/01/2020 ∙ by Christopher Jenkins, et al. ∙ 0

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.

READ FULL TEXT VIEW PDF
POST COMMENT

Comments

There are no comments yet.

Authors

page 1

page 2

page 3

page 4

This week in AI

Get the week's most popular data science and artificial intelligence research sent straight to your inbox every Saturday.

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].

Figure 1: A fragment of CDLE (equality and implicit products)

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 .

(a) Cast (zero-cost coercions)
(b) Mono (internalized monotonicity predicate)
(c) Rec (recursive type former)
Figure 2: Derived type constructors for monotone recursive types

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 1(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 1(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 1(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 ).

Figure 3: Derived datatypes: pairs and the unitary 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 1(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.
Figure 4: Generic lambda encoding for codata (part 1)

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 .
Figure 5: Generic lambda encoding for codata (part 2)

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) .
Figure 6: Generic lambda encoding for codata (part 3)

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.
Figure 7: Violation of Lambek’s lemma

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 = <..>
Figure 8: Definition of streams

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))) .
Figure 9: Programming with the coiteration, corecursion, and course-of-values coiteration schemes

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 = <..>
Figure 10: Definition of generalized relations between streams

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) .
Figure 11: Coinductive proofs of reflexivity, symmetry, and transitivity for StreamRel

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

Appendix A CDLE: additional term constructors

Figure 12: Dependent intersections and

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 } =  .
Figure 13: Type coercions and monotonicity witnesses (utils/cast.ced)

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 1(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} = .
Figure 14: Monotone recursive types (utils/rec.ced)

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.