These keywords were added by machine and not by the authors. This process is experimental and the keywords may be updated as the learning algorithm improves.

1 Introduction

Dependent type theory is useful both for programming, and for proving properties of elements of types. Modern implementations of dependent type theories such as Coq [17], Nuprl [11], Agda [21], and Idris [8], have been used successfully in many projects. However, they offer limited support for programming and proving with coinductive types.

One of the key challenges is to ensure that functions on coinductive types are well-defined; that is, productive with unique solutions. Syntactic guarded recursion [12], as used for example in Coq [13], ensures productivity by requiring that recursive calls be nested directly under a constructor, but it is well known that such syntactic checks exclude many valid definitions, particularly in the presence of higher-order functions.

To address this challenge, a type-based approach to guarded recursion, more flexible than syntactic checks, was first suggested by Nakano [20]. A new modality, written \(\triangleright \) and called ‘later’ [2], allows us to distinguish between data we have access to now, and data which we will get later. This modality must be used to guard self-reference in type definitions, so for example guarded streams of natural numbers are described by the guarded recursive equation

$$\begin{aligned} {\textsf {Str}^{g}_{\mathbb {N}}} \simeq \mathbb {N}\times \triangleright {\textsf {Str}^{g}_{\mathbb {N}}} \end{aligned}$$

asserting that stream heads are available now, but tails only later.

Types defined via guarded recursion with \(\triangleright \) are not standard coinductive types, as their denotation is defined via models based on the topos of trees [5]. More pragmatically, the bare addition of \(\triangleright \) disallows productive but acausal [16] functions such as the ‘every other’ function that returns every second element of a stream. Atkey and McBride proposed clock quantifiers [3] for such functions; these have been extended to dependent types [7, 19], and Møgelberg [19, Theorem2] has shown that they allow the definition of types whose denotation is precisely that of standard coinductive types interpreted in set-based semantics. As such, they allow us to program with real coinductive types, while retaining productivity guarantees.

In this paper we introduce the extensional guarded dependent type theory \(\mathsf {gDTT}\), which provides a framework where guarded recursion can be used not just for programming with coinductive types but also for coinductive reasoning.

As types depend on terms, one of the key challenges in designing \(\mathsf {gDTT}\) is coping with elements that are only available later, i.e., elements of types of the form \(\triangleright {A}\). We do this by generalising the applicative functor structure of \(\triangleright \) to the dependent setting. Recall the rules for applicative functors [18]:


The first rule allows us to make later use of data that we have now. The second allows, for example, functions to be applied recursively to the tails of streams.

Suppose now that f has type \(\triangleright (\varPi x:A.B)\), and t has type \(\triangleright A\). What should the type of be? Intuitively, t will eventually reduce to some value \({\textsf {next} \ {}}u\), and so the resulting type should be \(\triangleright (B[u/x])\), but if t is an open term we may not be able to perform this reduction. This problem occurs in coinductive reasoning: if, e.g., A is \({\textsf {Str}^{g}_{\mathbb {N}}}\), and B a property of streams, in our applications f will be a (guarded) coinduction assumption that we will want to apply to the tail of a stream, which has type \(\triangleright {\textsf {Str}^{g}_{\mathbb {N}}}\).

We hence must introduce a new notion, of delayed substitution, similar to let-binding, allowing us to give the type

binding x in B. Definitional equality rules then allow us to simplify this type when t has form \({\textsf {next} \ {}}{u}\), i.e., . This construction generalises to bind a list of variables. Delayed substitution is essential to many examples, as shown in Sect. 3, and surprisingly the applicative functor term-former , so central to the standard presentation of applicative functors, turns out to be definable via delayed substitutions, as shown in Sect. 2.

Contributions. The contributions of this paper are:

  • We introduce the extensional guarded dependent type theory \(\mathsf {gDTT}\), and show that it gives a framework for programming and proving with guarded recursive and coinductive types. The key novel feature is the generalisation of the ‘later’ type-former and ‘next’ term-former via delayed substitutions;

  • We prove the soundness of \(\mathsf {gDTT}\) via a model similar to that used in earlier work on guarded recursive types and clock quantifiers [7, 19].

We focus on the design and soundness of the type theory and restrict attention to an extensional type theory. We postpone a treatment of an intensional version of the theory to future work (see Sects. 7 and 8).

In addition to the examples included in this paper, we are pleased to note that a preliminary version of \(\mathsf {gDTT}\) has already proved crucial for formalizing a logical relations adequacy proof of a semantics for PCF using guarded recursive types by Paviotti et. al. [22].

Note that for space reasons many details appear only in the technical report version of this paper [6].

2 Guarded Dependent Type Theory

\(\mathsf {gDTT}\) is a type theory with base types unit \(\mathbf {1}\), booleans \(\mathbf {B}\), and natural numbers \(\mathbf {N}\), along with \(\varPi \)-types, \(\varSigma \)-types, identity types, and universes. For space reasons we omit all definitions that are standard to such a type theory; see e.g. Jacobs [15]. Our universes are à la Tarski, so we distinguish between types and terms, and have terms that represent types; they are called codes of types and they can be recognised by their circumflex, e.g., \(\widehat{\mathbf {N}}\) is the code of the type \(\mathbf {N}\). We have a map \(\textsf {El}\) sending codes of types to their corresponding type. We follow standard practice and often omit \(\textsf {El}\) in examples, except where it is important to avoid confusion.

Fig. 1.
figure 1figure 1

Judgements in \(\mathsf {gDTT}\).

We fix a countable set of clock variables \(\text {CV}= \{\kappa _1, \kappa _2, \cdots \}\) and a single clock constant \(\kappa _0\), which will be necessary to define, for example, the function \(\textsf {hd }{}\) in Sect. 5. A clock is either a clock variable or the clock constant; they are intuitively temporal dimensions on which types may depend. A clock context \(\varDelta , \varDelta ', \cdots \) is a finite set of clock variables. We use the judgement \(\vdash _{\varDelta } \kappa \) to express that either \(\kappa \) is a clock variable in the set \(\varDelta \) or \(\kappa \) is the clock constant \(\kappa _0\). All judgements, summarised in Fig. 1, are parametrised by clock contexts. Codes of types inhabit universes \(\mathcal {U}_{\varDelta }\) parametrised by clock contexts similarly. The universe \(\mathcal {U}_{\varDelta }\) is only well-formed in clock contexts \(\varDelta '\) where \(\varDelta \subseteq \varDelta '\). Intuitively, \(\mathcal {U}_{\varDelta }\) contains codes of types that can vary only along dimensions in \(\varDelta \). We have universe inclusions from \(\mathcal {U}_{\varDelta }\) to \(\mathcal {U}_{\varDelta '}\) whenever \(\varDelta \subseteq \varDelta '\); in the examples we will not write these explicitly. Note that we do not have \(\widehat{\mathcal {U}_{\varDelta }} : \mathcal {U}_{\varDelta '}\), i.e., these universes do not form a hierarchy. We could additionally have an orthogonal hierarchy of universes, i.e. for each clock context \(\varDelta \) a hierarchy of universes \(\mathcal {U}_{\varDelta }^1 : \mathcal {U}_{\varDelta }^2 : \cdots \).

All judgements are closed under clock weakening and clock substitution. The former means that if, e.g., \(\varGamma \vdash _{\varDelta } t : A\) is derivable then, for any clock variable \(\kappa \not \in \varDelta \), the judgement \(\varGamma \vdash _{\varDelta ,\kappa } t : A\) is also derivable. The latter means that if, e.g., \(\varGamma \vdash _{\varDelta ,\kappa } t : A\) is derivable and \(\vdash _{\varDelta } \kappa '\) then the judgement \(\varGamma [\kappa '/\kappa ] \vdash _{\varDelta } t[\kappa '/\kappa ] : A[\kappa '/\kappa ]\) is also derivable, where clock substitution \([\kappa '/\kappa ]\) is defined as obvious.

The rules for guarded recursion can be found in Figs. 2 and 3; rules for coinductive types are postponed until Sect. 4. Recall the ‘later’ type former \(\triangleright \), which expresses that something will be available at a later time. In \(\mathsf {gDTT}\) we have \(\overset{\kappa }{\triangleright }{}\) for each clock \(\kappa \), so we can delay a type along different dimensions. As discussed in the introduction, we generalise the applicative functor structure of each \(\overset{\kappa }{\triangleright }{}\) via delayed substitutions, which allow a substitution to be delayed until its substituent is available. We showed in the introduction how a type with a single delayed substitution should work. However if we have a term f with more than one argument, for example of type \(\overset{\kappa }{\triangleright }{\left( {\varPi {\left( x : A\right) }.{\varPi {\left( y : B\right) }.C}}\right) }\), and wish to type an application (where is the applicative functor operation for clock \(\kappa \)) we may have neither t nor u available now, and so we need sequences of delayed substitutions to define the type . Our concrete examples of Sect. 3 will show that this issue arises in practice. We therefore define sequences of delayed substitutions \(\xi \). The new raw types, terms, and delayed substitutions of \(\mathsf {gDTT}\) are given by the grammar

Note that we just write \(\overset{\kappa }{\triangleright }{A}\) where its delayed substitution is the empty \(\mathop {\cdot }\), and that \(\overset{\kappa }{\triangleright }\xi .{A}\) binds the variables substituted for by \(\xi \) in A, and similarly for \(\textsf {next}\).

The three rules \(\textsc {DS-Emp}\), \(\textsc {DS-Cons}\), and \(\textsc {Tf-}\triangleright \) are used to construct the type \(\overset{\kappa }{\triangleright }{\xi }.{A}\). These rules formulate how to generalise these types to arbitrarily long delayed substitutions. Once the type formation rule is established, the introduction rule \(\textsc {Ty-Next}\) is the natural one.

With delayed substitutions we can define as

Using the rules in Fig. 2 we can derive the following typing judgement for

When a term has the form , then we have enough information to perform the substitution in both the term and its type. The rule \(\textsc {TmEq-Force}\) applies the substitution by equating the term with the result of an actual substitution, . The rule \(\textsc {TyEq-Force}\) does the same for its type. Using \(\textsc {TmEq-Force}\) we can derive the basic term equality

typical of applicative functors [18].

It will often be the case that a delayed substitution is unnecessary, because the variable to be substituted for does not occur free in the type/term. This is what \(\textsc {TyEq-}\triangleright \textsc {-Weak}\) and \(\textsc {TmEq-Next-Weak}\) express, and with these we can justify the simpler typing rule

In other words, delayed substitutions on the type are not necessary when we apply a non-dependent function.

Further, we have the applicative functor identity law

This follows from the rule \(\textsc {TmEq-Next-Var}\), which allows us to simplify a term to t.

Sometimes it is necessary to switch the order in the delayed substitution. Two substitutions can switch places, as long as they do not depend on each other; this is what \(\textsc {TyEq-}\triangleright \textsc {-Exch}\) and \(\textsc {TmEq-Next-Exch}\) express.

Rule TmEq-Next-Comm is not used in the examples of this paper, but it implies the rule , which is needed in Paviotti’s PhD work.

Fig. 2.
figure 2figure 2

Overview of the new typing rules involving \(\triangleright \) and delayed substitutions.

2.1 Fixed Points and Guarded Recursive Types

In \(\mathsf {gDTT}\) we have for each clock \(\kappa \) valid in the current clock context a fixed-point combinator \({\textsf {fix}}^{\kappa }\). This differs from a traditional fixed-point combinator in that the type of the recursion variable is not the same as the result type; instead its type is guarded with \(\overset{\kappa }{\triangleright }{}\). When we define a term using the fixed-point, we say that it is defined by guarded recursion. When the term is intuitively a proof, we say we are proving by Löb induction [2].

Guarded recursive types are defined as fixed-points of suitably guarded functions on universes. This is the approach of Birkedal and Møgelberg [4], but the generality of the rules of \(\mathsf {gDTT}\) allows us to define more interesting dependent guarded recursive types, for example the predicates of Sect. 3.

We first illustrate the technique by defining the (non-dependent) type of guarded streams. Recall from the introduction that we want the type of guarded streams, for clock \(\kappa \), to satisfy the equation \({\textsf {Str}^{\kappa }_{A}} \equiv A \times \overset{\kappa }{\triangleright }{{\textsf {Str}^{\kappa }_{A}}}\).

The type A will be equal to \(\textsf {El}(B)\) for some code B in some universe \(\mathcal {U}_{\varDelta }\) where the clock variable \(\kappa \) is not in \(\varDelta \). We then define the code \(S_A^\kappa \) of \({\textsf {Str}^{\kappa }_{A}}\) in the universe \(\mathcal {U}_{\varDelta , \kappa }\) to be , where \(\widehat{\times }\) is the code of the (simple) product type. Via the rules of \(\mathsf {gDTT}\) we can show \({\textsf {Str}^{\kappa }_{A}} \simeq A \times \overset{\kappa }{\triangleright }{\textsf {Str}^{\kappa }_{A}}\) as desired.

Fig. 3.
figure 3figure 3

New type and term equalities in \(\mathsf {gDTT}\). Rules TyEq- \(\triangleright \)-Weak and TmEq-Next-Weak require that A and u are well-formed in a context without x. Rules TyEq- \(\triangleright \)-Exch and TmEq-Next-Exch assume that exchanging x and y is allowed, i.e., that the type of x does not depend on y and vice versa. Likewise, rule TmEq-Next-Comm assumes that exchanging the codomains of \(\xi \) and \(\xi '\) is allowed and that none of the variables in the codomains of \(\xi \) and \(\xi '\) appear in the type of u.

The head and tail operations, \(\textsf {hd}^{\kappa } : {\textsf {Str}^{\kappa }_{A}} \rightarrow A\) and \(\textsf {tl}^{\kappa } : {\textsf {Str}^{\kappa }_{A}} \rightarrow \overset{\kappa }{\triangleright }{{\textsf {Str}^{\kappa }_{A}}}\) are simply the first and the second projections. Conversely, we construct streams by pairing. We use the suggestive \({\textsf {cons}^{\kappa }}\) notation which we define as

Defining guarded streams is also done via guarded recursion, for example the stream consisting only of ones is defined as .

The rule \(\textsc {TyEq-El-}\triangleright \) is essential for defining guarded recursive types as fixed-points on universes, and it can also be used for defining more advanced guarded recursive dependent types such as covectors; see Sect. 3.

2.2 Identity Types

\(\mathsf {gDTT}\) has standard extensional identity types \({\textsf {Id}_{A}\left( t,u\right) }\) (see, e.g., Jacobs [15]) but with two additional type equivalences necessary for working with guarded dependent types. We write \({\textsf {r}_{A}t}\) for the reflexivity proof \({\textsf {Id}_{A}\left( t,t\right) }\). The first type equivalence is the rule TyEq- \(\triangleright \). This rule, which is validated by the model of Sect. 6, may be thought of by analogy to type equivalences often considered in homotopy type theory [24], such as

$$\begin{aligned} {\textsf {Id}_{A \times B}\left( \left\langle s_1, s_2 \right\rangle ,\left\langle t_1, t_2 \right\rangle \right) } \equiv {\textsf {Id}_{A}\left( s_1,t_1\right) } \times {\textsf {Id}_{B}\left( s_2,t_2\right) }. \end{aligned}$$

There are two important differences. The first is that (2) is (using univalence) a propositional type equality, whereas TyEq- \(\triangleright \) specifices a definitional type equality. This is natural in an extensional type theory. The second difference is that there are terms going in both directions in (2), whereas we would have a term of type without the rule TyEq- \(\triangleright \).

The second novel type equality rule, which involves clock quantification, will be presented in Sect. 4.

3 Examples

In this section we present some example terms typable in \(\mathsf {gDTT}\). Our examples will use a term, which we call \(\textsf {p}\eta \), of type \( \varPi (s, t : A \times B) . {\textsf {Id}_{A}\left( \pi _1 t,\pi _1 s\right) } \rightarrow {\textsf {Id}_{B}\left( \pi _2 t,\pi _2 s\right) } \rightarrow {\textsf {Id}_{A \times B}\left( t,s\right) }\). This term is definable in any type theory with a strong (dependent) elimination rule for dependent sums. The second property we will use is that \({\textsf {Str}^{\kappa }_{A}} \equiv A \times \overset{\kappa }{\triangleright }{\textsf {Str}^{\kappa }_{A}}\). Because \(\textsf {hd}^{\kappa }\) and \(\textsf {tl}^{\kappa }\) are simply first and second projections, \(\textsf {p}\eta \) also has type \( \varPi \left( xs,ys : {\textsf {Str}^{\kappa }_{A}}\right) . {{\textsf {Id}_{A}\left( \textsf {hd}^{\kappa }{xs},\textsf {hd}^{\kappa }{ys}\right) }} \rightarrow {{\textsf {Id}_{\overset{\kappa }{\triangleright }{\textsf {Str}^{\kappa }_{A}}}\left( \textsf {tl}^{\kappa }{xs},\textsf {tl}^{\kappa }{ys}\right) }} \rightarrow {{\textsf {Id}_{{\textsf {Str}^{\kappa }_{A}}}\left( xs,ys\right) }}\).

\(\textsf {zipWith}^{\kappa }\) Preserves Commutativity. In \(\mathsf {gDTT}\) we define the \(\textsf {zipWith}^{\kappa }\) function which has the type \((A \rightarrow B \rightarrow C) \rightarrow {\textsf {Str}^{\kappa }_{A}} \rightarrow {\textsf {Str}^{\kappa }_{B}} \rightarrow {\textsf {Str}^{\kappa }_{C}}\) by

We show that commutativity of f implies commutativity of \(\textsf {zipWith}^{\kappa } f\), i.e., that

$$\begin{aligned}&\varPi (f : A \rightarrow A \rightarrow B) . \left( {\varPi {\left( x,y : A\right) }.{\textsf {Id}_{B}\left( f\,x\,y,f\,y\,x\right) }}\right) \rightarrow \\&{\varPi {\left( xs,ys : {\textsf {Str}^{\kappa }_{A}}\right) }.{\textsf {Id}_{{\textsf {Str}^{\kappa }_{B}}}\left( \textsf {zipWith}^{\kappa } f\,xs\,ys,\textsf {zipWith}^{\kappa } f\,ys\,xs\right) }} \end{aligned}$$

is inhabited. The term that inhabits this type is

Here, \(\phi \) has type \(\overset{\kappa }{\triangleright }({\varPi {\left( xs,ys : {\textsf {Str}^{\kappa }_{A}}\right) }.{\textsf {Id}_{{\textsf {Str}^{\kappa }_{B}}}\left( \textsf {zipWith}^{\kappa } f\,xs\,ys,\textsf {zipWith}^{\kappa } f\,ys\,xs\right) }})\) so to type the term above, we crucially need delayed substitutions.

An Example with Covectors. The next example is more sophisticated, as it involves programming and proving with a data type that, unlike streams, is dependently typed. Indeed the generalised later, carrying a delayed substitution, is necessary to type even elementary programs. Covectors are the potentially infinite version of vectors (lists with length). To define guarded covectors we first need guarded co-natural numbers. The definition in \(\mathsf {gDTT}\) is ; this type satisfies . Using we can define the type family of covectors , where

We will not distinguish between and . As an example of covectors, we define \(\mathsf{ones}\) of type which produces a covector of any length consisting only of ones:

Although this is one of the simplest covector programs one can imagine, it does not type-check without the generalised later with delayed substitutions.

The \({\mathsf{map}}\) function on covectors is defined as

It preserves composition: the following type is inhabited

by the term

4 Coinductive Types

As discussed in the introduction, guarded recursive types on their own disallow productive but acausal function definitions. To capture such functions we need to be able to remove \(\overset{\kappa }{\triangleright }{}\). However such eliminations must be controlled to avoid trivialising \(\overset{\kappa }{\triangleright }{}\). If we had an unrestricted elimination term \(\textsf {elim} : \overset{\kappa }{\triangleright }{A} \rightarrow A\) every type would be inhabited via \({\textsf {fix}}^{\kappa }\), making the type theory inconsistent.

However, we may eliminate \(\overset{\kappa }{\triangleright }{}\) provided that the term does not depend on the clock \(\kappa \), i.e., the term is typeable in a context where \(\kappa \) does not appear. Intuitively, such contexts have no temporal properties along the \(\kappa \) dimension, so we may progress the computation without violating guardedness. Figure 4 extends the system of Fig. 2 to allow the removal of clocks in such a setting, by introducing clock quantifiers \(\forall {\kappa }{}\) [3, 7, 19]. This is a binding construct with associated term constructor \(\varLambda {\kappa }{}\), which also binds \(\kappa \). The elimination term is clock application. Application of the term t of type \(\forall {\kappa }{.A}\) to a clock \(\kappa \) is written as \(t\!\left[ \kappa \right] \). One may think of \(\forall {\kappa }{.A}\) as analogous to the type \(\forall \alpha .A\) in polymorphic lambda calculus; indeed the basic rules are precisely the same, but we have an additional construct \(\textsf {prev } {\kappa } . t\), called ‘previous’, to allow removal of the later modality \(\overset{\kappa }{\triangleright }{}\).

Typing this new construct \(\textsf {prev } {\kappa } . t\) is somewhat complicated, as it requires ‘advancing’ a delayed substitution, which turns it into a context morphism (an actual substitution); see Fig. 5 for the definition. The judgement \({\rho :_{\varDelta } \varGamma \rightarrow \varGamma '}\) expresses that \(\rho \) is a context morphism from context \(\varGamma \vdash _{\varDelta }\) to the context \(\varGamma ' \vdash _{\varDelta }\). We use the notation \(\rho [t/x]\) for extending the context morphism by mapping the variable x to the term t. We illustrate this with two concrete examples.

First, we can indeed remove later under a clock quantier:

The type is correct because advancing the empty delayed substitution in \(\overset{\kappa }{\triangleright }\) turns it into the identity substitution \(\iota \), and \(A\iota \equiv A\). The \(\beta \) and \(\eta \) rules (Fig. 6) ensure that \(\mathsf{force}\) is the inverse to the canonical term of type \(\forall {\kappa }{.A} \rightarrow \forall {\kappa }{.\overset{\kappa }{\triangleright }{A}}\).

Second, we may see an example with a non-empty delayed substitution in the term of type \(\forall {\kappa }{.\mathbb {N}}\). Recall that is syntactic sugar and so more precisely the term is


Advancing the delayed substitution turns it into the substitution mapping the variable f to the term and the variable x to the term . Using the \(\beta \) rule for \(\textsf {prev}\), then the \(\beta \) rule for \(\forall \kappa \), this simplifies to the substitution mapping f to \(\lambda n.\textsf {succ }n\) and x to \(0\). With this we have that the term (3) is equal to \(\varLambda {\kappa }{.\left( (\lambda n.\textsf {succ }n)\,0\right) }\) which is in turn equal to \(\varLambda {\kappa }{.1}\).

An important property of the term \(\textsf {prev } {\kappa } . t\) is that \(\kappa \) is bound in t; hence \(\textsf {prev } {\kappa } . t\) has type \(\forall {\kappa }{.A}\) instead of just A. This ensures that substitution of terms in types and terms is well-behaved and we do not need the explicit substitutions used, for example, by Clouston et al. [9] where the unary type-former \(\square \) was used in place of clocks. This binding structure ensures, for instance, that the introduction rule Ty- \(\varLambda \) closed under substitution in \(\varGamma \).

The rule TmEq- \(\forall \) -fresh states that if t has type \(\forall {\kappa }{.A}\) and the clock \(\kappa \) does not appear in the type A, then it does not matter to which clock t is applied, as the resulting term will be the same. In the polymorphic lambda calculus, the corresponding rule for universal quantification over types would be a consequence of relational parametricity.

We further have the construct \({\widehat{\forall }}\) and the rule Ty- \(\forall \) -code which witness that the universes are closed under \(\forall \kappa \).

To summarise, the new raw types and terms, extending those of Sect. 2, are

$$\begin{aligned} {\begin{matrix} A, B&\mathrel {::=}\cdots | ~ \forall {\kappa }{.A} \end{matrix}} {\begin{matrix} t, u&\mathrel {::=}\cdots | ~ \varLambda {\kappa }{.t} ~|~ t\!\left[ \kappa \right] ~|~ {\widehat{\forall }}{t} ~|~ \textsf {prev } {\kappa } . t \end{matrix}} \end{aligned}$$

Finally, we have the equality rule TyEq- \(\forall \) -Id analogous to the rule TyEq- \(\triangleright \). Note that, as in Sect. 2.2, there is a canonical term of type \({\textsf {Id}_{\forall {\kappa }{.A}}\left( t,s\right) } \rightarrow \forall {\kappa }{.{\textsf {Id}_{A}\left( t\!\left[ \kappa \right] ,s\!\left[ \kappa \right] \right) }}\) but, without this rule, no term in the reverse direction.

Fig. 4.
figure 4figure 4

Overview of the new typing rules for coinductive types.

Fig. 5.
figure 5figure 5

Advancing a delayed substitution.

4.1 Derivable Type Isomorphisms

The encoding of coinductive types using guarded recursive types crucially uses a family of type isomorphisms commuting \(\forall \kappa \) over other type formers [3, 19]. By a type isomorphism \(A \cong B\) we mean two well-typed terms f and g of types \(f : A \rightarrow B\) and \(g : B \rightarrow A\) such that \(f(g\,x) \equiv x\) and \(g(f\,x) \equiv x\). The first type isomorphism is \(\forall {\kappa }{.A} \cong A\) whenever \(\kappa \) is not free in A. The terms \(g = \lambda x.\varLambda {\kappa }{.x}\) of type \(A \rightarrow \forall {\kappa }{.A}\) and \(f = \lambda x . x\!\left[ \kappa _0\right] \) of type \(A \rightarrow \forall {\kappa }{.A}\) witness the isomorphism. Note that we used the clock constant \(\kappa _0\) in an essential way. The equality \(f(g\,x) \equiv x\) follows using only the \(\beta \) rule for clock application. The equality \(g(f\,x) \equiv x\) follows using by the rule TmEq- \(\forall \) -fresh.

The following type isomorphisms follow by using \(\beta \) and \(\eta \) laws for the constructs involved.

  • If \(\kappa \not \in A\) then \(\forall {\kappa }{.{\varPi {\left( x : A\right) }.B}} \cong {\varPi {\left( x : A\right) }.\forall {\kappa }{.B}}\).

  • \(\forall {\kappa }.{\varSigma \left( x :{A}\right) B} \cong \varSigma \left( y: {\forall {\kappa }.{A}}\right) \left( \forall {\kappa }.B [y[\kappa ]/x]\right) .\)

  • \(\forall {\kappa }{.A} \cong \forall {\kappa }{.\overset{\kappa }{\triangleright }{A}}\).

There is an important additional type isomorphism witnessing that \(\forall \kappa \) commutes with binary sums; however unlike the isomorphisms above we require equality reflection to show that the two functions are inverse to each other up to definitional equality. There is a canonical term of type \(\forall {\kappa }{.A} + \forall {\kappa }{.B} \rightarrow \forall {\kappa }{.(A + B)}\) using just ordinary elimination of coproducts. Using the fact that we encode binary coproducts using \(\varSigma \)-types and universes we can define a term \(\textsf {com}^{+}\) of type \(\forall {\kappa }{.(A + B)} \rightarrow \forall {\kappa }{.A} + \forall {\kappa }{.B}\) which is a inverse to the canonical term. In particular \(\textsf {com}^{+}\) satisfies the following two equalities which will be used below.

$$\begin{aligned} \begin{aligned} \textsf {com}^{+}\left( \varLambda {\kappa }{.\textsf {inl }\,t}\right)&\equiv \textsf {inl }\,\varLambda {\kappa }{.t} \end{aligned} \qquad \begin{aligned} \textsf {com}^{+}\left( \varLambda {\kappa }{.\textsf {inr}~\,t}\right)&\equiv \textsf {inr}~\, \varLambda {\kappa }{.t}. \end{aligned} \end{aligned}$$
Fig. 6.
figure 6figure 6

Type and term equalities involving clock quantification.

5 Example Programs with Coinductive Types

Let A be a type with code \(\widehat{A}\) in clock context \(\varDelta \) and \(\kappa \) a fresh clock variable. Let \(\textsf {Str}_{A} = \forall {\kappa }{.{\textsf {Str}^{\kappa }_{A}}}\). We can define head, tail and cons functions

With these we can define the acausal ‘every other’ function \({\textsf {eo}}^{\kappa }\) that removes every second element of the input stream. It is acausal because the second element of the output stream is the third element of the input. Therefore to type the function we need to have the input stream always available, so clock quantification must be used. The function \({\textsf {eo}}^{\kappa }\) of type \(\textsf {Str}_{A} \rightarrow {\textsf {Str}^{\kappa }_{A}}\) is defined as

The result is a guarded stream, but we can easily strengthen it and define \(\textsf {eo}\) of type \(\textsf {Str}_{A} \rightarrow \textsf {Str}_{A}\) as .

We can also work with covectors (not just guarded covectors as in Sect. 3). This is a dependent coinductive type indexed by conatural numbers which is the type . It is easy to define \(\overline{0}\) and \({\overline{\textsf {succ}}}\) as and . Next, we can define a transport function \(\textsf {com}^{\mathsf{Co}\mathbb {N}}\) of type \(\textsf {com}^{\mathsf{Co}\mathbb {N}}: \mathsf{Co}\mathbb {N}\rightarrow 1 + \mathsf{Co}\mathbb {N}\) satisfying

$$\begin{aligned} \begin{aligned} \textsf {com}^{\mathsf{Co}\mathbb {N}}\overline{0}&\equiv \textsf {inl }{\langle \rangle }\end{aligned} \qquad \begin{aligned} \textsf {com}^{\mathsf{Co}\mathbb {N}}({\overline{\textsf {succ}}}n)&\equiv \textsf {inr}~n. \end{aligned} \end{aligned}$$

This function is used to define the type family of covectors as where is the term

Using term equalities (4) and (5) we can derive the type isomorphisms


which are the expected properties of the type of covectors.

A simple function we can define is the tail function

Note that (6) is needed to type \(\textsf {tl}\). The \({\textsf {map}}\) function of type

is defined as where \(\textsf {map}^{\kappa }\) is

5.1 Lifting Guarded Functions

In this section we show how in general we may lift a function on guarded recursive types, such as addition of guarded streams, to a function on coinductive streams. Moreover, we show how to lift proofs of properties, such as the commutativity of addition, from guarded recursive types to coinductive types.

Let \(\varGamma \) be a context in clock context \(\varDelta \) and \(\kappa \) a fresh clock. Suppose A and B are types such that \(\varGamma \vdash _{\varDelta ,\kappa } A \, \textsf {type}\) and \(\varGamma , x : A \vdash _{\varDelta ,\kappa } B \, \textsf {type}\). Finally let f be a function of type \(\varGamma \vdash _{\varDelta ,\kappa } f : {\varPi {\left( x : A\right) }.B}\). We define \(\mathfrak {L}(f)\) satisfying the typing judgement \(\varGamma \vdash _{\varDelta } \mathfrak {L}(f) : {\varPi {\left( y : \forall {\kappa }{.A}\right) }.\forall {\kappa }{.\left( B\left[ y\!\left[ \kappa \right] /x\right] \right) }}\) as .

Now assume that \(f'\) is another term of type \({\varPi {\left( x : A\right) }.B}\) (in the same context) and that we have proved \(\varGamma \vdash _{\varDelta ,\kappa } p : {\varPi {\left( x : A\right) }.{\textsf {Id}_{B}\left( f\,x,f'\,x\right) }}\). As above we can give the term \(\mathfrak {L}(p)\) the type \({\varPi {\left( y : \forall {\kappa }{.A}\right) }.\forall {\kappa }{.{\textsf {Id}_{B\left[ y\!\left[ \kappa \right] /x\right] }\left( f(y\!\left[ \kappa \right] ),f'(y\!\left[ \kappa \right] )\right) }}}.\) which by using the type equality TyEq- \(\forall \) -Id and the \(\eta \) rule for \(\forall \) is equal to the type \({\varPi {\left( y : \forall {\kappa }{.A}\right) }.{\textsf {Id}_{\forall {\kappa }{.B\left[ y\!\left[ \kappa \right] /x\right] }}\left( \mathfrak {L}(f)\,y,\mathfrak {L}(f')\,y\right) }}\). So we have derived a property of lifted functions \(\mathfrak {L}(f)\) and \(\mathfrak {L}(f')\) from the properties of the guarded versions f and \(f'\). This is a standard pattern. Using Löb induction we prove a property of a function whose result is a “guarded” type and derive the property for the lifted function.

For example we can lift the \(\textsf {zipWith}^{}\) function from guarded streams to coinductive streams and prove that it preserves commutativity, using the result on guarded streams of Sect. 3.

6 Soundness

\(\mathsf {gDTT}\) can be shown to be sound with respect to a denotational model interpreting the type theory. The model is a refinement of Bizjak and Møgelberg’s [7] but for reasons of space we leave the description of a full model of \(\mathsf {gDTT}\) for future work. Instead, to provide some intuition for the semantics of delayed substitutions, we just describe how to interpret the rule


in the case where we only have one clock available.

The subsystem of \(\mathsf {gDTT}\) with only one clock can be modelled in the category \(\mathcal {S}\), known as the topos of trees [5], the presheaf category over the first infinite ordinal \(\omega \). The objects X of \(\mathcal {S}\) are families of sets \(X_1,X_2,\ldots \) indexed by the positive integers, together with families of restriction functions \(r_i^X: X_{i+1}\rightarrow X_i\) indexed similarly. There is a functor \({\blacktriangleright }: \mathcal {S}\rightarrow \mathcal {S}\) which maps an object X to the object

$$\begin{aligned} 1 \mathop {\longleftarrow }^{!}\, X_1\, \mathop {\longleftarrow }^{r_1^X}\, X_2\, \mathop {\longleftarrow }^{r_2^X}\, X_3 \, {\longleftarrow }\cdots \end{aligned}$$

where ! is the unique map into the terminal object.

In this model, a closed type A is interpreted as an object of \(\mathcal {S}\) and the type \(x : A \vdash _{} B \, \textsf {type}\) is interpreted as an indexed family of sets \(B_i(a)\), for a in \(A_i\) together with maps \(r_i^B(a):B_{i+1}(a) \rightarrow B_{i}(r_i^A(a))\). The term t in (7) is interpreted as a morphism \(t : 1 \rightarrow \triangleright {}{A}\) so \(t_i(*)\) is an element of \(A_i\) (here we write \(*\) for the element of 1).

The type is then interpreted as the object X, defined by

$$\begin{aligned} X_1&= 1&X_{i+1}&= B_i(t_{i+1}(*)). \end{aligned}$$

Notice that the delayed substitution is interpreted by substitution (reindexing) in the model; the change of the index in the model (\(B_{i}\) is reindexed along \(t_{i+1}(*)\)) corresponds to the delayed substitution in the type theory. Further notice that if B does not depend on x, then the interpretation of reduces to the interpretation \(\triangleright {}{B}\), which is defined to be \({\blacktriangleright }\) applied to the interpretation of B.

The above can be generalised to work for general contexts and sequences of delayed substitutions, and one can then validate that the definitional equality rules do indeed hold in this model.

7 Related Work

Birkedal et al. [5] introduced dependent type theory with the \(\triangleright \) modality, with semantics in the topos of trees. The guardedness requirement was expressed using the syntactic check that every occurrence of a type variable lies beneath a \(\triangleright \). This requirement was subsequently refined by Birkedal and Møgelberg [4], who showed that guarded recursive types could be constructed via fixed-points of functions on universes. However, the rules considered in these papers do not allow one to apply terms of type \(\triangleright (\varPi (x : A) . B)\), as the applicative functor construction was defined only for simple function spaces. They are therefore less expressive for both programming (consider the covector \(\textsf {ones}\), and function \({\mathsf{map}}\), of Sect. 3) and proving, noting the extensive use of delayed substitutions in our example proofs. They further do not consider coinductive types, and so are restricted to causal functions.

The extension to coinductive types, and hence acausal functions, is due to Atkey and McBride [3], who introduced clock quantifiers into a simply typed setting with guarded recursion. Møgelberg [19] extended this work to dependent types and Bizjak and Møgelberg [7] refined the model further to allow clock synchronisation.

Clouston et al. [9] introduced the logic \(L\mathsf {g}\lambda \) to prove properties of terms of the (simply typed) guarded \(\lambda \)-calculus, \(\mathsf {g}\lambda \). This allowed proofs about coinductive types, but not in the integrated fashion supported by dependent type theories. Moreover it relied on types being “total”, a property that in a dependently typed setting would entail a strong elimination rule for \(\triangleright \), which would lead to inconsistency.

Sized types [14] have been combined with copatterns [1] as an alternative type-based approach for modular programming with coinductive types. This work is more mature than ours with respect to implementation and the demonstration of syntactic properties such as normalisation, and so further development of \(\mathsf {gDTT}\) is essential to enable proper comparison. One advantage of \(\mathsf {gDTT}\) is that the later modality is useful for examples beyond coinduction, and beyond the utility of sized types, such as the guarded recursive domain equations used to model program logics [23].

8 Conclusion and Future Work

We have described the dependent type theory \(\mathsf {gDTT}\). The examples we have detailed show that \(\mathsf {gDTT}\) provides a setting for programming and proving with guarded recursive and coinductive types.

In future work we plan to investigate an intensional version of the type theory and construct a prototype implementation to allow us to experiment with larger examples. Preliminary work has suggested that the path type of cubical type theory [10] interacts better with the new constructs of \(\mathsf {gDTT}\) than the ordinary Martin-Löf identity type.

Finally, we are investigating whether the generalisation of applicative functors [18] to apply over dependent function spaces, via delayed substitutions, might also apply to examples quite unconnected to the later modality.