Hostname: page-component-76c49bb84f-t7r7g Total loading time: 0 Render date: 2025-07-12T16:05:50.312Z Has data issue: false hasContentIssue false

A contextual formalization of structural coinduction

Published online by Cambridge University Press:  07 July 2025

PAUL DOWNEN
Affiliation:
University of Massachusetts, Lowell, MA, USA (e-mail: paul_downen@uml.edu)
ZENA M. ARIOLA
Affiliation:
University of Oregon, Eugene, OR 97403, USA (e-mail: ariola@cs.uoregon.edu)
Rights & Permissions [Opens in a new window]

Abstract

Structural induction is pervasively used by functional programmers and researchers for both informal reasoning as well as formal methods for program verification and semantics. In this paper, we promote its dual—structural coinduction—as a technique for understanding corecursive programs only in terms of the logical structure of their context. We illustrate this technique as an informal method of proofs which closely match the style of informal inductive proofs, where it is straightforward to check that all cases are covered and the coinductive hypotheses are used correctly. This intuitive idea is then formalized through a syntactic theory for deriving program equalities, which is justified purely in terms of the computational behavior of abstract machines and proved sound with respect to observational equivalence.

Information

Type
Research Article
Creative Commons
Creative Common License - CCCreative Common License - BY
This is an Open Access article, distributed under the terms of the Creative Commons Attribution licence (https://creativecommons.org/licenses/by/4.0/), which permits unrestricted re-use, distribution and reproduction, provided the original article is properly cited.
Copyright
© The Author(s), 2025. Published by Cambridge University Press

1 Introduction

Every day, a large community of computer scientists—working on applications and theory of functional programming, verification, type systems, and semantics—employ induction to effectively reason about software and its behavior. Whether mechanically checked by a computer or informally written with pen and paper, various forms of inductive techniques are applied with confidence that the result is well-founded. What is the secret to this confidence? The inductive principle itself limits recursive reasoning to only pieces of the original example which are structurally smaller than it (Burstall, Reference Burstall1969).

Coinduction, the dual to induction, is not understood or used with the same level of familiarity or frequency. It is usually relegated to coalgebras (Rutten, Reference Rutten2019), since traditionally only the categorical setting speaks clearly about the duality that relates induction and coinduction. Despite its difficulty, coinduction remains an essential principle for dealing with important software systems like concurrent processes, web servers, and operating systems (Barwise & Moss, Reference Barwise and Moss1997), which endlessly run while interacting with their environment.

Why, then, does coinduction see less use in both informally written and mechanically verified proofs of programming language theory? One major obstacle is that coinduction is easy to formulate in a dangerous way, where the recursive nature of coinduction seems too powerful on the surface and can lead to nonsensical, viciously circular proofs. To tamper down on this unreasonable power, we must externally check that the coinductive hypothesis is only applied in certain special contexts, which fundamentally breaks compositional reasoning; certain proofs may seem valid until they are embedded into a larger context.

In this paper, we aim to alleviate the non-compositional difficulty of coinduction by reformulating it to more closely resemble the familiar forms of induction used in practice, with the hope that this presentation will make coinduction more suitable for widespread use in programming environments (Gordon, Reference Gordon2017). Our methodology is to work in a setting based on Curien & Herbelin (Reference Curien and Herbelin2000) where the important contexts are reified into first-class objects that can be labeled and have a predictable structure—similar to inductive objects like numbers and trees which can be named and analyzed structurally. The key idea here is that the coinductive principle limits recursion to only contexts which are structurally smaller than the starting point of coinduction, and that this requirement is checked locally by just looking at the label where corecursion happens. This paper then demonstrates how coinduction—in terms of both an informal pen-and-paper methodology as well as a formal program logic for proving equality of corecursive programs with or without side effects—can be seen as induction on the context. We thus avoid resorting to the least or greatest fixed point notions of lattice theory and domain theory to explain the duality between induction and coinduction (Gordon, Reference Gordon1994). Both the informal technique and formal system are sound in the sense that every syntactically derived equality implies an observational equivalence: the program logic is proven sound with respect to an adequate denotational model of observational equivalence defined in terms of its operational semantics.

Having (co)inductive reasoning principles expressed within a calculus follows previous work (Curien & Herbelin, Reference Curien and Herbelin2000) on defining a calculus that directly expresses the dualities commonly seen in logic, specifically (Downen & Ariola, Reference Downen and Ariola2023). For example, the duality between true and false computationally appears as the duality between a process that produces information and one that consumes information (Downen & Ariola, Reference Downen and Ariola2018). We think our work follows the spirit of Kozen & Silva (Reference Kozen and Silva2017); the authors present several examples of the use of coinduction in informal-style mathematical arguments. This paper strives to put those arguments on solid ground that can be justified only in terms of computation. We believe our approach only requires the same mathematical skills already used by computer scientists to reason inductively over data structures, while still capturing the essential property of compositionality. Coinduction is explained in terms of subcomponents, much the same way structural induction is presented.

In addition to giving a compositional and computational foundation for coinduction, this paper studies both induction and coinduction proof principles in the setting of a language derived from classical logic à la Curry–Howard, and identifies the syntactic conditions that must be imposed on an argument to make it correct in the presence of computational effects. For example, it is well known that one needs to be careful applying induction in non-strict languages such as Haskell. For example, the optimization

$$ x * 0 \overset{?}= 0 $$

can be proved by traditional induction over the natural numbers, but it does not hold according to a call-by-name evaluation strategy because this proof does not account for the case of nontermination. Letting $\Omega$ stand for a nonterminating expression, notice that plugging in $\Omega$ for x leads to an incorrect equality; $\Omega * 0 = 0$ claims that a nonterminating expression $\Omega * 0$ is equal to a constant value. Even worse, if we consider other computational effects such as aborting a computation, then substituting $\texttt{abort} 1$ for x leads to

$$ (\texttt{abort} 1) * 0 = 0 $$

seemingly equating 1 to 0.

Dually, strict languages such as OCaml suffer from the same kinds of problems where naïve coinduction is not always correct. For example, consider infinite stream ( $x_0, x_1, x_2, x_3, \dots$ ) with the two main projections:

\begin{align*} \mathrm{head}(x_0, x_1, x_2, \dots) &= x_0 & \mathrm{tail}(x_0, x_1, x_2, \dots) &= x_1, x_2, \dots\end{align*}

Now, intuitively, taking the ${head}$ and ${tail}$ of a stream and putting them back does nothing:

\begin{align*} \mathrm{head} \, \, xs, \mathrm{tail} \, xs &= xs\end{align*}

and this equation does indeed hold, under both call-by-name and call-by-value evaluation, both with or without side effects (as we will see in more detail later). So intuitively, we should be able to apply this equality a second time to expand out two places, right?

(1.1) \begin{align} \mathrm{head} \, \, xs, \mathrm{head}(\mathrm{tail} \, xs), \mathrm{tail}(\mathrm{tail} \, xs) &\mathrel{\overset{?}{=}} xs\end{align}

As it turns out, this equation fails in call-by-value languages with side effects, similar to the problem with $x * 0 = 0$ in call-by-name. Of course, in the call-by-value setting, we need to be careful of timing considerations when handling infinite objects: an infinite stream cannot be fully evaluated in advance. So to support infinite streams, we ensure that the head and tail of the stream are only computed on demand, that is, at the last moment when they are required. As such, any pair M, N of a head element M and tail N is treated as a first-class value, even if M and N have not been evaluated yet. Now, consider the partial stream value $0, \Omega$ : asking for its head element returns 0, and asking for its tail does not return (it incurs the non-terminating computation $\Omega$ ). Plugging in $0, \Omega$ for xs in (1.1) gives

\begin{align*} \mathrm{head} \, (0, \Omega) , \mathrm{head}(\mathrm{tail} (0, \Omega)), \mathrm{tail}(\mathrm{tail} (0, \Omega)) &= (0, \Omega)\end{align*}

This leads to a counterexample in call-by-value, where $\mathrm{\textbf{let}} \, \, z = \mathrm{tail}(0, \Omega) \mathrm{\textbf{in}} \, 1$ does not terminate because $\mathrm{tail}(0, \Omega) = \Omega$ which never returns a value that can be bound to z, but

\begin{align*} &\mathrm{\textbf{let}} \, \, z = \mathrm{tail}(\mathrm{head}(0, \Omega), \mathrm{head}(\mathrm{tail}(0, \Omega)), \mathrm{tail}(\mathrm{tail}(0, \Omega))) ~ \mathrm{\textbf{in}} \, 1 & =\\ &\mathrm{\textbf{let}} \, \, z = \Omega, \Omega ~ \mathrm{\textbf{in}} \, 1 & =\\ & 1\end{align*}

In place of non-termination, plugging in $(0, \texttt{abort}~2)$ for xs in (1.1) also serves as another example using abort as a side-effect:

\begin{align*}& \mathrm{\textbf{let}} \, \, z = \mathrm{tail}(0, \texttt{abort}~2) \mathrm{\textbf{in}} \, 1 & = \\ &\mathrm{\textbf{let}} \, z = \mathrm{tail}(\mathrm{head}(0, \texttt{abort}~2), \mathrm{head}(\mathrm{tail}(0, \texttt{abort}~2)),\mathrm{tail}(\mathrm{tail}(0, \texttt{abort}~2 ))) ~ \mathrm{\textbf{in}} \, 1 &\end{align*}

where the left-hand side aborts with 2, but the right-hand side returns 1.

For a more practical example, consider these informally defined operations on streams:

\begin{align*} {evens}~(x_0, x_1, x_2, x_3, \dots) &= x_0, x_2, x_4, \dots \\ {odds}~(x_0, x_1, x_2, x_3, \dots) &= x_1, x_3, x_5, \dots \\ {merge}~(x_0, x_1, x_2, \dots)~(y_0, y_1, y_2, \dots) &= x_0, y_0, x_1, y_1, x_2, y_2, \dots\end{align*}

The ${evens}$ function selects only the even elements of a stream, ${odds}$ selects only the odd elements of a stream, and ${merge}$ interleaves two streams together by alternating between them. It should be intuitive that selecting the even and odd elements of a stream and merging them back together is the same as the original stream:

\begin{align*} {merge}~({evens}~xs)~({odds}~xs) \overset{?}= xs\end{align*}

We can prove this fact by conventional methods of coinduction, and this paper shows that it also holds true using our notion of strong structural coinduction under call-by-name evaluation whether or not xs contains side effects. However, strong structural coinduction is not sound in a call-by-value language with effects, and as a consequence, the intuitive equality is incorrect. What goes wrong? The timing considerations of when the head or tail of a stream are computed become important in call-by-value and need to be explicated. So if we rewrite ${evens}$ , ${odds}$ , and ${merge}$ more formally as

\begin{align*} {evens}~xs &= {head}~xs, {odds}~({tail}~xs) \\ {odds}~xs &= {evens}~({tail}~xs) \\ {merge}~xs~ys &= {head}~xs, {head}~ys, {merge}~({tail}~xs)~({tail}~ys)\end{align*}

then notice that ${merge}$ applied to any two stream values xs and ys will always return a stream starting with at least two comma-separated elements. So if we consider the counter-example stream value $0, \Omega$ with exactly one comma, notice that ${odds}~(0, \Omega) = {evens}~\Omega$ which does not terminate. Thus, ${merge}~({evens}~(0, \Omega))~({odds}~(0, \Omega))$ does not terminate, too, which is immediately different from the value $0, \Omega$ . As a second counterexample, consider the stream value $0, 1, 2, \Omega$ with three commas, we will return a value:

\begin{align*} {merge}~({evens}~(0, 1, 2, \Omega))~({odds}~(0, 1, 2, \Omega)) = 0, 1, \Omega\end{align*}

but that value can be differentiated from the starting stream $0, 1, 2, \Omega$ by asking for the third element (2) via $\mathrm{head}(\mathrm{tail}(\mathrm{tail} \, ~ xs))$ . Notice in each case, the stream returned by ${merge}$ always has an even number of comma-separated elements; if the starting xs has an odd number of elements before $\Omega$ , the last one is forgotten. As before, using an $\texttt{abort}$ in place of $\Omega$ gives us alternative abort-based counter-examples. So plugging in the partial stream value $0, \texttt{abort}~1$ causes ${merge}~({evens}~(0, \texttt{abort}~1))~({odds}~(0,\texttt{abort}~1))$ to immediately abort with 1 instead of returning some value, and plugging in $0, 1, 2, \texttt{abort}~3$ returns the smaller partial stream $0, 1, \texttt{abort}~3$ .

The remainder of this paper will give a firm, unambiguous, computational foundation for reasoning about corecursive programs using structural coinduction, including the subtle timing implications when side effects are involved. As an example of an inductive type we take the canonical definition of natural numbers, and for coinductive types we consider streams, building on top of the abstract machine language from Downen & Ariola (Reference Downen and Ariola2023), which defines a calculus of primitive recursion and corecursion. Here, we extend that calculus with an informal (“pen-and-paper”) proof technique for structural coinduction as well as a formal logic for soundly deriving equalities between programs with control effects. While we focus on examples involving natural numbers and streams, the reasoning techniques discussed here are applicable to other data and codata types. More specifically, we provide the following contributions:

  • Section 2 provides examples of applying informal (co)inductive reasoning to programs which use (co)recursion to process (co)inductive types like numbers and streams.

  • Section 3 introduces the differences between intensional and extensional equality in the presence of (co)inductive types and gives a sound, formal program logic for reasoning (co)inductively about (co)recursive programs with control effects. It also discusses how to soundly generalize the induction principle for call-by-value and soundly generalize the coinduction principle for call-by-name.

  • Section 4 discusses the contrast in expressive power between the different (co)inductive principles: restricted and universally sound versus unrestricted and conditionally sound. To do so, we derive a number of more familiar reasoning principles, such as strong induction on the numbers, bisimulation, and compositionality of coinduction.

  • Section 5 provides a proof that the program logic in Section 3 is sound: syntactic formal proofs of equality imply semantic contextual equivalence, and more specifically, 0 is not equal to 1. This proof is modeled in terms of a logical relation based on the notion of orthogonality between producers and consumers.

2 (Co)Inductive reasoning about (co)recursive programs

Skilled functional programmers are quite adept at using induction, both for writing their programs and reasoning about them. For example, we can follow the inductive structure of the usual natural number type,

to inductively define the addition $plus : \mathrm{Nat} \to \mathrm{Nat} \to \mathrm{Nat}$ by the patterns of Nat like so:

\begin{align*} plus~\mathrm{zero}~y &= y \\ plus~(\mathrm{succ} \, x)~y &= \mathrm{succ} \, (plus~x~y)\end{align*}

Why is plus well-founded—meaning it never causes an infinite loop, and always returns a valid result for any valid arguments? Because its first argument always gets smaller (Burstall, Reference Burstall1969); the x passed into the recursive call $plus~x~y$ is a piece of the original argument $\mathrm{succ} \, x$ from the call $plus~(\mathrm{succ} \, x)~y$ that triggered it (a property we can statically check in the definition).

2.1 Structural induction

To reason about functions like plus that take Nats as arguments, programmers can also reason by induction that follows the structure of Nat in the same way the code is written. For example, the very definition of plus is first built on the identity of addition, that $plus~\mathrm{zero}~y = y$ for any number y, so it holds by just calculation with no further verification required. However, addition’s second identity law, $plus~x~\mathrm{zero} = x$ for any number x, cannot be directly calculated in the same way. Instead, matching the inductive structure of plus itself, we have to prove this property by cases on what that first argument x might be.

Example Theorem 2.1. For all x of type Nat, $plus~x~\mathrm{zero} = x$ .

Proof. By induction on the structure of the value x

  • $x = \mathrm{zero}$ . We have: $plus~{\mathrm{zero}}~{\mathrm{zero}} = \mathrm{zero} = x$ , by definition of plus.

  • $x = \mathrm{succ} \, x'$ . Assume the inductive hypothesis $plus~x'~{\mathrm{zero}} = x'$ . From there,

Why is the proof of Example Theorem 2.1 well-founded—meaning it does not contain any vicious circle in its reasoning? In the inductive hypothesis, we assume that Example Theorem 2.1 is true for the specific x’ that is the predecessor of the x we started with. As such, the cyclic reasoning always applies to a strictly smaller x, and the inductive hypothesis can never lead to a vicious cycle no matter how we use it, so no further checks are necessary to validate this proof. More formally, this inductive argument is justified because the set of natural numbers of type Nat is a least fixed point (Pierce, Reference Pierce2002): it is the smallest set containing zero and closed under succ.

2.2 Coinductive programs and proofs

The correspondence between inductive type, inductive program, and inductive proof, all line up quite neatly in the functional paradigm, with each of them following exactly the same structure. Since coinduction is the logical dual of induction, should not this correspondence naturally extend to coinductive structures like infinite streams? One can define the type of streams coinductively as the largest data type (i.e., greatest fixed point, or final coalgebra)

built from the Cons constructor—appending an element to the front of another stream—without any base case for the empty stream. From there, coinductive functions—like $always : a \to \mathrm{Stream} \, a$ which returns the stream that always contains the same value of type a, or $iterate : (a \to a) \to a \to \mathrm{Stream} \, a$ that builds an infinite stream from some original a by repeatedly applying a given function to it—can be defined cyclically like so:

\begin{align*} always~x &= \mathrm{Cons} \, x ~ (always~x) & iterate~f~x &= \mathrm{Cons} \, x ~ (iterate~f~(f~x))\end{align*}

Why are always and iterate well-founded? The answer here is not so clear; at first glance, they look like infinite loops that never return a definite answer. However, one justification is that both definitions are productive: they always return a Cons before recursing. In other words, the self-references of always and iterate are both found inside a Cons (that is, in the context $\mathrm{Cons}~first~\dots$ ). If we assume lazy evaluation of Cons this can be enough to prevent infinite loops for well-behaved observers of the stream; trying to access the “last” element of an infinite stream is not a well-behaved observer. While this justification may not be as self-evident as the structural induction of functions like plus, at least it is a property that can be syntactically checked in the specific definitions of always and iterate.

Example Theorem 2.2. For all values x, $iterate~(\lambda y.y)~x = always~x$ .

Proof. Assume the coinductive hypothesis

$$iterate~(\lambda y.y)~x = always~x ~. $$

From there,

Why is the proof of Example Theorem 2.2 well-founded? Compared to the inductive proof, skepticism of this form of coinduction is more warranted. After all, the proof begins by immediately assuming the very fact it is trying to prove, with no stipulation! What’s to stop us from this much simpler, but hopelessly vicious, “coinductive” proof of Example Theorem 2.2?

Bad Proof Assume the coinductive hypothesis $iterate~(\lambda y.y)~x = always~x$ . From the coinductive hypothesis, it follows that $iterate~(\lambda y.y)~x = always~x$ , as required.

This bad proof is obviously invalid, even though it “proved” the goal through a trivial sequence of apparently valid steps (introducing a hypothesis and using it). What is the difference between the bad proof above and the good proof of Example Theorem 2.2? The good proof only tried to use the coinductive hypothesis “inside” a Cons, whereas the bad proof just nakedly used the coinductive hypothesis outside of any Cons. Thus, somehow a coinductive proof of this form must be very careful that certain hypotheses can only be used in certain contexts, whatever that means, even if they are a perfect match for the current goal.

The concern over even a trivial theorem like Example Theorem 2.2 shows the potential breakdown of the correspondence of coinductive types, coinductive programs, and coinductive proofs; at each step, our certainty in the basic structures wanes. Even if the intuition for distinguishing “good” from “bad” programs may be fraught, a formal system like a proof assistant might be up to the task of regulating context-sensitive uses of the coinductive hypothesis to verify a proof. But a human that needs to understand a proof with informal reasoning, which has no perfect overseer like a mental proof assistant, can quickly become overwhelmed as the theorems and proofs grow ever larger. No wonder why coinduction fills us with such trepidation.

Instead, what is needed is a style of coinductive reasoning which is not burdened by precariously implicit context-sensitive rules of validity. Or put another way, the context-sensitivity imposed by coinduction should be made an explicit part of the coinductive hypothesis, so that it may be used freely, and fearlessly, in any place that it fits.

The first step is to shift our view away from coinductively-defined data types, to coinductively-defined codata types (Hagino, Reference Hagino1987). Rather than constructors, codata types define the basic observations, or projections, allowed on values of the type. For infinite streams, these are the head and tail projections that access the first element and the remainder of the stream, respectively, as described in the following declaration:

While abstractly these may be two views of the same isomorphic structure, they give us a different way to understand coinduction and the operational meaning of programs. With codata types, we define programs by matching on the structure of their projections, dual to the way function programmers define functions like plus by matching on the structure of constructors of data types. For example, the always and iterate functions can be rewritten in terms of copatterns (Abel et al., Reference Abel, Pientka, Thibodeau and Setzer2013) like so:

\begin{align*}\begin{aligned} \mathrm{head}(always~x) &= x \\ \mathrm{tail}(always~x) &= always~x\end{aligned}&&\begin{aligned} \mathrm{head}(iterate~f~x) &= x \\ \mathrm{tail}(iterate~f~x) &= iterate~f~(f~x)\end{aligned}\end{align*}

Here, head and tail are seen as projection functions, and the streams returned by $always~x$ and $iterate~f~x$ are defined by the two lines, describing what their head and tail is.

But copatterns alone are not enough. We also need to label our context, so that the language itself is expressive enough to regulate how to control the use of coinduction to certain contexts. To do so, we have to move outside of pure functional programming, based on intuitionistic logic, to a more language based on classical logic with labels and jumps. One such language (Downen et al., Reference Downen, Johnson-Freyd and Ariola2015) is modeled on the sequent calculus (Curien & Herbelin, Reference Curien and Herbelin2000), which provides a syntax for writing contextual observations as first-class objects. In this sequent style, a Greek letter $\alpha$ , $\beta$ ,, stands for an observer of values, and the command $\langle {x} |\!| {\alpha} \rangle$ says that the observer $\alpha$ is applied to x, or symmetrically, that the value x is returned to $\alpha$ .

Rather than viewing the Stream operations head and tail as functions, as we did above, we could instead view them as primitive ways to build new observations. So if $\alpha$ is expecting to observe a value of type a, then the composition $\mathrm{head} \, \, \alpha$ observes a value of type $\mathrm{Stream} \, a$ by taking its first element and passing it to $\alpha$ . Similarly, if $\beta$ is expecting to observe a value of type $\mathrm{Stream} \, a$ , then $\mathrm{tail} \, \beta$ observes a value of type $\mathrm{Stream} \, a$ by discarding its first element and passing the rest to $\beta$ . Putting them together, the observation $\mathrm{tail} \, (\mathrm{head} \, \, \beta)$ should be read as first observing the tail of a stream and then applying the head to that result so that $\beta$ receives the second element of the stream. The two different views—head and tail as functions versus observations—are always equal to one another:

(2.1) \begin{align} \langle {\mathrm{head} \, s} |\!| {\alpha} \rangle &= \langle {s} |\!| {\mathrm{head} \, \alpha} \rangle & \langle {\mathrm{tail} \, s} |\!| {\beta} \rangle &= \langle {s} |\!| {\mathrm{tail} \, \beta} \rangle \end{align}

In this observer-centric style, we can further refine always and iterate by labeling the full context in which they are observed in a command, $\langle {always~x} |\!| {\alpha} \rangle$ and $\langle {iterate~f~x} |\!| {\alpha} \rangle$ . The two definitions then follow by matching on the structure of the observer $\alpha$ , which must be built by either a head or tail projection.

\begin{align*}\begin{alignedat}{2} & \langle {always~x} |\!| {\mathrm{head} \, \beta} \rangle &&= \langle {x} |\!| {\beta} \rangle \\ & \langle {always~x} |\!| {\mathrm{tail} \, \, \alpha'} \rangle &&= \langle {always~x} |\!| {\alpha'} \rangle\end{alignedat}&&\begin{alignedat}{2} & \langle {iterate~f~x} |\!| {\mathrm{head} \, \beta} \rangle &&= \langle {x} |\!| {\beta} \rangle \\ & \langle {iterate~f~x} |\!| {\mathrm{tail} \, \, \alpha'} \rangle &&= \langle {iterate~f~(f~x)} |\!| {\alpha'} \rangle\end{alignedat}\end{align*}

Now, the fact that these corecursive functions are well-founded follows the same basic reasoning as the recursive function plus: all instances of self-reference are invoked with a strictly smaller observer. In particular, the observer $\alpha'$ in the corecursive call $\langle {always~x} |\!| {\alpha'} \rangle$ is a piece of the original observer $\mathrm{tail} \, \, \alpha'$ from the command $\langle {always~x} |\!| {\mathrm{tail} \, \, \alpha'} \rangle$ . Similarly, the observer of the corecursive call $\langle {iterate~f~(f~x)} |\!| {\alpha'} \rangle$ came from a piece of the observer in the proceeding command $\langle {iterate~f~x} |\!| {\mathrm{tail} \, \, \alpha'} \rangle$ . So copattern-matching over observers restores the symmetry between recursive functions (which consume inductively defined arguments) and corecursive functions (which produce coinductively-defined results).

2.3 Structural coinduction

What about proofs involving these programs? Let’s try to prove the analogous version of Example Theorem 2.2 but in the context of an observer labeled $\alpha$ .

Example Theorem 2.3. For all values x of type a and all observers $\alpha$ of type $\mathrm{Stream} \, a$ , $\langle {iterate~(\lambda y.y)~x} |\!| {\alpha} \rangle = \langle {always~x} |\!| {\alpha} \rangle$ .

Proof By coinduction on the stream received by $\alpha$ , i.e., by induction on the structure of $\alpha$ :

  • $\alpha = \mathrm{head} \, \beta$ . We have $ \langle {iterate~(\lambda y.y)~x} |\!| {\mathrm{head} \, \beta} \rangle = \langle {x} |\!| {\beta} \rangle = \langle {always~x} |\!| {\mathrm{head} \, \beta} \rangle $ by definition of iterate and always.

  • $\alpha = \mathrm{tail} \, \, \alpha'$ . Assume the coinductive hypothesis

    $$ \langle {iterate~(\lambda y.y)~x} |\!| {\alpha'} \rangle = \langle {always~x} |\!| {\alpha'} \rangle.$$
    From there,

Notice how the proof of Example Theorem 2.3 above follows much closer the overall shape of the inductive proof of Example Theorem 2.1. First, the coinductive hypothesis is only introduced in the step for $\alpha = \mathrm{tail} \, \, \alpha'$ ; as with induction, the coinductive hypothesis is not available to show the base case of $\alpha = \mathrm{head} \, \beta$ . Furthermore, the coinductive hypothesis $ \langle {iterate~(\lambda y.y)~x} |\!| {\alpha'} \rangle = \langle {always~x} |\!| {\alpha'} \rangle$ carries enough information to fully dictate the valid contexts in which it can be used. In particular, we can only assume the goal (that $iterate~(\lambda y.y)~x$ is equal to $always~x$ ) when observed by $\alpha'$ , the specific ancestor to the original observer $\alpha = \mathrm{tail} \, \, \alpha'$ . There is no way to use the coinductive hypothesis to equate these two streams when seen by any other observer. In particular, the coinductive hypothesis doesn’t even apply to the original goal $ \langle {iterate~(\lambda y.y)~x} |\!| {\alpha} \rangle = \langle {always~x} |\!| {\alpha} \rangle$ , like we did in the bad coinductive proof, because $\alpha \neq \alpha'$ . As such, even though the proof above is informal, there is no longer any ambiguity about its validity, so no further checks are necessary to avoid vicious cycles. Since it follows the structure of the context, we call it structural coinduction.

But have we proved the same result; are Example Theorems 2.2 and 2.3 logically the same? In order to compare the two, we can employ the notion of observational equivalence, which says that two terms are equal exactly when no observer can tell them apart. Spelled out in terms of labeled contexts, observational equivalence is the principle that, for any terms M and N (without a free reference to $\alpha$ ):

\begin{align*} M = N \text{ if and only if, for all } \alpha, \langle {M} |\!| {\alpha} \rangle = \langle {N} |\!| {\alpha} \rangle\end{align*}

Applying this principle to Example Theorems 2.2 and 2.3, we know for all values x,

\begin{alignat*}{2} iterate~(\lambda y.y)~x = always~x \text{ if and only if, for all } \alpha, \langle {iterate~(\lambda y.y)~x} |\!| {\alpha} \rangle = \langle {always~x} |\!| {\alpha} \rangle\end{alignat*}

So the two theorems state the same equality, up to observational equivalence.

Note that we can derive the result of applying head and tail as functions to iterate via observational equivalence. Starting with a generic $\alpha$ , we can convert these function applications to observations on top of $\alpha$ to match the definition of iterate as follows:

and thus by observational equivalence, we have

(2.2) \begin{align} \mathrm{head}(iterate~f~x) &= x \end{align}
(2.3) \begin{align} \\ \mathrm{tail}(iterate~f~x) &= iterate~f~(f~x) \end{align}

Notice that these equations derived by observational equivalence are exactly the same as the purely functional, copattern-matching definition of iterate that we gave above. In other words, the two copattern-based definitions—one in a functional style, and the other matching on the structure of a labeled observer—are equivalent.

Let’s continue with one more example of structural coinduction. Here is a definition for mapping a function over all elements in an infinite stream, where we use head and tail as both part of the main coinductive observer on the left-hand side of the equations, as well as a function to be applied to the given stream we are mapping over on the right-hand sides.

\begin{alignat*}{2} & \langle {map~f~s} |\!| {\mathrm{head} \, \beta} \rangle &&= \langle {f ~ (\mathrm{head} \, s)} |\!| {\beta} \rangle \\ & \langle {map~f~s} |\!| {\mathrm{tail} \, \, \alpha'} \rangle &&= \langle {map~f~(\mathrm{tail} \, s)} |\!| {\alpha'} \rangle\end{alignat*}

Notice how, in the following proof, we can make use of observational equivalence in order to reason about head and tail applied as a function to iterate.

Example Theorem 2.4. For all functions f of type $A \to B$ , values x of type A, and observers $\alpha$ of type $\mathrm{Stream} \, A$ , $ \langle {map~f~(iterate~f~x)} |\!| {\alpha} \rangle = \langle {iterate~f~(f~x)} |\!| {\alpha} \rangle .$

Proof By structural coinduction on the observer $\alpha$ (leaving the value x generic):

  • $\alpha = \mathrm{head} \, \beta$ .

  • $\alpha = \mathrm{tail} \, \, \alpha'$ . Assume the coinductive hypothesis

    $$ \langle {map~f~(iterate~f~x)} |\!| {\alpha'} \rangle = \langle {iterate~f~(f~x)} |\!| {\alpha'} \rangle$$
    for all values x of type A.

2.4 Mutual coinduction

Given a stream s, we can define mutually corecursive functions taking the elements of s at even and odd positions as so:

\begin{align*}\begin{aligned} \langle {evens~s} |\!| {\mathrm{head} \, \beta} \rangle &= \langle {s} |\!| {\mathrm{head} \, \beta} \rangle \\ \langle {evens~s} |\!| {\mathrm{tail} \, \, \alpha'} \rangle &= \langle {odds~(\mathrm{tail} \, s)} |\!| {\alpha'} \rangle\end{aligned}&&\begin{aligned} \langle {odds~s} |\!| {\mathrm{head} \, \beta} \rangle &= \langle {s} |\!| {\mathrm{tail} \, (\mathrm{head} \, \beta)} \rangle \\ \langle {odds~s} |\!| {\mathrm{tail} \, \, \alpha'} \rangle &= \langle {evens~(\mathrm{tail} \, s)} |\!| {\mathrm{tail} \, \, \alpha'} \rangle\end{aligned}\end{align*}

By observational equivalence and the definitions of odds and evens, we have:

(2.4) \begin{align} odds~s & = evens~ (\mathrm{tail}~ s)\end{align}
(2.5) \begin{align}\\ \mathrm{tail}(evens~s)) & = odds~(\mathrm{tail} \, ~ s) \end{align}

Merging two streams is defined as

\begin{align*} \langle {merge~s_1~s_2} |\!| {\mathrm{head} \, \beta} \rangle &= \langle {s_1} |\!| {\mathrm{head} \, \beta} \rangle \\ \langle {merge~s_1~s_2} |\!| {\mathrm{tail} \, (\mathrm{head} \, \beta)} \rangle &= \langle {s_2} |\!| {\mathrm{head} \, \beta} \rangle \\ \langle {merge~s_1~s_2} |\!| {\mathrm{tail} \, (\mathrm{tail} \, \, \alpha')} \rangle &= \langle {merge~(\mathrm{tail} \, s_1)~(\mathrm{tail} \, s_2)} |\!| {\alpha'} \rangle\end{align*}

As an application of observational equivalence, we have

(2.6) \begin{align}\mathrm{tail} \, (\mathrm{tail} (merge~s_1~s_2)) = merge (\mathrm{tail} \, s_1)(\mathrm{tail} \, s_2) \end{align}

Example Theorem 2.5. For all values $s_1$ and $s_2$ and observers $\alpha$ of type $\mathrm{Stream} \, A$ , $ \langle {evens~(merge~s_1~s_2)} |\!| {\alpha} \rangle = \langle {s_1} |\!| {\alpha} \rangle $ and $ \langle {odds~(merge~s_1~s_2)} |\!| {\alpha} \rangle = \langle {s_2} |\!| {\alpha} \rangle . $

Proof Both equalities can be proved at the same time by structural coinduction on $\alpha$ (leaving $s_1$ and $s_2$ generic):

  • $\alpha = \mathrm{head} \, \beta$ .

  • $\alpha = \mathrm{tail} \, \, \alpha'$ . Assume the coinductive hypotheses

    (2.7) \begin{align} \langle {evens~(merge~s_1~s_2)} |\!| {\alpha'} \rangle = \langle {s_1} |\!| {\alpha'} \rangle \end{align}
    (2.8) \begin{align} \\ \langle {odds~(merge~s_1~s_2)} |\!| {\alpha'} \rangle = \langle {s_2} |\!| {\alpha'} \rangle \end{align}
    for all values $s_1$ and $s_2$ of type $\mathrm{Stream} \, A$ .

2.5 Strong coinduction

Let us try to prove that the property $ \langle {merge~(evens~s)~(odds~s)} |\!| {\alpha} \rangle = \langle {s} |\!| {\alpha} \rangle $ holds for all values s and observers $\alpha$ of type $\mathrm{Stream} \, A$ . We will show the complete proof shortly. For now, we will focus on the problematic step. We do a proof by conduction on $\alpha$ . We can easily prove the property if $\alpha = \mathrm{head}(\beta)$ . If $\alpha = \mathrm{tail}(\beta)$ , then we proceed by case analysis on $\beta$ . If $\beta = \mathrm{head} \, (\beta')$ the proof goes through without any issues. If $\beta = \mathrm{tail} \, (\beta')$ , we need to prove $ \langle {merge~(evens~s)~(odds~s)} |\!| {\mathrm{tail} \, (\mathrm{tail} \, (\beta'))} \rangle = \langle {s} |\!| {\mathrm{tail} \, (\mathrm{tail}( \beta'))} \rangle$ and note that the coinductive hypothesis is

(2.9) \begin{align} \langle {merge~(evens~s)~(odds~s)} |\!| {\beta} \rangle = \langle {s} |\!| {\beta} \rangle \end{align}

for a generic s. We then have

At this point, we would like to apply the coinductive hypothesis 2.9, but it is fixed to $\beta$ and does not hold on $\beta'$ . What we need instead is a strong version of coinduction. This is not surprising since the same issue comes up with induction. If $\alpha = \mathrm{tail} \, (\mathrm{tail} \, \beta')$ , we assume the property holds not just for the immediate subcontext $\mathrm{tail} \, \, \beta'$ , but also for $\beta'$ , too. We use this strengthened reasoning principle to break the following coinductive proof into more specific sub-cases.

Example Theorem 2.6. For all values s and observers $\alpha$ of type $\mathrm{Stream} \, A$ , $ \langle {merge~(evens~s)~(odds~s)} |\!| {\alpha} \rangle = \langle {s} |\!| {\alpha} \rangle $

Proof By strong coinduction on the structure of the observer $\alpha$ (where we leave the stream value s generic):

  • $\alpha = \mathrm{head} \, \beta$ .

  • $\alpha = \mathrm{tail}(\mathrm{head} \, \beta')$ .

  • $\beta = \mathrm{tail}(\mathrm{tail} \, \, \beta')$ . Assume the coinductive hypothesis (CH)

    (2.10) \begin{align} \langle {merge~(evens~s)~(odds~s)} |\!| {\beta'} \rangle = \langle {s} |\!| {\beta'} \rangle \end{align}
    for all values s of type $\mathrm{Stream} \, A$ .

3 Intensional versus extensional equality with (co)inductive types

Before we lay out our formal rules of (co)inductive reasoning about the behavior of programs, we need to specify the language in which those programs are written. For the sake of illustration, we will use the abstract machine language with both recursion and corecursion defined in Downen & Ariola (Reference Downen and Ariola2023)—which is an extension of Curien & Herbelin (Reference Curien and Herbelin2000) with primitive types for inductive numbers and coinductive streams, with full primitive (co)recursion, not just (co)iteration—because the symmetry of its syntax lets us express the duality of induction and coinduction most clearly. However, note that the important (co)inductive reasoning principles below can be applied to other languages as well—provided the language can label points in the flow of control.

The syntax and operational semantics of our (co)recursive abstract machine language are given in Figure 1. Computation occurs as a reduction of machine commands (c), which are made up of a term (v) interacting with a coterm (e). Intuitively, terms correspond to the expressions of a $\lambda$ -calculus-like language and coterms correspond to continuations (or evaluation contexts) that arise during computation. Of note, the machine is uniform in the sense that it can express either call-by-value or call-by-name evaluation with the same form of operational rules. The only difference between the two evaluation strategies is in the definitions of values (V), which denote the terms that may be bound to and substituted for variables, and covalues (E) which correspond to evaluation contexts. For example, the right-hand sides of the $\beta_{\mathrm{succ}}$ and $\beta_{\mathrm{tail}}$ rules seem to have two possible reductions via $\mu$ or ${\tilde{\mu}}$ , but the definition of V and E will only permit one of them. In call-by-name, a ${\tilde{\mu}}$ -abstraction is never a covalue, so the next step will be a ${\tilde{\mu}}$ -reduction that computes the coterm side first; for $\beta_{\mathrm{succ}}$ calculating the updated covalue for the with-clause of recursion, and for $\beta_{\mathrm{tail}}$ this means immediately unrolling the corecursive loop again. In call-by-value, a $\mu$ -abstraction is never a value, so the next step will be a $\mu$ -reduction that computes the term side first; for $\beta_{\mathrm{succ}}$ this means immediately unrolling the recursive loop again, and for $\beta_{\mathrm{tail}}$ this means calculating the updated value for the with-clause of corecursion.

Fig. 1. Syntax and semantics of the uniform, (co)recursive abstract machine.

The type system is given in Figure 2. We use FV to denote the set of free variables an expression refers to, e.g., $FV(v : A)$ for the free variables in a term v, $FV(e \div A)$ for the free variables in a co-term e, and FV(c) for the free variables in a command. $AV(\Gamma)$ denotes the set of variables that have been assigned a type by $\Gamma$ , e.g., $AV(x_i : A_i,\dots, \alpha_j \div B_j,\dots) = \{x_i,\dots,\alpha_j,\dots\}$ . Besides the ordinary function type $A \to B$ , the (co)recursive abstract machine includes the types Nat of natural numbers, serving as a canonical example of an inductive type, and $\mathrm{Stream} \, A$ of infinite streams containing A elements, serving as a canonical example of a coinductive type. Note that in the style of the sequent calculus (Curien & Herbelin, Reference Curien and Herbelin2000; Downen & Ariola, Reference Downen and Ariola2018), the constructs of these types are divided between the term and coterm sides of a command. For example, we include the usual abstraction $\lambda x. v$ from the $\lambda$ -calculus, but instead of application we build a call stack $V \cdot E$ which accepts a function of type $A \to B$ when V produces an A and E consumes a B. Similarly for numbers, we include the constructors zero and succ for building values of Nat, which are consumed by a rec continuation corresponding to the System T’s recursor (Gödel, Reference Gödel1980). Symmetrically for streams, we instead have the destructors head and tail for building covalues of $\mathrm{Stream} \, A$ , which project out of a corec value that corecursively builds a stream, on-demand, one piece at a time. To check the types of these (co)terms and validity of commands, we use a typing environment $\Gamma$ that describes both the variables x and covariables $\alpha$ in scope that can be referenced, along with their types, written $x : A$ and $\alpha \div A$ , respectively. These variables are considered free in the underlying (co)term and command expressions, and they are assigned a type by the environment $\Gamma$ . Notice that we make the simplifying assumption throughout this paper that environments $\Gamma$ never assign types to the same (co)variable x or $\alpha$ more than once (i.e., every x or $\alpha$ bound by a $\Gamma$ are distinct), ruling out cases like $x:\mathrm{Nat}, y:\mathrm{Nat}, x:\mathrm{Nat}\to\mathrm{Nat}$ . Since this abstract machine language does not have an application like the $\lambda$ -calculus, how can it express basic compositions like f(g(x))? These sorts of terms can be encoded thanks to the $\mu$ - and ${\tilde{\mu}}$ -abstractions in the machine language. For example, f(g(x)) can be written

\begin{align*}\mu{\alpha}.\langle {\mu{\beta}.\langle {g} |\!| {x \cdot \beta} \rangle} |\!| {{\tilde{\mu}}{z}.\langle {f} |\!| {z \cdot \alpha} \rangle} \rangle\end{align*}

where the outer $\mu$ assigns the name $\alpha$ to the surrounding calling context of f, and ${\tilde{\mu}}$ gives a name to the computation g(x) and invokes f with that name and the return point $\alpha$ . More generally, we can use the syntactic sugar given in Figure 3 as macro-definitions for all the usual expressions of $\lambda$ -calculi, including applications $(v~w)$ , using head and tail directly as projections, let-bindings, and the recursor as a term. Notice how each of these macro-definitions uses $\mu$ to name the current evaluation context $\alpha$ , in order to build a larger continuation. But what happens if we want to use a non-value term v in a context like $v \cdot e$ or $\mathrm{succ} \, v$ , which is not allowed by the syntax of Figure 1? Again, we can use $\mu$ and ${\tilde{\mu}}$ to give a name to non-(co)value expressions and follow the syntactic restrictions of the abstract machine. These additional macro-expansions are also shown in Figure 3.

Fig. 2. Type system of the uniform, (co)recursive abstract machine.

Fig. 3. Syntactic sugar in the abstract machine language.

Example 3.1. As pointed out above, the syntactic sugar might help in better grasping the (co)recursors; we present next how to define the plus and iterate functions seen in the previous section. The reader might consult (Downen & Ariola, Reference Downen and Ariola2023) for a detailed explanation of their use.

The plus function is defined as

\begin{align*}\lambda x. \lambda z. \mu{\alpha}. \langle {x} |\!| {} \rangle{ \mathrm{\textbf{rec}} \{ \mathrm{zero} \to z \mid \mathrm{succ} \, \_ \to y. \mathrm{succ} \, y \} \mathrm{\textbf{with}} \, \alpha }\end{align*}

Running $\langle {plus~ 2~ 2} |\!| {\beta} \rangle$ (with $1 = \mathrm{succ} \, \mathrm{zero}$ and $2 = \mathrm{succ} \, 1$ ) in call-by-value becomes:

Notice how at each recursive step the continuation gets updated: first $\beta$ , then ${\tilde{\mu}}{y}. \langle {\mathrm{succ} \, y} |\!| {\beta} \rangle$ , and finally ${\tilde{\mu}}{z}. \langle {\mathrm{succ} \, z} |\!| {{\tilde{\mu}}{y}. \langle {\mathrm{succ} \, y} |\!| {\beta} \rangle} \rangle$ .

The iterate function is expressed as

If add2 stands for the function $\lambda x . \mu{\alpha}. \langle {\mathrm{succ} \, \mathrm{succ} \, x} |\!| {\alpha} \rangle$ , then the even natural numbers can be represented as $\mu{\alpha}. \langle {iterate} |\!| {add2 \cdot {\mathrm{zero} \cdot \alpha}} \rangle$ , and the third element of this stream is computed in call-by-value as follows (where $ iter2 = \{ \mathrm{head} \, \alpha \to \alpha \mid \mathrm{tail} \, \, \beta \to \gamma. {\tilde{\mu}}{x}. \langle {add2} |\!| {x \cdot \gamma} \rangle \}$ ):

Notice how at each co-recursive step it is not the continuation that gets updated but the internal seed: zero, 2, and 4.

3.1 Intensional equational theory

The machine’s operational semantics in Figure 1 only allows us to apply the reduction steps ( $c \mapsto c'$ ) to the top-level of the given command, and only ever forward: the multi-step reduction $c_1 \mathrel{\mapsto\!\!\!\!\to} c_n$ combines several individual steps together, $c_1 \mapsto c_2 \mapsto c_3 \mapsto \dots \mapsto c_n$ , but requires that all the arrows are pointed in the same direction. These two restrictions make the operational semantics deterministic: it always marches forward in one path because there is never more than one choice of step to take.

In contrast, an equational theory—for reasoning about when two programs, or fragments of programs, have the same observable result—gives us more freedom to relate programs that appear to have the same behavior. One of the key allowances is that the reduction steps can be applied both forwards and backwards; i.e., equality is symmetric. The other is that we can apply the reduction steps in any context (no matter how deep within the given expression); i.e., equality is congruent. Such an equational theory for the (co)recursive abstract machine is given in in Figure 4.Footnote 1 All judgments have the form $\Gamma \mid \Delta \vdash \Phi$ , where $\Gamma$ contains the (unordered) type assignments to free (co)variables, $\Delta$ contains the (unordered) hypothesized properties involving those free (co)variables, and $\Phi$ is the property being proved. The main properties are the base equalities for commands ( $c = c' $ ) and (co)terms of some type A ( $v = v' : A$ and $e = e' \div A$ ). For now, the hypotheses $\Delta$ do not yet interact with the inference rules—they will soon play a crucial role in Section 3.2 —so as shorthand, we will write $\Gamma \vdash \Phi$ instead of $\Gamma \mid \bullet \vdash \Phi$ in examples.

Fig. 4. Intensional equational theory of computation.

This equational theory is the smallest equivalence relation that includes the operational semantics, letting us reason about which programs are equal up to execution. As a result, it is more discriminating than a purely external observer, and can distinguish between two definitions with the same input–output behavior depending on the way they are defined. For this reason, this kind of equational theory is sometimes called intensional (because it lacks extensionality or any nontrivial mathematical reasoning) or definitional (because it is based on the definition of code).

For example, it is easy to show by reduction that $ \langle {plus} |\!| {\mathrm{zero} \cdot x \cdot \alpha} \rangle \mathrel{\mapsto\!\!\!\!\to} \langle {x} |\!| {\alpha} \rangle$ because plus was defined by recursion on its first argument (see Section 2), and therefore we have the following equality:

$$ \alpha \div \mathrm{Nat}, x : \mathrm{Nat} \vdash \langle {plus} |\!| {\mathrm{zero} \cdot x \cdot \alpha} \rangle = \langle {x} |\!| {\alpha} \rangle$$

However, $\langle {plus} |\!| {x \cdot \mathrm{zero} \cdot \alpha} \rangle$ does not reduce at all, even though it is nonetheless equivalent to x in any context. Thus,

$$ \alpha \div \mathrm{Nat}, x: \mathrm{Nat} \vdash \langle {plus} |\!| {x \cdot \mathrm{zero} \cdot \alpha} \rangle = \langle {x} |\!| {\alpha} \rangle $$

is not provable. Likewise, $iterate~(\lambda x.x)~x$ is observationally equivalent to the stream $always~x$ , but the intensional theory considers them different because their definitions are too different.

The bulk of the rules are dedicated to congruence: the allowance that equalities may be applied in any context. Though there are many different congruence rules to spell out (accounting for the many different contexts that may appear), they thankfully reflect exactly the same structure as the type system. Each typing rule from Figure 2 for checking a single command, term, or coterm has a corresponding rule of the same name in Figure 4 which just compares two such expressions hereditarily.

Note that reflexivity of well-typed commands, terms, and coterms is not included as an inference rule in Figure 4 because it holds by performing an induction on the typing derivation, and applying the appropriate congruence rules. Still, in the following, we will sometimes refer to these reflexivity rules:

where the vertical dots indicate the rule is derivable.

The Red rule states that any reduction step of the operational semantics can be added onto another equality. Together with reflexivity, we can say that any well-typed command c is equal to its next step, $c \mapsto c'$ :

and we will simply write

Whereas a (unary) type system interprets the free variable $x : A$ (and analogously, $\alpha \div A$ ) as one unknown value of type A, a (binary) equational theory interprets the free $x : A$ as two unknown values which are equal at type A. More concretely, we can understand the meaning of free (co)variables in terms of the following notion that substitution commutes with equality—substitution of equals into equals are equals:

(3.1)

With the rules we already have, we can use reduction to derive substitution of equal values for variables like so:

And the derivation of the dual substitution of covalues for covariables follows analogously to the above, using the dual $\mu$ activation and operational steps.

Example 3.2. Consider this basic application of corec which just (corecursively) forwards all observations onto some underlying stream xs:

\begin{align*} parrot~xs &:= \mathrm{\textbf{corec}} \{ \mathrm{head} \, \alpha \to \mathrm{head} \, \alpha \mid \mathrm{tail} \, \, \alpha \to \gamma.\mathrm{tail} \, \, \gamma \} \mathrm{\textbf{with}} xs\end{align*}

Intuitively, $parrot~xs$ produces a stream that produces exactly the same elements as xs. We can understand parrot at a higher level in terms of these equations that show how it reacts to head and tail observations:

\begin{align*} \langle {parrot~xs} |\!| {\mathrm{head} \, \beta} \rangle &= \langle {xs} |\!| {\mathrm{head} \, \beta} \rangle & \langle {parrot~xs} |\!| {\mathrm{tail} \, \, \alpha} \rangle &= \langle {parrot~(\mathrm{tail} \, xs)} |\!| {\alpha} \rangle\end{align*}

Both of these equalities are derivable from the intensional equational theory by just applying the operational rules (and expanding any syntactic sugar from Figure 3 as necessary). The head case follows directly from the $\beta_{\mathrm{head}}$ step:

\begin{align*} \langle {parrot~xs} |\!| {\mathrm{head} \, \beta} \rangle &\mapsto \langle {xs} |\!| {\mathrm{head} \, \beta} \rangle &(\beta_{\mathrm{head}})\end{align*}

The tail case is slightly more involved because its reduction depends on whether the command is evaluated according to the call-by-name or call-by-value. In the call-by-name operational semantics, we have the forward reduction (where according to Figure 3, $\mathrm{tail} \, xs$ corresponds to $\mu{\gamma}.\langle {xs} |\!| {\mathrm{tail} \, \gamma} \rangle$ ):

In the call-by-value operational semantics, we have this conversion instead:

3.2 Extensional program logic

It’s often unsatisfactory to only consider two expressions equal when they reduce to some common reduct; that misses out on far too many equalities. Instead, we will enhance the intensional equational theory with a program logic that is extensional, in the sense that it considers expressions equal when they appear to be the same from the outside. This means we will have to add additional rules for saying when two terms (or two coterms) are equal because they cannot be distinguished by some observer. But which observer is that? The other side of the command! Terms are observed by coterms, and vice versa. Therefore, the idea of extensionality in the abstract machine comes down to the idea that (co)terms of any type are equal if and only if they always form equal computations when interacting with equal counterparts of that type. The extensional program logic is given in Figure 5. The distinctive feature of this theory is that it is applicable to both call-by-name and call-by-value. That is why some properties needed to be restricted.

Fig. 5. Extensional program logic.

Propositions - $\Phi$ : We enrich the language of properties that we are proving by internalizing the implicit “for all” generalization made by the free $x : A$ and $\alpha \div A$ in the environment in terms of an explicit $\forall$ quantifier in the syntax of propositions. In addition to the same three cases of equality as before, we now have two dual forms of universal quantification as properties: $\forall x{:}A. \Phi$ generalizes the property $\Phi$ over all choices of equal values of type A for x, and $\forall \alpha{\div}A. \Phi$ generalizes $\Phi$ over all choices of equal covalues for $\alpha$ of type A. The rules governing these two $\forall$ properties are given in Figure 5 as well.

Universal quantifiers can be introduced by IntroL and IntroR, which state that $\forall$ internalizes a free (co)variable in the environment, and eliminated by ElimL and ElimR. The notation $\Phi[{{V}/{x} = {V'}/{x}}]$ (and likewise $\Phi[{{E}/{\alpha} = {E'}/{\alpha}}]$ ) means to perform the substitution $[{{V}/{x}}]$ on the left-hand side of the equation in $\Phi$ and $[{{V'}/{x}}]$ on the right-hand side. For example, the base cases of this substitution are when $\Phi$ is just an equality; for a command equality, this looks like:

\begin{align*} (c = c' )[{{V}/{x} = {V'}/{x}}] &:= (c[{{V}/{x}}]) = (c'[{{V'}/{x}}]) \\ (c = c' )[{{E}/{\alpha} = {E'}/{\alpha}}] &:= (c[{{E}/{\alpha}}]) = (c'[{{E'}/{\alpha}}])\end{align*}

ElimL and ElimR generalize the substitution rules previously derived in Equation (3.1) to instantiate the quantified (co)variables by any equal (co)values of the appropriate type:

(3.2)

which are derived from the Intro and Elim rules like so:

A special case of Elim is to reverse the Intro rules:

(3.3)

These can be derived from the Elim and Var rules by weakening the premise (adding additional (co)variable type assignments which are never used).Footnote 2 $Elim \,L_x$ is derived like so:

Similar to the quantifiers, we also have plain propositional implication, written $\Phi' \Rightarrow \Phi$ , for stating that the truth of $\Phi'$ implies the truth of $\Phi$ . The rules governing $\Phi' \Rightarrow \Phi$ are IntroH, which introduces an implication that internalizes a hypothesis in the environment, and Lemm, which lets us eliminate an implication by proving its hypothesis in the style of instantiating a lemma. While propositional implication is not strictly necessary for the kinds of simple equalities we have considered thus far, their addition makes it possible for us to explore some more complex forms of reasoning that can all be derived from the same rules of structural (co)induction. For the same reason, we also introduce propositional conjunction, written $\Phi_1 \wedge \Phi_2$ , to describe the compositionality of (co)induction. Propositional conjunction is introduced and eliminated with the familiar ConjI and ConjE rules.

Strict and productive propositions - $\Psi(x)$ and $\Psi(\alpha)$ : In some rules, we need to impose some constraints on the use of (co)variables. This is because we aim for the program logic to be applicable in both the call-by-value and call-by-name setting. This requires careful attention to avoid equating a value with a non-value, as well as ensuring the same distinction for co-values. These restrictions are defined syntactically and approximate the two dual notions of control flow and data flow:

  • A property $\Psi(x)$ is strict on x when it uses x directly with some covalue on both sides of its underlying equality, with the base case of a strict property on x being $\langle {x} |\!| {E} \rangle = \langle {x} |\!| {E'} \rangle $ where x is not free in E or E’. Intuitively, $\Psi(x)$ is some property which observes x exactly once with a covalue, since all covalues are strict, forcing their input to be computed first before they act.

  • Dually, a property $\Psi(\alpha)$ is productive on $\alpha$ when it immediately returns a value to $\alpha$ on both side of its underlying equality, with the base case of a productive property on $\alpha$ being $\langle {V} |\!| {\alpha} \rangle = \langle {V'} |\!| {\alpha} \rangle $ where $\alpha$ is not free in V or V’. Intuitively, $\Psi(\alpha)$ is some property which produces exactly one value to $\alpha$ .

The $\sigma\mu$ and $\sigma\tilde\mu$ rules implement the idea of observational equality, we would like formal inference rules that embody these two relationships between different forms of equality.

  • $\Gamma \mid \Delta \vdash v = v' : A$ if and only if $\Gamma \mid \Delta \vdash \langle {v} |\!| {e} \rangle = \langle {v'} |\!| {e'} \rangle $ for all $\Gamma \mid \Delta \vdash e = e' \div A$ .

  • $\Gamma \mid \Delta \vdash e = e' \div A$ if and only if $\Gamma \mid \Delta \vdash \langle {v} |\!| {e} \rangle = \langle {v'} |\!| {e'} \rangle $ for all $\Gamma \mid \Delta \vdash v = v' : A$ .

Thankfully, the “only if” direction of both of these can be derived by the Cut congruence rule already present in the intensional equational theory (Figure 4):

If we already know two terms $\Gamma \mid \Delta \vdash v = v' : A$ are equal, then for any other equal coterms $\Gamma \mid \Delta \vdash e = e' \div A$ , Cut lets us conclude that their pointwise combination gives equal commands $\Gamma \mid \Delta \vdash \langle {v} |\!| {e} \rangle = \langle {v'} |\!| {e'} \rangle $ . Dually, starting with two equal coterms, Cut lets us combine them with any equal terms to give equal commands. Whereas the “if” direction is implemented by the $\sigma\mu$ and $\sigma\tilde\mu$ rules, which establish a logical equivalence between equality of commands versus equality of (co)terms. These rules say that any two terms (dually coterms) are equal when they form equal commands when interacting with a generic covariable (dually variable). The $\sigma$ rules allow the derivation of the extensional $\eta$ rules for $\mu$ and ${\tilde{\mu}}$ (Herbelin, Reference Herbelin2005):

They can be derived from the $\sigma\mu$ , $\sigma{\tilde{\mu}}$ inference rules and $\mu{\tilde{\mu}}$ reductions. $\eta_\mu$ is derived as

Analogously for $\eta_{\tilde{\mu}}$ :

Note that the $\mu$ and ${\tilde{\mu}}$ reduction apply to both call-by-name and call-by-value since (co)-variables are considered values in these strategies. This means that the $\eta_\mu$ and $\eta_{\tilde{\mu}}$ axioms are sound in both semantics.

The $\omega{\to}$ rule expresses a form of extensionality for functions in terms of call stacks. It states that the only canonical covalue of type $A \to B$ has the form $V \cdot E$ , and testing a property on a generic call stack $x \cdot \beta$ is sufficient to generalize that property over all $\alpha$ of type $A \to B$ . The rule allows the derivation of the following $\eta$ axiom for functions (Curien & Herbelin, Reference Curien and Herbelin2000):

which is equivalent to the familiar $\eta$ law of the $\lambda$ -calculus, as it can be seen by macro-expanding the syntactic sugar for application according to Fig. 3: $\lambda{x}. (V~x) = \lambda{x}. \mu{\alpha}.\langle {V} |\!| {x \cdot \alpha} \rangle =_{\eta_\to} V$ . The derivation is as follows:

Second from the bottom, $\omega{\to}$ can be applied to $\gamma \div A \to B$ because the equation $\langle {\lambda{x}. \mu{\alpha}.\langle {V} |\!| {x \cdot \alpha} \rangle} |\!| {\gamma} \rangle = \langle {V} |\!| {\gamma} \rangle$ is productive on $\gamma$ ; both sides of the equation immediately produce a syntactic value to $\gamma$ .

Remark 3.3. If we relax the restriction on the $\omega{\to}$ rule and instead allow it for any property $\Phi$ (which we’ll refer to as $\sigma{\to}$ ), then it would be possible to conclude that

The problem is that $\langle {v} |\!| {\gamma} \rangle$ may not produce a single value to $\gamma$ v might throw $\gamma$ away, as shown in the example below.

\begin{align*} \beta \div \mathrm{Nat} , \gamma \div A \to B \vdash \langle { \lambda{x}. \mu{\alpha}.\langle {\mu{\delta}.\langle {\mathrm{zero}} |\!| {\beta} \rangle} |\!| {x \cdot \alpha} \rangle} |\!| {\gamma} \rangle &= \langle {\mu{\delta}.\langle {\mathrm{zero}} |\!| {\beta} \rangle} |\!| {\gamma} \rangle : A \to B\end{align*}

This equality is fine under call-by-name evaluation but is inconsistent under call-by-value, wherein not all covalues of function type have the form $\alpha$ or $x \cdot \alpha$ . For example, the coterm ${\tilde{\mu}}{\rule{1ex}{0.5pt}}.\langle {\mathrm{succ}\, \mathrm{zero}} |\!| {\alpha} \rangle$ lets us observe the difference call-by-value evaluation makes between the two sides of the equation. On the left, we have

whereas on the right, we have

Therefore, we would be able to derive $\mathrm{zero} = \mathrm{succ} \, \mathrm{zero} : \mathrm{Nat}$ using call-by-value evaluation, making the theory inconsistent.

This inconsistency in call-by-value should not be surprising. It is well known that unrestricted $\eta$ equivalence is unsound in the call-by-value $\lambda$ -calculus with general recursion or side effects. The usual counter-example is that the term $\Omega = (\lambda{x}. x) ~ (\lambda{x}. x)$ is observationally different from a $\lambda$ -abstraction, but the $\eta$ law requires $\Omega = \lambda{x}. (\Omega ~ x)$ . Instead, the sound version of the call-by-value $\eta$ law only applies to values: $\lambda{x}. (V ~ x) = V$ .

The induction rule $\omega{\mathrm{Nat}}$ summarizes the following deduction for proving a property $\Psi$ over any number x using an infinite number of premises:

This deduction is justified from the reasoning that zero, $\mathrm{succ}\, \mathrm{zero}$ , $\mathrm{succ}(\mathrm{succ}\, \mathrm{zero})$ —are all the canonical values of Nat; testing $\Psi$ on all of them is sufficient to generalize $\Psi$ over any x of type Nat. $\omega{\mathrm{Nat}}$ uses the usual structure of primitive induction on the numbers to summarize this kind of argument in a finite form and can be understood as an inference rule representing the usual axiom of induction:

\begin{align*} & \Psi(\mathrm{zero}) \Rightarrow (\forall x{:}{\mathrm{Nat}}. \Psi(x) {\Rightarrow} \Psi(\mathrm{succ} \, x)) \Rightarrow (\forall x{:}{\mathrm{Nat}}. \Psi(x)) & (\omega{\mathrm{Nat}})\end{align*}

Rather than listing a separate proof for each number, just start with a proof for zero specifically, and give a transformation from a proof of $\Psi$ on an arbitrary number to the next proof of $\Psi$ for the successor of that same number. Because this second step is a transformation, we first assume that the property $\Psi$ is true on a generic $x : \mathrm{Nat}$ by placing $\Psi$ in the environment $\Gamma$ of other assumptions, with the intention that the assumed $\Psi$ in $\Gamma$ can be used to prove $\Psi$ with x replaced by $\mathrm{succ} \, x$ .

As an example of the application of induction, we would like to prove the following deep extensionality axiom for “trivial” uses of recursion (where a $\rule{1ex}{0.5pt}$ stands for an unused variable):

The above is saying that any generic observer $\alpha$ cannot tell the difference if a natural number is first broken down and rebuilt from scratch from the base case (zero) up. Let us use the following shorthand:

and proceed as follows:

We can apply the induction rule $\omega{\mathrm{Nat}}$ here because the property $\langle {x} |\!| { noop~\alpha } \rangle = \langle {x} |\!| {\alpha} \rangle$ is strict on x. This is evident because both sides of the equation observe x with a covalue ( $\mathrm{\textbf{rec}}\dots\mathrm{\textbf{with}} \, \alpha$ on the left and $\alpha$ on the right) in both call-by-name and -value.

We can just evaluate the left-hand-side to prove the base case:

\begin{align*}\langle {\mathrm{zero}} |\!| {noop~\alpha} \rangle = \langle {\mathrm{zero}} |\!| {\alpha} \rangle\end{align*}

What remains is to show $\langle {\mathrm{succ} \, x} |\!| {noop~\alpha} \rangle = \langle {\mathrm{succ} \, x} |\!| {\alpha} \rangle$ from the inductive hypothesis (IH) $\langle {x} |\!| {noop~\alpha} \rangle = \langle {x} |\!| {\alpha} \rangle$ . We would like to put together the following equality:

The problem is that the induction hypothesis holds only for $\alpha$ , but we now need to apply it in a different context. This requires a generalization of the context:

Note that the $\omega\mathrm{Nat}$ rule can still be applied since quantifying over a strict property gives another strict property. Now we can instantiate the inductive hypothesis

$$\forall \alpha \div \mathrm{Nat} . \langle {x} |\!| { noop~\alpha } \rangle = \langle {x} |\!| {\alpha} \rangle$$

to the new context $\beta$ : $ \langle {x} |\!| { noop~\beta } \rangle = \langle {x} |\!| {\beta} \rangle$ .

The need to generalize over the context does not show up when one does inductive proofs in $\lambda$ -calculus since the context is left implicit. In fact, let’s go back to the proof of Example Theorem 2.1. Notice how we applied the inductive hypothesis not at the top level, which we can represent as $\Box$ , but in the bigger context $\mathrm{succ} \, \Box$ . The inductive hypothesis should be better expressed as $\forall C[\Box], C[plus~x'~{\mathrm{zero}}]=C[x']$ .

Analogously, we can also prove the following “shallow” extensionality property $\eta{\mathrm{Nat}}$ that just look at the outermost structure of a numeric value or a stream projection:

Remark 3.4 Note that the property $\forall \alpha. \langle {x} |\!| { noop~\alpha} \rangle = \langle {x} |\!| {\alpha} \rangle$ is strict in x, since the recursor is a co-value in both call-by-value and call-by-name. If we remove that restriction and apply induction on an arbitrary proposition $\Phi$ , we can derive inconsistent equations under call-by-name because it can equate any coterm $e \div \mathrm{Nat}$ with a rec covalue, whether or not e itself is a covalue. For example, we would be able to prove this property:

The call-by-name version of the derived SubstL rule, see Equations (3.1) and (3.2), allows for the call-by-name value $\mu{\rule{1ex}{0.5pt}}.\langle {\mathrm{succ}\, \mathrm{zero}} |\!| {\alpha} \rangle$ to be substituted for x in this equation, leading to the inconsistent equality $ \alpha {\div} \mathrm{Nat} \vdash \langle {\mathrm{succ}\, \mathrm{zero}} |\!| {\alpha} \rangle{=} \langle {\mathrm{zero}} |\!| {\alpha} \rangle ~ .$

The coinduction rule $\omega{\mathrm{Stream}}$ specifies a form of structural coinduction for streams. It works in exactly the same way as structural induction for numbers—just with the roles of values and covalues reversed. The $\omega{\mathrm{Stream}}$ rule summarizes this deduction for proving a property $\Psi$ over any stream projection $\alpha$ using an infinite number of premises:

This deduction is justified by the reasoning that the listed projections— $\mathrm{head} \, \beta$ , $\mathrm{tail}(\mathrm{head} \, \beta)$ , $\mathrm{tail}(\mathrm{tail}(\mathrm{head} \, \beta))$ —cover all the canonical covalues of $\mathrm{Stream} \, A$ ; testing $\Psi$ on all of them is sufficient to generalize $\Psi$ over any generic $\alpha$ of type $\mathrm{Stream} \, A$ . $\omega{\mathrm{Stream}}$ summarizes this kind of argument in a finite form, avoiding the list of separate proofs for each of the infinitely possible projections. Whereas $\omega{\mathrm{Nat}}$ corresponds to the usual induction axiom for the natural numbers, the $\omega{\mathrm{Stream}}$ rule corresponds to the dual form of the coinduction axiom for proving a property holds for all observations of infinite streams in both call-by-name and call-by-value:

Dual to induction on the numbers, we start with a proof for $\mathrm{head} \, \beta$ specifically, and give a transformation from a proof of $\Psi$ on an arbitrary observation on streams to the next proof of $\Psi$ for the same observation on the tail of the stream. As before, this transformation is represented by assuming $\Psi$ holds for a generic $\alpha \div \mathrm{Stream} \, A$ by listing it in the environment $\Gamma$ , which can then be used to derive a proof of $\Psi$ with $\alpha$ replaced by $\mathrm{tail} \, \, \alpha$ .

As an example of application of co-induction, we would like to prove the following deep extensional property of streams, which is the dual of $\delta_{\mathrm{Nat}}$ :

The above is saying that any generic stream xs gives the same response when its projections are broken down and rebuilt from scratch from the base case ( $\mathrm{head} \, \alpha$ ) up. We should be able to apply the rules from Figure 5 to prove it.

By using the shorthand $parrot ~xs := \mathrm{\textbf{corec}} \{ \mathrm{head} \, \alpha \to \mathrm{head} \, \alpha \mid \mathrm{tail} \, \, \alpha \to \gamma. \, \mathrm{tail} \, \, \gamma \} \mathrm{\textbf{with}} \, xs$ as in Example 3.2, the bottom of the derivation starts like this:

We begin by assuming some generic stream value $xs : \mathrm{Stream} \, A$ is in scope. The first step (from the bottom up) applies $\sigma\mu$ to generalize equality of terms to an equality of commands, by introducing a generic continuation $\alpha \div \mathrm{Stream} \, A$ expecting a stream. From here, we can apply the $\omega{\mathrm{Stream}}$ coinductive rule since we invoke $\alpha$ with a value. We continue with two proof obligations:

  1. 1. Show $\langle {parrot~xs} |\!| {\mathrm{head} \, \beta} \rangle = \langle {xs} |\!| {\mathrm{head} \, \beta} \rangle $ .

  2. 2. Show $\langle {parrot~xs} |\!| {\mathrm{tail} \, \, \alpha} \rangle = \langle {xs} |\!| {\mathrm{tail} \, \, \alpha} \rangle $ follows from the coinductive hypothesis (CIH) $\langle {parrot~xs} |\!| {\alpha} \rangle = \langle {xs} |\!| {\alpha} \rangle $ .

Step 1 follows directly from $\beta_{\mathrm{head}}$ , as shown in Example 3.2. Step 2 proceeds as follows:

\begin{align*} \langle {parrot~xs} |\!| {\mathrm{tail} \, \, \alpha} \rangle &= \langle {parrot~(\mathrm{tail} \, xs)} |\!| {\alpha} \rangle &(\beta_{\mathrm{tail}}\mu{\tilde{\mu}}) \\ &= \langle {\mathrm{tail} \, xs} |\!| {\alpha} \rangle &(CIH?) \\ &= \langle {xs} |\!| {\mathrm{tail} \, \, \alpha} \rangle &(\mu)\end{align*}

The coinductive hypothesis does not apply in the middle step, because it is already fixed for some previously-chosen xs, which is not the same as ( $\mathrm{tail} \, xs$ ) used here. What we need is the ability to generalize the coinductive hypothesis. Rather than introducing a generic stream xs first and then applying coinduction, we should apply coinduction to prove an equality holds for all choices of xs, as shown below:

The base case is as before. For the co-inductive case, we have the following calculation in call-by-value and -name:

Note that the generalization over xs in the coinductive hypothesis is essential for instantiating xs with the bound y newly introduced by $\beta_{\mathrm{tail}}$ reduction.

As we did for Nat, we can also derive the following “shallow” extensionality property that just look at the outermost structure of a stream projection:

Remark 3.5. The coinductive $\omega{\mathrm{Stream}}$ rule is similar in spirit to $\omega{\to}$ : it matches over the possible shapes of a generic covalue $\alpha \div \mathrm{Stream} \, A$ in scope. The problem is that in call-by-value there are more values than the ones we considered. If we relax the restriction of productivity and allow the application of the rule to a generic proposition $\Phi$ , we would then prove:

and yet the call-by-value version of the derived SubstR rule, see Equations (3.1) and (3.2), lets us substitute ${\tilde{\mu}}{\rule{1ex}{0.5pt}}.\langle {\mathrm{succ}\, \mathrm{zero}} |\!| {\alpha} \rangle$ as a covalue for $\beta$ , leading to an inconsistent equality:

3.3 Consistency of the extensional program logic

Ultimately, the program logic is not useful if it derives inconsistent results. One very simplistic version of consistency is that 0 is different from any successor (like 1); and dually, we should also know that a head projection is different from a tail projection.

Definition 3.6 (Consistency). An equational theory or program logic for the (co)recursive abstract machine is consistent iff the following equalities are not derivable:

  • ${}\vdash \mathrm{zero} = \mathrm{succ} \, V : \mathrm{Nat}$ , and

  • ${}\vdash \mathrm{head} \, E = \mathrm{tail} \, E' \div \mathrm{Stream} \, A$ .

As with most systems, equating 0 and 1 collapses the notion of equality. Assuming $\mathrm{zero} = \mathrm{succ}\, \mathrm{zero} : \mathrm{Nat}$ lets us prove that any two terms v and w of type A are equal by abstracting over the output $\alpha \div A$ in this derivation with $\sigma\mu$ :

This forces every $v = w : A$ to hold, which we can use to equate any two commands and any two coterms of the same type, as well. Likewise, equating the head and tail projections leads to the same collapse, due to a similar derivation. Assuming $\mathrm{head} \, \alpha = \mathrm{tail}(\mathrm{head} \, \alpha)$ , we can prove any two coterms e and f of type A are equal by abstracting over the input $x : A$ via $\sigma{\tilde{\mu}}$ in this derivation:

By restricting induction to only apply to strict properties, and restricting coinduction to only productive properties, we get a single extensional program logic (parameterized by the definition of values and covalues) that is consistent in both call-by-value and call-by-name evaluation. See Section 5 for the proof of consistency.

Theorem 3.7. The extensional program logic in Figure 5 is consistent for both the call-by-name and call-by-value semantics.

3.4 When is unrestricted (co)induction sound?

Common folklore says that induction holds only in call-by-value, and thus dually coinduction should hold only in call-by-name. This is reflected in part through the restrictions defining strict versus productive properties in the “universally” sound extensional program logic given in Figure 5. Call-by-value has a more permissive notion of covalue (any coterm is a call-by-value covalue), so the induction principle $\omega{\mathrm{Nat}}$ applies to more properties in the call-by-value logic than in the call-by-name one. Symmetrically, call-by-name has a more permissive notion of value (any term is a call-by-name value), so the coinduction principle $\omega{\mathrm{Stream}}$ applies to more properties in call-by-name than in call-by-value. In Figure 6, we give the unrestricted reasoning rules.

Fig. 6. Unrestricted coinduction rules $\sigma{\to}$ , $\sigma{\mathrm{Stream}}$ , and unrestricted induction rule $\sigma{\mathrm{Nat}}$ .

For non-recursive types like $A \to B$ the full power of $\sigma{\to}$ can be recovered from the weaker $\omega{\to}$ in the right setting. In call-by-name, the productivity restriction of $\omega{\to}$ is not important since any term can be a value, and we break down any property to apply $\omega{\to}$ at the root. However, this difference in power between (co)induction in the two semantics is not quite enough to account for the true strength of call-by-value induction and call-by-name coinduction because the strategy of breaking down the property in advance weakens the (co)inductive hypothesis. As a consequence, the fact that the sub-syntax of strict and productive properties includes $\forall$ quantifiers but not implications ( $\Phi' \Rightarrow \Phi$ ) of any form means that choosing the “best” semantics still does not fully restore $\omega{\mathrm{Nat}}$ to $\sigma{\mathrm{Nat}}$ or $\omega{\mathrm{Stream}}$ to $\sigma{\mathrm{Stream}}$ .

This essential difference in reasoning power raises the question: are the unrestricted induction and coinduction principles ever safe? Thankfully, it turns out that the full (co)induction rules $\sigma{\mathrm{Nat}}$ and $\sigma{\mathrm{Stream}}$ can be consistently added to the program logic, even in the presence of computational effects like first-class control, under the correct evaluation strategy (see Section 5).

Definition 3.8 (Strong Program Logics). The two strong program logics, which generalize the common extensional program logic (Figure 5) with additional sound rules specifically for call-by-name and call-by-value reduction are

  • The strong call-by-name program logic extends the call-by-name instance of Figure 5 with the $\sigma{\mathrm{Stream}}$ and $\sigma{\to}$ rules of coinduction from Figure 6.

  • The strong call-by-value program logic extends the call-by-value instance of Figure 5 with the $\sigma{\mathrm{Nat}}$ rule of induction from Figure 6.

Theorem 3.9. The strong call-by-name and call-by-value program logics are consistent.

4 The strength of strong (co)induction

Due to the lack of propositional implication, there are certain forms of inductive reasoning (for example, “strong” induction on the numbers) that are possible using $\sigma{\mathrm{Nat}}$ with a property $\Phi' \Rightarrow \Phi$ that cannot be derived from the $\omega{\mathrm{Nat}}$ —even in call-by-value. Likewise, there are certain forms of coinductive reasoning (for example, bisimulation) that are possible with $\sigma{\mathrm{Stream}}$ but cannot be derived from $\omega{\mathrm{Stream}}$ —even in call-by-name.

Next, we will explore the strength of full $\sigma{\mathrm{Nat}}$ and $\sigma{\mathrm{Stream}}$ versus the weaker $\omega{\mathrm{Nat}}$ and $\omega{\mathrm{Stream}}$ and the use of structural (co)induction for encoding several different reasoning principles for (co)inductive types.

Compositionality of weak mutual (co)induction

Before we get to the full strength of strong structural (co)induction, consider an example of what can be done with just the weak version all on its own. Mutual induction lets us prove two properties at the same time, where the correctness of each one depends simultaneously on the other. To prove $\Psi_1(x)$ and $\Psi_2(x)$ for all natural numbers x, there are two inductive cases: one showing $\Psi_1(\mathrm{succ} \, x)$ and the other showing $\Psi_2(\mathrm{succ} \, x)$ . The two cases can be proved separately from one another, but each one gets to assume both inductive hypotheses $\Psi_1(x)$ and $\Psi_2(x)$ hold. This principle is especially useful for generalizing the inductive hypotheses in situations where we are only interested in $\Psi_1(x)$ at the end, but the proof of $\Psi_1(x)$ requires additional knowledge about $\Psi_2(x)$ during the inductive step.

This mutual induction reasoning principle can be derived by applying the weak induction rule $\omega{\mathrm{Nat}}$ on the conjunction $\Psi_1(x) \wedge \Psi_2(x)$ first, before splitting the two apart like so:

This application of the weak $\omega{\mathrm{Nat}}$ is allowed because the conjunction of two strict properties $\Psi_1(x) \wedge \Psi_2(x)$ is also strict on x.

Since the rules for induction and coinduction mirror each other, we can encode mutual (weak) coinduction on streams in the exact same way using the $\omega{\mathrm{Stream}}$ and ConjI rules. This mutual weak coinduction rule looks like:

For example, we can apply this rule to formalize our previous mutually coinductive proof of Example Theorems 2.5 and 2.5 about evens and odds like so:Footnote 3

Where the two coinductive hypotheses are

\begin{align*} \Psi_1(\alpha) &= \forall s_1 \forall s_2. \langle {evens~(merge~s_1~s_2)} |\!| {\alpha} \rangle = \langle {s_1} |\!| {\alpha} \rangle \\ \Psi_2(\alpha) &= \forall s_1 \forall s_2. \langle {odds~(merge~s_1~s_2)} |\!| {\alpha} \rangle = \langle {s_2} |\!| {\alpha} \rangle\end{align*}

Both of these two propositions are productive on $\alpha$ because on the right side they are given $s_i$ which is always a value, and on the left side they are immediately matched on by evens or odds (which are represented by a corec which is itself a value). From here, the calculations showing all four required equalities follow the same steps as the informal proof in Example Theorems 2.5 and 2.5. Since only the weak form of coinduction is used, this fact about evens and odds holds true in languages with side effects under both call-by-name and call-by-value evaluation.

Notice that, for both mutual induction and coinduction, the strong rules $\sigma{\mathrm{Stream}}$ or $\sigma{\mathrm{Nat}}$ are only needed to verify fundamentally non-productive or non-strict propositions $\Phi_1 \wedge \Phi_2$ , respectively.

Strong induction on the naturals

In contrast to mutual induction, which can be derived from $\omega{\mathrm{Nat}}$ , the traditional notion of strong induction on the natural numbers really requires the full $\sigma{\mathrm{Nat}}$ . How can we formalize the derivation of strong induction? First, define the ordering relation on numbers in terms of the following equality and translation of the usual minus function (replacing negative results with zero) specified as follows:

We then write $\forall y \leq x : \mathrm{Nat}. \Phi$ as shorthand for the property $\forall y : \mathrm{Nat}.~ y \leq x : \mathrm{Nat} \Rightarrow \Phi$ . Applying $\sigma{\mathrm{Nat}}$ to the free x in this property gives:

Since we can derive the properties $\forall y \leq \mathrm{zero} : \mathrm{Nat}.~ y = \mathrm{zero} : \mathrm{Nat}$ (by definition of $\leq$ ) and $\forall x : \mathrm{Nat}.~ x \leq x : \mathrm{Nat}$ (by induction with $\sigma{\mathrm{Nat}}$ ), we can specialize the above application to derive the following simpler statement of strong induction on the naturals:

Notice that we can never use $\omega{\mathrm{Nat}}$ for this derivation, even if $\Phi$ happens to be strict on x. Why not? Because the property to which we apply induction,

includes an implication where the inducted-upon x is referenced to the left of $\implies$ , which is not allowed in properties that are strict on x.

Strong coinduction on streams

As with induction on the natural numbers, we can derive the dual notion of strong coinduction on infinite streams. First, define the ordering relation on stream projections as

\begin{align*} Q \leq R \div {\mathrm{Stream} \, A} &:= depth~Q \leq depth~R : {\mathrm{Nat}}\end{align*}

where $depth~Q$ computes the depth of any stream projection Q, effectively converting $\mathrm{tail}^n(\mathrm{head} \, \alpha)$ to $\mathrm{succ}^n\mathrm{zero}$ :

As before, we write the quantification $\forall \beta \leq \alpha \div \mathrm{Stream} \, A. \Phi$ as shorthand for $\forall\beta \div \mathrm{Stream} \, A.~\beta \leq \alpha\div\mathrm{Stream} \, A\Rightarrow\Phi$ . Applying $\sigma{\mathrm{Stream}}$ to this property gives:

Analogous to strong induction on the naturals, we can use this application to derive the following simpler statement of strong coinduction on streams:

From this, we can derive the following special case of strong coinduction, where we must show the first $n+1$ base cases (for $\mathrm{head} \, \beta$ , $\mathrm{tail}(\mathrm{head} \, \beta)$ , $\mathrm{tail}^n(\mathrm{head} \, \beta)$ ) directly, and then take the $n+1\textrm{th}$ tail projection in the coinductive case:

The above principle can prove that

(4.1) \begin{align} \alpha \div \mathrm{Stream} \, A \vdash \forall s : \mathrm{Stream} \, A. \langle {merge~(evens~s)~(odds~s)} |\!| {\alpha} \rangle = \langle {s} |\!| {\alpha} \rangle\end{align}

by stepping by 2. We prove the property for the base cases ( $\mathrm{head} \, \beta$ and $\mathrm{tail}(\mathrm{head} \, \beta)$ ) and then prove the coinductive case

\begin{align*} \alpha \div \mathrm{Stream} \, A \vdash \forall s : \mathrm{Stream} \, A. \langle {merge~(evens~s)~(odds~s)} |\!| {\mathrm{tail}(\mathrm{tail} \, \beta)} \rangle = \langle {s} |\!| {\mathrm{tail}(\mathrm{tail} \, \beta)} \rangle\end{align*}

assuming that the property holds for $\beta$ . This principle captures the proof of Example Theorem 2.6, with the difference that it avoids the case analysis. From Equation (4.1), we can then prove

(4.2) \begin{align} \forall s : \mathrm{Stream} \, A.~ merge~(evens~s)~(odds~s) = s : \mathrm{Stream} \, A\end{align}

by $IntroL,\sigma\mu, ElimR$ .

Bisimulation on streams

To conclude our exploration, we now turn to one of the most commonly used principles for reasoning about coinductive structures—bisimulation—which allows us to prove two objects are equal whenever they are related by any valid bisimulation relation of our choosing. The traditional principle of bisimulation on streams can be represented by the following inference rule, where the property $\Phi$ (with free variables $s_1$ and $s_2$ ) stands for an arbitrary relationship between two streams $s_1$ and $s_2$ :

The two assumptions confirm that $\Phi$ is a valid bisimulation relation: $\Phi$ only relates streams with equal heads and is closed under tail projection. We show that this principle is also subsumed by the strong coinduction rule $\sigma{\mathrm{Stream}}$ . We are going to prove

(4.3) \begin{align} \Gamma, \alpha : \mathrm{Stream} \, A \mid \Delta \vdash \forall s_1 , s_2 : \mathrm{Stream} \, A. \Phi \Rightarrow \langle {s_1} |\!| {\alpha} \rangle = \langle {s_2} |\!| {\alpha} \rangle\end{align}

Where we use the shorthand $\forall s_1,s_2 : \mathrm{Stream} \, A. \Phi$ to stand for multiple quantifications of the same type $\forall s : \mathrm{Stream} \, A. \forall s' : \mathrm{Stream} \, A. \Phi$ . From the above the goal follows:

We are proving property (4.3) by strong coinduction ( $\sigma{\mathrm{Stream}}$ ):

  • For the head case, we must show that

    The first bisimulation assumption already guarantees that $\mathrm{head} \, s_1 = \mathrm{head} \, s_2 : A$ whenever $\Phi$ holds on $s_1$ and $s_2$ , so this sub-goal follows directly from the congruence rules, as shown below:

  • For the tail case, from the coinductive hypothesis (referred to locally as CIH)

    we must show
    The second bisimulation assumption guarantees that $\Phi[{{\mathrm{tail} \, s_1}/{s_1},{\mathrm{tail} \, s_2}/{s_2}}]$ holds as well. Therefore, substituting $\mathrm{tail} \, s_1$ and $\mathrm{tail} \, s_2$ in the coinductive hypothesis gives the required result $\langle {\mathrm{tail} \, s_1} |\!| {\alpha} \rangle = \langle {\mathrm{tail} \, s_2} |\!| {\alpha} \rangle$ . More precisely, using the shorthand
    we can derive the goal of the coinductive step by weakening the given bisimulation premise $\Gamma_{Sim}\mid\Delta,\Phi\vdash\Phi[{{\mathrm{tail}{s_1}}/{s_1},{\mathrm{tail}{s_2}}/{s_2}}]$ as follows:

5 Consistency of the program logic

We have seen the (strong) program logics used to encode and prove a variety of different reasoning principles and program equalities. But how do we know if and when the syntactic rules in Figures 5 and 6 imply real equivalences between the results of programs? Applying $\beta$ reductions may be easy enough to believe since they correspond to actual steps of execution, but what about the (co)induction rules? They do not correspond to steps taken by the abstract machine, and we have already seen counterexamples where some of them, like $\sigma{\mathrm{Nat}}$ and $\sigma{\mathrm{Stream}}$ , can be inconsistent in certain contexts.

In order to prove that the syntactic program logics are consistent, we will show that they are all approximations of a more general notion of observational equivalence (also known as contextual equivalence Pitts, Reference Pitts1997b), defined directly in terms of the behavior of running programs. First, we need to characterize the valid stopping points where computation has ended and we can observe the last attempt at communication between a constructor or observer and some free (co)variable. Two such commands are considered equivalent if the top-level structure is the same (ignoring anything deeper, since the computation is finished).

Definition 5.1 (Observable).

The set of observable typing environments ( $\Theta$ ) and observable commands (d) is

The weak equivalence relation on observable commands, $d \sim d'$ , is

This weak equivalence relation is extended to any two commands, $c \approx c'$ , via computation: $c \approx c'$ if and only if there are observable commands d, d’ such that .

Definition 5.2 (Observational Equivalence).

Typed observational equivalence is defined as:

  1. 1. $\Gamma \vdash c_1 \approx c_2 $ iff $\Gamma \vdash c_i $ and for all contexts C, $\Theta \vdash C[c_i] $ implies $C[c_1] \approx C[c_2]$ .

  2. 2. $\Gamma \vdash v_1 \approx v_2 : A$ iff $\Gamma \vdash v_i : A$ and for all contexts C, $\Theta \vdash C[v_i] $ implies $C[v_1] \approx C[v_2]$ .

  3. 3. $\Gamma \vdash e_1 \approx e_2 \div A$ iff $\Gamma \vdash e_i \div A$ and for all contexts C, $\Theta \vdash C[e_i] $ implies $C[e_1] \approx C[e_2]$ .

where a context C is any command with a hole (written $\square$ ) somewhere in it. Filling the context (written C[c], C[v], or C[e]) means replacing the hole $\square$ by the given sub-expression, potentially capturing that sub-expression’s free variables.

Observational equivalence is particularly interesting since it is a consistent, computational congruence by definition:

Congruence Meaning it is a reflexive, transitive, and symmetric equivalence relation, which is also compatible with all contexts of the appropriate type. For example, if $\Gamma \vdash v_1 \approx v_2 : A$ , and C is a context such that $\Gamma \vdash C[v_i] $ is a well-typed command, then $\Gamma \vdash C[v_1] \approx C[v_2] $ holds by definition, because contexts compose.

Computational In the sense that it is closed under the reductions of the operational semantics: if $\Gamma \vdash c_1 \approx c_2 $ and $c_i \mathrel{\mapsto\!\!\!\!\to} c_i'$ then $\Gamma \vdash c_1' \approx c_2' $ .

Consistent As per Definition 3.6. $\bullet \vdash \mathrm{zero} \approx \mathrm{succ} \, V : \mathrm{Nat}$ does not hold, due to the counterexample context $\langle {\square} |\!| {\alpha} \rangle$ ; both $\alpha\div\mathrm{Nat}\vdash\langle {\mathrm{zero}} |\!| {\alpha} \rangle$ and $\alpha\div\mathrm{Nat}\vdash\langle {\mathrm{succ} \, V} |\!| {\alpha} \rangle$ are well-typed, irreducible commands in an observable environment, and yet $\langle {\mathrm{zero}} |\!| {\alpha} \rangle \not\sim \langle {\mathrm{succ} \, V} |\!| {\alpha} \rangle$ . Similarly, $\bullet \vdash \mathrm{head} \, E \approx \mathrm{tail} \, E' \div \mathrm{Stream} \, A$ does not hold, due to the counterexample context $\langle {x} |\!| {\square} \rangle$ in the observable environment $x : \mathrm{Stream} \, A$ .

In fact, observational equivalence is the coarsest such relation (Harper, Reference Harper2016, Theorem 46.6), meaning that any other relation with these properties are included in Definition 5.2. So, proving that another computational congruence relation is consistent (such as the syntactic theories in Figure 5 and Definition 3.8) can be reduced to proving they are included within observational equivalence.

Our primary goal, then, is to prove that these syntactic theories of equality all imply observational equivalence, from which their consistency falls out as a corollary. To do so, we will generalize the model of (co)inductive types from (Downen & Ariola, Reference Downen and Ariola2023)—based on the techniques of classical realizability (Krivine, Reference Krivine2005), (bi)orthogonality (Girard, Reference Girard1987; Munch-Maccagnoni, Reference Munch-Maccagnoni2009), $\top\top$ -closure (Pitts, Reference Pitts2000), and symmetric candidates (Barbanera & Berardi, Reference Barbanera and Berardi1994) for proving strong normalization of classical calculi—from unary predicates describing safety to binary relations describing equivalence.

5.1 Orthogonal relations and equality candidates

The safety model of Downen & Ariola (Reference Downen and Ariola2023, Section 6) is a logical relation built around the idea of orthogonality (Girard, Reference Girard1987; Pitts, Reference Pitts2000; Munch-Maccagnoni, Reference Munch-Maccagnoni2009): a safety predicate (written ) classifying when producers (v) and consumers (e) can safely interact with one another in a command (). Here we are interested in equality, in the sense that two commands have equivalent behavior when run. As such, we need to generalize orthogonality beyond the unary safety predicate on one command, and instead consider a binary equivalence relation between two commands.

We can now give our main definition of binary orthogonality serving in terms of the untyped weak equivalence among commands. Orthogonality, in turn, lets us describe a semantics for typed equality as a certain pair of relations between terms and coterms. This forms the potential (i.e., candidate Girard, Reference Girard1972) denotations of types, so each type of our language can be interpreted as a particular candidate of equality.

Definition 5.3 (Orthogonality).

The equivalence pole is the untyped equivalence relation on arbitrary commands ( $c \approx c'$ ) given in terms of weak equivalence of observable commands ( $d \sim d'$ ) from Definition 5.1:

Orthogonality of two binary relations $\mathbb{A}^+ \subseteq Term^2$ and $\mathbb{A}^- \subseteq CoTerm^2$ is defined asFootnote 4

We write to denote the largest coterm relation orthogonal to the term relation $\mathbb{A}^+$ , and symmetrically write to denote the largest term relation orthogonal to the coterm relation $\mathbb{A}^-$ , which are respectively defined as

Definition 5.4 (Candidates).

A pre-candidate is any pair $\mathbb{A}=(\mathbb{A}^+,\mathbb{A}^-)$ where $\mathbb{A}^+$ is a binary relation on terms, and $\mathbb{A}^-$ is a binary relation on coterms, i.e.,

A sound (pre-)candidate $\mathbb{A}=(\mathbb{A}^+,\mathbb{A}^-)$ satisfies the following soundness requirement:

  • Soundness: every combination of $\mathbb{A}^+$ -related terms $v ~\mathbb{A}^+~ v'$ and $\mathbb{A}^-$ -related coterms $e ~\mathbb{A}^-~ e'$ forms -equivalent commands .

A complete (pre-)candidate $\mathbb{A}=(\mathbb{A}^+,\mathbb{A}^-)$ satisfies these two completeness requirements:

  • Positive completeness: if for all $\mathbb{A}^-$ -related covalues $E ~\mathbb{A}^-~ E'$ , then $v ~\mathbb{A}^+~ v'$ are related by $\mathbb{A}^+$ .

  • Negative completeness: if for all $\mathbb{A}^+$ -related values $V ~\mathbb{A}^+~ V'$ , then $e ~\mathbb{A}^-~ e'$ are related by $\mathbb{A}^-$ .

An equality candidate is any sound and complete pre-candidate. ${\mathcal{PC}}$ denotes the set of all pre-candidates, ${\mathcal{SC}}$ denotes the set of sound ones, ${\mathcal{CC}}$ the set of complete ones, and ${\mathcal{EC}}$ denotes the set of all equality candidates.

As notation, given any pre-candidate $\mathbb{A}$ , we will always write $\mathbb{A}^+$ to denote the first component of $\mathbb{A}$ and $\mathbb{A}^-$ to denote the second one, so that $\mathbb{A} = (\mathbb{A}^+, \mathbb{A}^-)$ . Given a binary relation on terms $\mathbb{A}^+$ , we will occasionally write the sound candidate $(\mathbb{A}^+,\{\})$ as just $\mathbb{A}^+$ when the difference is clear from the context (notice that $(\mathbb{A}^+,\{\})$ is trivially sound by definition, but is incomplete). Likewise, we will occasionally write the sound candidate $(\{\},\mathbb{A}^-)$ as just the binary coterm relation $\mathbb{A}^-$ when unambiguous. The common case of the empty set $\{\}$ —which could be read as either the empty set of terms or the empty set of coterms—denotes the same sound candidate $(\{\},\{\})$ according to either reading.

5.2 Dual lattices and completion

With its two halves—one describing terms the other coterms—candidates provide multiple views on the relationship between types. These appear in the unary case of typed (co)terms, as in Downen & Ariola (Reference Downen and Ariola2023, Definition 6.5), and carry over, essentially unchanged, to binary relationships, too. In particular, the set of candidates supports two separate, but complementary, lattice structures with different orderings; one based on a refinement notion of plain containment and the other based on a notion of subtyping from programming languages.

Definition 5.5 (Refinement and Subtyping).

There are two ways of ordering pre-candidates: refinement (denoted by $\mathbb{A} \sqsubseteq \mathbb{B}$ meaning “ $\mathbb{A}$ refines $\mathbb{B}$ ” and “ $\mathbb{B}$ extends $\mathbb{A}$ ) and subtyping (denoted by $\mathbb{A} \leq \mathbb{B}$ meaning “ $\mathbb{A}$ is a subtype of $\mathbb{B}$ ” and “ $\mathbb{B}$ is a supertype of $\mathbb{A}$ ”), defined as:

Refinement and subtyping both define a complete lattice on pre-candidates with the following unions and intersections for refinement ( $\sqcup, \sqcap$ ) and subtyping ( $\vee, \wedge$ ), defined over any set of pre-candidates $\{\mathbb{A}_i\}_i \subseteq {\mathcal{PC}}$ as:

where $\bigcup$ and $\bigcap$ denote the union and intersection of binary relations, respectively.

There are many other ways in which refinement and subtyping differ from one another, and reveal different structures of equality candidates. Of note, the orthogonality operation distributes over the two orderings in completely opposite directions.

Property 5.6 (Orthogonal Ordering).

Given any pre-candidates $\mathbb{A}$ and $\mathbb{B}$ :

  1. 1. Antitonicity: If $\mathbb{A} \sqsubseteq \mathbb{B}$ then .

  2. 2. Monotonicity: If $\mathbb{A} \leq \mathbb{B}$ then .

Furthermore, the way the union and intersection operations in the two lattices preserve (or fail to preserve) soundness and completeness conditions also differ.

Property 5.7 (Sound and Complete Lattices).

Given any subset of sound candidates and complete candidates:

  1. 1. $\bigwedge_i\mathbb{A}_i$ and $\bigvee_i\mathbb{A}_i$ are sound, but $\bigwedge_i\mathbb{B}_i$ and $\bigvee_i\mathbb{B}_i$ may be incomplete.

  2. 2. is sound, but $\bigsqcup_i\mathbb{A}_i$ may be unsound.

  3. 3. $\bigsqcup_i\mathbb{B}_i$ is complete, but may be incomplete.

In order to build the interpretation of (co)inductive types, we need a complete lattice of equality candidates, not just a lattice of pre-candidates, that preserves both soundness and completeness. Since subtyping naturally gives us a complete sub-lattice of sound candidates, we will begin there, with the subgoal of filling in the missing parts of a sound candidate to generate the fully completed equality candidate.

Beginning with some initial starting point, completeness demands that we include all other relationships which are compatible with what is already there. Since completeness only tests potential (co)term relations w.r.t the (co)values already related by a candidate, we will have to isolate these (co)values as part of our testing criteria. For this purpose, the (co)value restriction $\mathbb{A}^v$ of a candidate $\mathbb{A} = (\mathbb{A}^+, \mathbb{A}^-)$ includes only those values and covalues related by $\mathbb{A}$ , defined as

We can use this (co)value restriction to form a complete equality candidate by interleaving it with the orthogonality operation. But there is a dual choice in our starting point: the positive viewpoint uses the values related by $\mathbb{A}$ as the defining axioms to define the equality candidate, and the negative viewpoint uses the covalues related by $\mathbb{A}$ as the defining axioms.

Definition 5.8 (Positive and Negative Candidates).

Given a sound candidate $\mathbb{A}$ , the positive and negative constructions of equality candidates around $\mathbb{A}$ are, respectively, defined as

The positive and negative viewpoints give complementary equality candidates. By starting with the values first, Pos gives a smaller equality candidate (w.r.t subtyping) compared to Neg. In fact, these two are the canonically largest and smallest equality candidates that extend any sound starting point. This fact lets us modify the subtyping lattice to preserve both soundness and completeness in both directions.

Lemma 5.9 (Positive & Negative Completion).

For any sound candidate $\mathbb{A}$ , $\mathrm{Pos}(\mathbb{A})$ is the smallest sound and complete extension of $\mathbb{A}^v$ w.r.t subtyping, and $\mathrm{Neg}(\mathbb{A})$ is the largest sound and complete extension of $\mathbb{A}^v$ w.r.t subtyping. In other words, both $\mathrm{Pos}(\mathbb{A})$ and $\mathrm{Neg}(\mathbb{A})$ are equality candidates such that $\mathrm{Pos}(\mathbb{A}) \sqsupseteq \mathbb{A}^v$ and $\mathrm{Neg}(\mathbb{A}) \sqsupseteq \mathbb{A}^v$ , and given any other equality candidate $\mathbb{C} \sqsupseteq \mathbb{A}^v$ ,

Proof sketch The proof follows the same structure as in Downen & Ariola (Reference Downen and Ariola2023, Lemma 6.7) extended from sets to binary relations, which uses the facts that $\mathrm{Pos}(\mathbb{A})$ and $\mathrm{Neg}(\mathbb{A})$ are fixed points of and, furthermore, that the set of these fixed points is exactly the set of all equality candidates (Downen et al., Reference Downen, Johnson-Freyd and Ariola2020, Property 9).

Definition 5.10 (Equality Candidate Lattice).

Equality candidates form a complete lattice w.r.t subtyping whose unions ( $\curlyvee$ ) and intersections ( $\curlywedge$ ) are (Downen et al., Reference Downen, Ariola and Ghilezan2019):

Notice that the least equality candidate w.r.t subtyping is

and the greatest one is

From this perspective, we can re-describe the positive and negative completions in terms of the subtyping lattice of equality candidates. As per Lemmas 5.9 and 5.9, Pos and Neg are the intersection and union (respectively) of all extensions of a restricted sound candidate $\mathbb{A}^v$ :

As a corollary of Lemmas 5.9 and 5.9 and the definition of Pos and Neg, we get the following facts that let us reason about positively and negatively constructed equality candidates.

Property 5.11 (Positive and Negative Invariance).

For any sound candidates $\mathbb{A}$ and $\mathbb{B}$ :

  • If $\mathbb{A}$ and $\mathbb{B}$ relate the same values, then $\mathrm{Pos}(\mathbb{A}) = \mathrm{Pos}(\mathbb{B})$ .

  • If $\mathbb{A}$ and $\mathbb{B}$ relate the same covalues, then $\mathrm{Neg}(\mathbb{A}) = \mathrm{Neg}(\mathbb{B})$ .

Property 5.12 (Strong Positive Negative Completeness).

For any sound candidate $\mathbb{A}$ :

  • $E ~\mathrm{Pos}(\mathbb{A})^-~ E'$ if and only if for all $V ~\mathbb{A}^+~ V'$ .

  • $V ~\mathrm{Neg}(\mathbb{A})^+~ V'$ if and only if for all $E ~\mathbb{A}^-~ E'$ .

Property 5.13. For any set of equality candidates $\{\mathbb{A}_i\}_i$ :

  1. 1. If $e ~\mathbb{A}_i^-~ e'$ for some i, then $e \curlywedge_i\mathbb{A}_i~ e'$ . If $V ~\mathbb{A}_i^+~ V'$ for all i, then $V \curlywedge_i\mathbb{A}_i~ V'$ .

  2. 2. If $v ~\mathbb{A}_i~ v'$ for some i, then $v \curlyvee_i\mathbb{A}_i~ v'$ . If $E ~\mathbb{A}_i^-~ E'$ for all i, then $E \curlyvee_i\mathbb{A}_i~ E'$ .

5.3 Interpretation of types and properties

We now have enough infrastructure to define the model of observational equivalence—as shown in Figure 7—by interpreting each syntactic entity (types, properties, environments, and judgements) into its semantic counterpart.

Fig. 7. Model of observational equivalence in the abstract machine.

Each syntactic type A is interpreted as an equality candidate, denoted by $[\![{A}]\!]$ , which is defined by induction on the syntax of A. This interpretation has three main cases—one for each type constructor—which are all defined in the style of Knaster-Tarski (Knaster, Reference Knaster1928; Tarski, Reference Tarski1955) fixed points in the subtyping lattice of equality candidates:

  • A function type $A \to B$ is interpreted as the equality candidate relating the fewest covalues possible, while still relating any two call stacks built from $[\![{A}]\!]$ -related arguments and $[\![{B}]\!]$ -related return continuations. Dually, this is the equality candidate relating the most values possible, as long as they have equivalent behavior when observed by those previously described related call stacks.

  • The number type Nat is interpreted as the equality candidate relating the fewest terms possible, while still relating 0 to itself, and ensuring that the successors of any two related values are still related. Dually, this is the equality candidate relating the most covalues possible, as long as they respond the same to any of those related numbers.

  • A stream type $\mathrm{Stream} \, A$ is interpreted as the equality candidate relating the fewest covalues possible, while still relating any two head projections with $[\![{A}]\!]$ -related continuations, and ensuring that the tail of any two related $\mathrm{Stream} \, A$ projection are still related. Dually, this equality candidate relates the most terms possible, as long as they have equivalent behavior when observed by those related stream projections.

Each typing environment $\Gamma$ is interpreted as a binary relation on substitutions, $[\![{\Gamma}]\!]$ , both of which replace some variables with values, and some covariables with covalues. The interpretation of $\Gamma$ (written $\rho ~[\![{\Gamma}]\!]~ \rho'$ ) relates two such substitutions $\rho$ and $\rho'$ that abide by all three of the following criteria:

  • For each variable x of type A in the environment $\Gamma$ , both $\rho$ and $\rho'$ must substitute some value for x (call them $x[{\rho}]=V$ and $x[{\rho'}]=V'$ , respectively), such that V and V’ are related by the interpretation of A.

  • For each covariable x of type A in the environment $\Gamma$ , both $\rho$ and $\rho'$ must substitute some covalue for $\alpha$ (call them $\alpha[{\rho}]=E$ and $\alpha[{\rho'}]=E'$ , respectively) such that E and E’ are related by the interpretation of A.

  • For each property $\Phi$ assumed in the environment $\Gamma$ , the interpretation of $\Phi$ must be true when given both $\rho$ and $\rho'$ . Or in other words, $\Phi$ must relate $\rho$ and $\rho'$ .

Singular syntactic properties $\Phi$ —as well as collections of hypotheses $\Delta$ —are interpreted as a binary predicate deciding whether or not that property holds under a given pair of substitutions. This is equivalent to interpreting $\Phi$ or $\Delta$ as a binary relation on substitutions, just like we did for typing environments, that identifies which substitutions make the property true. The interpretation of these properties as relations comes in three different flavors, which correspond to the three different roles served by each specific $\Phi$ :

  • Equalities: There are three different forms of equalities. Two commands are considered equal under a pair of substitutions when they are -related after applying the left substitution to the left command and the right substitution to the right command. Similarly, two (co)terms are considered equal at a type A under a pair of substitutions when applying those substitutions leads to $[\![{A}]\!]$ -related (co)terms.

  • Quantifiers: Universal quantifiers signify that a property holds under any possible extension allowed by the type of the quantified (co)variable. Universal quantification over a variable, $\forall x{:}A. \Phi$ , relates two substitutions when $\Phi$ does, after extending the substitutions with any pair $[\![{A}]\!]$ -related values for x. Universal quantification over a covariable is defined in the same way.

  • Logical connectives: The logical connectives of implication ( $\Phi \Rightarrow \Phi'$ ) and conjunction ( $\Phi \wedge \Phi'$ ) are interpreted directly for each pair of substitutions. Equivalently, we can say that $[\![{\Phi \wedge \Phi'}]\!]$ means $[\![{\Phi}]\!] \cap [\![{\Phi'}]\!]$ using the intersection of relations ( $\cap$ ) that we’ve used previously, and $[\![{\Phi \Rightarrow \Phi'}]\!]$ means $[\![{\Phi}]\!] \implies [\![{\Phi'}]\!]$ where ( $\implies$ ) denotes the implication of relations.

Speaking more broadly, we can generalize the universal quantification and environment extension from Figure 7 to range over pre-candidates that lie outside the syntactic type system. This generality will be needed as we simplify away the extraneous elements of a type that we don’t need to consider while proving a property. For any pre-candidate $\mathbb{A}$ and binary substitution relations $\gamma$ and $\phi$ , the two universal quantifiers and environment extensions are:

Last but not least are judgements of the form $\Gamma \mid \Delta \vdash \Phi$ , which are interpreted as just true or false statements. The syntactic entailment $\vdash$ is interpreted as the boolean test for relational implication $\subseteq$ , so that $[\![{\Gamma \mid \Delta \vdash \Phi}]\!]$ whenever the environment $[\![{\Gamma}]\!]$ combined with the constraints in $[\![{\Delta}]\!]$ implies the property $[\![{\Phi}]\!]$ . In other words, we can understand $[\![{\Gamma \mid \Delta \vdash \Phi}]\!]$ pointwise as the equivalent statement

that $\rho~[\![{\Phi}]\!]~\rho'$ holds for all possible substitutions $\rho~[\![{\Gamma}]\!]~\rho'$ given by the typing environment and satisfying the pre-condition $\rho~[\![{\Delta}]\!]~\rho'$ . This implicational interpretation of entailment gives rise to some useful structure to reason about the semantics of judgements.

Property 5.14. For any pre-candidate $\mathbb{A}$ and binary substitution relations $\gamma$ , $\phi$ , and $\phi'$ :

Furthermore, for any related $\rho ~\gamma~ \rho'$ , we have related extensions $\rho[{{V}/{x}}]~(\gamma,x:\mathbb{A})~\rho[{{V'}/{x}}]$ for all $V ~\mathbb{A}~ V'$ , and $\rho[{{E}/{\alpha}}]~(\gamma,\alpha:\mathbb{A})~\rho[{{E'}/{\alpha}}]$ for all $E ~\mathbb{A}~ E'$ .

5.4 Universal consistency of weak (co)induction

We now turn to justifying inductive and coinductive reasoning in terms of the above model. One key component is that (co)induction seeks to reason about a type by only considering the concrete structures of a type. For an inductive type like Nat, that means we want to consider only the zero and succ cases of values, and ignore the rest. Dually for coinductive types like $\mathrm{Stream} \, A$ and $A \to B$ , we want to consider only the head and tail cases of stream covalues and only the stack $V \cdot E$ cases for function covalues.

The first step in this direction is to notice that certain universal properties need to consider fewer cases for positively and negatively complete equality candidates. A strict property on x holds for all related values of $\mathrm{Pos}(\mathbb{A})$ exactly when it holds on only the values related by $\mathbb{A}$ . Dually, a productive property on $\alpha$ holds for all related covalues of $\mathrm{Neg}(\mathbb{A})$ exactly when it holds on only the covalues related by $\mathbb{A}$ . Note that this fact does not depend on the evaluation strategy of the language, but is instead ensured by the strictness or productivity of the underlying property.

Lemma 5.15 ((De)Constructive (Co)Induction).

For any sound candidate $\mathbb{A}$ and substitution relation $\gamma$ :

  1. 1. $ \gamma, x : \mathrm{Pos}(\mathbb{A}) \subseteq [\![{\Psi(x)}]\!] $ if and only if $ \gamma, x : \mathbb{A} \subseteq [\![{\Psi(x)}]\!] , $ and

  2. 2. $ \gamma, \alpha \div \mathrm{Neg}(\mathbb{A}) \subseteq [\![{\Psi(\alpha)}]\!] $ if and only if $ \gamma, \alpha \div \mathbb{A} \subseteq [\![{\Psi(\alpha)}]\!] . $

Proof We use Property 5.14 to prove $\gamma, x:\mathbb{A} \subseteq [\![{\Psi(x)}]\!]$ and $\gamma, x:\mathrm{Pos}(\mathbb{A}) \subseteq [\![{\Psi(x)}]\!]$ are equivalent statements generically for all $\gamma$ by induction on the syntax of $\Psi(x)$ :

  • $\langle {x} |\!| {E} \rangle = \langle {x} |\!| {E'} \rangle $ where x is not free in E or E’. First, note that $\mathbb{A} \sqsubseteq \mathrm{Pos}(\mathbb{A})$ , so that $ \forall x{:}{\mathrm{Pos}(\mathbb{A})}. [\![{\langle {x} |\!| {E} \rangle=\langle {x} |\!| {E'} \rangle}]\!] $ implies $ \forall x{:}\mathbb{A}. [\![{\langle {x} |\!| {E} \rangle=\langle {x} |\!| {E'} \rangle}]\!] $ via this inclusion. Furthermore, $ \forall x{:}\mathbb{A}. [\![{\langle {x} |\!| {E} \rangle=\langle {x} |\!| {E'} \rangle}]\!] $ means

    for all $V ~\mathbb{A}~ V'$ (since $E[{{V}/{x}}]=E$ and $E'[{{V'}/{x}}]=E'$ ), and thus by the definition of orthogonality. Therefore, $E ~\mathrm{Pos}(\mathbb{A})~ E'$ by Property 5.12, and thus
    for any $V ~\mathrm{Pos}(\mathbb{A})~ V'$ , which means $\forall x{:}{\mathrm{Pos}(\mathbb{A})}. [\![{\langle {x} |\!| {E} \rangle=\langle {x} |\!| {E'} \rangle}]\!]$ . In other words,
    are equivalent substitution relations, and thus more generally
  • $\forall y{:}B. \Psi(x)$ where $y \neq x$ . Applying Property 5.14:

  • $\forall \alpha{\div}B. \Psi(x)$ . Follows by permuting the bindings of x and $\alpha$ and applying the inductive hypothesis to $\gamma$ extended with $\alpha \div [\![{B}]\!]$ analogously to the previous case.

  • $\Phi \Rightarrow \Psi(x)$ where x is not free in $\Phi$ . Applying Property 5.14:

  • $\Psi_1(x) \wedge \Psi_2(x)$ . Note that $[\![{\Psi_1(x) \wedge \Psi_2(x)}]\!] = [\![{\Psi_1(x)}]\!] \cap [\![{\Psi_2(x)}]\!]$ so

The “if” direction for property 2 follows analogously to the above using Property 5.12 for $\mathrm{Neg}(\mathbb{A})$ in the base case of an equality $\langle {V} |\!| {\alpha} \rangle = \langle {V'} |\!| {\alpha} \rangle $ .

Lemma 5.15 is enough to prove the extensional rule $\omega{\to}$ for function types, since the interpretation $[\![{A \to B}]\!]$ corresponds exactly to a negatively-constructed type. As the largest equality candidate which relates call stacks built from related parts, we can isolate these call stacks as a negative type.

Property 5.16 (Negative Functions).

$[\![{A \to B}]\!] = \mathrm{Neg}({[\![{A}]\!]}\odot{[\![{B}]\!]})$ where ${\mathbb{A}}\odot{\mathbb{B}}$ is the least relation on covalues such that:

Lemma 5.17 ( $\omega{\to}$ ).

Given $\alpha, \beta, x \notin FV(\Delta)$ , $[\![{\Gamma, \alpha \div A \to B \mid \Delta \vdash \Psi(\alpha)}]\!]$ if and only if $[\![{\Gamma, x : A, \beta \div B \mid \Delta \vdash \Psi(x \cdot \beta)}]\!]$ .

Proof By viewing $[\![{A \to B}]\!]$ in terms core call-stack relation ${[\![{A}]\!]}\odot{[\![{B}]\!]}$ (Property 5.16), and using the fact that $\alpha \notin FV(\Delta)$ to commute the hypothesis with the binding (Property 5.14),

we learn from Lemma 5.15 that the quantification over $\alpha \div \mathrm{Neg}({[\![{A}]\!]}\odot{[\![{B}]\!]})$ is equivalent to the same quantification over call stacks:

We can perform a similar inversion on the (co)inductive types, although not all at once. Rather, this bottom-up redefinition of natural numbers and streams must work incrementally. Beginning with the most extreme starting point (the least equality candidate $\mathrm{Pos}\{\}$ for inductive numbers and the greatest equality candidate $\mathrm{Neg}\{\}$ for coinductive streams), we iteratively build toward the final answer one step at a time. For the natural numbers, we use these interpretations of the zero and succ constructors as relations between values built by those constructors

\begin{align*} \mathrm{zero} ~[\![{\mathrm{zero}}]\!]~ \mathrm{zero} &:= \text{trivially true} & (\mathrm{succ} \, V) ~[\![{\mathrm{succ}}]\!](\mathbb{A})~ (\mathrm{succ} \, V') &:= V ~\mathbb{A}~ V'\end{align*}

in order to define larger and larger approximations of the $[\![{\mathrm{Nat}}]\!]$ equality candidate:

\begin{align*} [\![{\mathrm{Nat}}]\!]_0 &:= \mathrm{Pos}\{\} & [\![{\mathrm{Nat}}]\!]_{i+1} &:= \mathrm{Pos}([\![{\mathrm{zero}}]\!] \vee [\![{\mathrm{succ}}]\!]([\![{\mathrm{Nat}}]\!]_i))\end{align*}

At the limit, the union of all under-approximations is the Kleene-style fixed point definition (Kleene, Reference Kleene1971) of natural numbers. Thankfully, the dual construction of coinductive streams can be done in exactly the same way, just working from the other direction of the subtyping lattice. With these interpretations of the head and tail projections as relations between covalues built by those destructors

\begin{align*} (\mathrm{head} \, E) ~[\![{\mathrm{head}}]\!](\mathbb{A})~ (\mathrm{head} \, E') &:= E ~\mathbb{A}~ E' & (\mathrm{tail} \, E) ~[\![{\mathrm{tail}}]\!](\mathbb{A})~ (\mathrm{tail} \, E') &:= E ~\mathbb{A}~ E'\end{align*}

we can define smaller and smaller approximations of $[\![{\mathrm{Stream} \, A}]\!]$ :

\begin{align*} [\![{\mathrm{Stream} \, A}]\!]_0 &:= \mathrm{Neg}\{\} & [\![{\mathrm{Stream} \, A}]\!]_{i+1} &:= \mathrm{Neg}([\![{\mathrm{head}}]\!]([\![{A}]\!]) \wedge [\![{\mathrm{tail}}]\!]([\![{\mathrm{Stream} \, A}]\!]_i))\end{align*}

Here, the intersection of over-approximations is the dual Kleene-style fixed point definition of streams. These incremental fixed points define the same equality candidate as the Tarski-style fixed points from Figure 7.

Lemma 5.18 (Positive Numbers & Negative Streams).

Under both call-by-value and call-by-name evaluation,

Proof sketch Generalizing the proof from Downen & Ariola (Reference Downen and Ariola2023, Lemma 6.13) from sets to binary relations requires the analogous facts (Downen & Ariola, Reference Downen and Ariola2023, Lemmas 1.19 and 1.22) that

The key to demonstrating that these two instances of unions of numbers and intersections of streams are equal is in showing that we can fully observe a constructed number or a stream projection of any size.

For numbers, notice relates the following instance of the recursor to itself:

Then, given any , we can use the fact that to trace the reductions of the commands and show that $V ~[\![{\mathrm{Nat}}]\!]_i V'$ for some $i\textrm{th}$ finite approximation.

Streams follow a similar logic. Notice that relates this stream to itself for any $V ~[\![{A}]\!]~ V'$ :

Then, given any $V ~[\![{A}]\!]~ V'$ and , we can use the fact that to trace the reductions of the commands and show that $E ~[\![{\mathrm{Stream} \, A}]\!]_i E'$ for some $i\textrm{th}$ finite approximation.

It follows that these provide another definition of the least equality candidate closed under zero and succ, and the greatest equality candidate closed under head and tail, respectively. Since there can be only one least/greatest equality candidate satisfying the same closure condition, they must be the same as the ones in Figure 7.

The incremental nature of the Kleene-style redefinitions makes it easy to reason (co)inductively over the $i\textrm{th}$ approximation steps. This way, we can show that the premises to the (co)inductive inference rules $\omega{\mathrm{Nat}}$ and $\omega{\mathrm{Stream}}$ are interpreted as equivalent statements to their conclusions.

Lemma 5.19 ( $\omega{\mathrm{Nat}}$ ). Given $x \notin FV(\Delta)$ , $[\![{\Gamma, x : \mathrm{Nat} \mid \Delta \vdash \Psi(x)}]\!]$ if and only if $[\![{\Gamma \mid \Delta \vdash \Psi(\mathrm{zero})}]\!]$ and $[\![{\Gamma, x:\mathrm{Nat} \mid \Delta, \Psi(x) \vdash \Psi(\mathrm{succ} \, x)}]\!]$ .

Proof The “only if” direction follows immediately, since the relations $\mathrm{zero} ~[\![{\mathrm{Nat}}]\!]~ \mathrm{zero}$ and $(\mathrm{succ} \, V) ~[\![{\mathrm{Nat}}]\!]~ (\mathrm{succ} \, V')$ hold for any $V ~ [\![{\mathrm{Nat}}]\!]~ V'$ .

For the “if” direction, assume $[\![{\Gamma \mid \Delta \vdash \Psi(\mathrm{zero})}]\!]$ and $[\![{\Gamma, x : \mathrm{Nat} \mid \Delta, \Psi(x) \vdash \Psi(\mathrm{succ} \, x)}]\!]$ hold, and we will show that $[\![{\Gamma, x : \mathrm{Nat} \mid \Delta \vdash \Psi(x)}]\!]$ holds, too. From Property 5.14 and Lemmas 5.15 and 5.18, it suffices to show that the following equivalent proposition holds:

Let $\gamma = [\![{\Gamma}]\!] \cap [\![{\Delta}]\!]$ , and we can now proceed by proving each individual approximation $ \gamma, x : [\![{\mathrm{Nat}}]\!]_i \subseteq [\![{\Psi(x)}]\!] $ by induction on i.

  • (Base case: 0) $[\![{\mathrm{Nat}}]\!]_0=\mathrm{Pos}\{\}$ , so we must show $\gamma, x : \mathrm{Pos}\{\} \subseteq [\![{\Psi(x)}]\!]$ . By Lemma 5.15, this statement is equivalent to $\gamma, x : \{\} \subseteq [\![{\Psi(x)}]\!]$ , which is vacuously true since there are no possible choices for x in the empty pre-candidate $\{\}$ .

  • (Inductive case: $i+1$ ) $[\![{\mathrm{Nat}}]\!]_i=\mathrm{Pos}([\![{\mathrm{zero}}]\!]\vee[\![{\mathrm{succ}}]\!]([\![{\mathrm{Nat}}]\!]_i))$ . By Lemma 5.15, these statements

    \begin{align*} & \gamma, x : [\![{\mathrm{Nat}}]\!]_{i+1} \subseteq [\![{\Psi(x)}]\!] \\ &= \gamma, x : \mathrm{Pos}([\![{\mathrm{zero}}]\!]\vee[\![{\mathrm{succ}}]\!]([\![{\mathrm{Nat}}]\!]_i)) \subseteq [\![{\Psi(x)}]\!] \\ &= \gamma, x : [\![{\mathrm{zero}}]\!]\vee[\![{\mathrm{succ}}]\!]([\![{\mathrm{Nat}}]\!]_i) \subseteq [\![{\Psi(x)}]\!] \\ &= (\gamma, x : [\![{\mathrm{zero}}]\!] \subseteq [\![{\Psi(x)}]\!]) \mathbin{\mathrm{and}} (\gamma, x : \mathrm{succ}([\![{\mathrm{Nat}}]\!]_i) \subseteq [\![{\Psi(x)}]\!]) \end{align*}
    are equivalent, and we must show that they hold. Since $\mathrm{zero}~[\![{\mathrm{zero}}]\!]~\mathrm{zero}$ is the only related values of $[\![{\mathrm{zero}}]\!]$ , the assumption $[\![{\Gamma \mid \Delta \vdash \Psi(\mathrm{zero})}]\!]$ is equivalent to $\gamma, x : [\![{\mathrm{zero}}]\!] \subseteq [\![{\Psi(x)}]\!]$ . Similarly, $(\mathrm{succ} \, V)~[\![{\mathrm{succ}}]\!]([\![{\mathrm{Nat}}]\!]_i)~(\mathrm{succ} \, V')$ are the only related values of $[\![{\mathrm{succ}}]\!]([\![{\mathrm{Nat}}]\!]_i)$ for any $V~[\![{\mathrm{Nat}}]\!]_i~V'$ . By the inductive hypothesis, we know $\gamma, x : [\![{\mathrm{Nat}}]\!]_i \subseteq [\![{\Phi(x)}]\!]$ . Because $[\![{\mathrm{Nat}}]\!]_i \leq [\![{\mathrm{Nat}}]\!]$ , the assumption $[\![{\Gamma, x:\mathrm{Nat} \mid \Delta, \Phi(x) \vdash \Phi(\mathrm{succ} \, x)}]\!]$ implies $ ([\![{\Gamma}]\!], x : [\![{\mathrm{Nat}}]\!]_i) \cap \Delta \subseteq [\![{\Phi(\mathrm{succ} \, x)}]\!] $ which is equivalent to $ \gamma, x : [\![{\mathrm{succ}}]\!]([\![{\mathrm{Nat}}]\!]_i) \subseteq [\![{\Phi(x)}]\!] . $ Therefore, the equivalent statements
    \begin{align*} & (\gamma, x : [\![{\mathrm{zero}}]\!] \subseteq [\![{\Psi(x)}]\!]) \mathbin{\mathrm{and}} (\gamma, x : \mathrm{succ}([\![{\mathrm{Nat}}]\!]_i) \subseteq [\![{\Psi(x)}]\!]) \\ &= \gamma, x : [\![{\mathrm{Nat}}]\!]_{i+1} \subseteq [\![{\Psi(x)}]\!] \end{align*}
    hold.

So since $\gamma, x : [\![{\mathrm{Nat}}]\!]_i \subseteq [\![{\Psi(x)}]\!]$ holds for every i, so too does

\begin{align*} \gamma, x : \bigvee_{i=0}^\infty [\![{\mathrm{Nat}_i}]\!] \subseteq [\![{\Psi(x)}]\!] &= [\![{\Gamma, x : \mathrm{Nat} \mid \Delta \vdash \Psi (x)}]\!] \end{align*}

Lemma 5.20 ( $\omega{\mathrm{Stream}}$ ). Given $\alpha, \beta \notin FV(\Delta)$ , $[\![{\Gamma, \alpha \div \mathrm{Stream} \, A \mid \Delta \vdash \Psi(\alpha)}]\!]$ if and only if $[\![{\Gamma, \beta \div A \mid \Delta \vdash \Psi(\mathrm{head} \, \beta)}]\!]$ and $[\![{\Gamma, \alpha\div\mathrm{Stream} \, A \mid \Delta, \Psi(\alpha) \vdash \Psi(\mathrm{tail} \, \, \alpha)}]\!]$ .

Proof Analogous to Lemma 5.19. The “only if” direction is immediate since $(\mathrm{head} \, \, E) ~[\![{\mathrm{Stream} \, A}]\!]~ (\mathrm{head} \, \, E')$ holds for any $E ~[\![{A}]\!]~ E'$ and $(\mathrm{tail} \, E) ~[\![{\mathrm{Stream} \, A}]\!]~ (\mathrm{tail} \, E')$ holds for any $E ~[\![{\mathrm{Stream} \, A}]\!]~ E'$ . For the “if” direction, it suffices to show the equivalent statement $ ([\![{\Gamma}]\!] \cap [\![{\Delta}]\!]), \alpha \div \bigwedge_i[\![{\mathrm{Stream} \, A}]\!]_i \subseteq [\![{\Psi(\alpha)}]\!] $ holds via Property 5.14 and Lemmas 5.15 and 5.18, which follows by induction on i using Lemma 5.15 in a similar manner as in Lemma 5.19.

From this semantics of the main (co)inductive principles, we can prove soundness with respect to the model, analogous to Downen & Ariola (Reference Downen and Ariola2023), which in turn lets us derive the fact that the universal program logic is a consistent approximation of observational equivalence in both call-by-name and call-by-value evaluation.

Theorem 5.21 (Soundness).

If $\Gamma \mid \Delta \vdash \Phi$ is derivable in the extensional program logic, then $[\![{\Gamma \mid \Delta \vdash \Phi}]\!]$ is true for both call-by-value and call-by-name evaluation.

Lemma 5.22. $\alpha ~[\![{\mathrm{Nat}}]\!]~ \alpha$ , and $x ~[\![{\mathrm{Stream} \, A}]\!]~ x$ and $x ~[\![{A \to B}]\!]~ x$ for any $\alpha$ and x.

Proof by definition of , so that $x ~\mathrm{Neg}({[\![{A}]\!]}\odot{[\![{B}]\!]})~ x$ by Property 5.12, and thus $x ~[\![{A \to B}]\!]~ x$ by Property 5.16.

Dually, both and by definition of . As a result, we have $\alpha ~[\![{\mathrm{Nat}}]\!]_i~ \alpha$ for all i: the case for $[\![{\mathrm{Nat}}]\!]_0 = \mathrm{Pos}\{\}$ is trivial since all covalues are related by $\mathrm{Pos}\{\}$ , and the case for $[\![{\mathrm{Nat}}]\!]_{i+1} = \mathrm{Pos}([\![{\mathrm{zero}}]\!]\vee[\![{\mathrm{succ}}]\!][\![{\mathrm{Nat}}]\!]_i)$ follows from the previously mentioned fact about and Property 5.12. Finally, by Property 5.13, and thus $\alpha ~[\![{\mathrm{Nat}}]\!]~ \alpha$ by Lemma 5.18.

The fact that $x ~[\![{\mathrm{Stream} \, A}]\!]~ x$ follows from Properties 5.12 and 5.13 and Lemma 5.18 similarly to the above, using the fact that and by definition of .

Theorem 5.23. In the extensional program logic, the following holds for both call-by-name and call-by-value evaluation:

  1. 1. If $\Gamma \vdash c = c' $ then $\Gamma \vdash c \approx c' $ .

  2. 2. If $\Gamma \vdash v = v' : A$ then $\Gamma \vdash v \approx v' : A$ .

  3. 3. If $\Gamma \vdash e = e' \div A$ then $\Gamma \vdash e \approx e' \div A$ .

Proof Suppose $\Gamma \vdash c = c' $ (the cases for $\Gamma \vdash v = v' : A$ and $\Gamma \vdash e = e' \div A$ are analogous) and let C be any context such that $\Theta \vdash C[c] $ and $\Theta \vdash C[c'] $ , and thus $\Theta \vdash C[c] = C[c'] $ by congruence. From Theorem 5.21, it must be that $[\![{\Theta \vdash C[c] = C[c'] }]\!]$ , i.e., for any substitution $\rho ~[\![{\Theta}]\!]~ \rho'$ , then . Note that all (co)variable type assignments in $\Theta$ have the form $\alpha \div \mathrm{Nat}$ , $x : \mathrm{Stream} \, A$ , and $x : A \to B$ , so by Lemma 5.22, we know that the $[\![{\Theta}]\!]$ relates the identity substitution to itself. Thus, a valid instance of $[\![{\Theta \vdash C[c] = C[c'] }]\!]$ is just , meaning . In other words, we know $\Gamma \vdash c \approx c' $ by definition of observational equivalence.

Theorem 3.7. The extensional program logic in Figure 5 is consistent for both the call-by-name and call-by-value semantics.

Proof A corollary of Theorem 5.23, since observational equivalence is a consistent congruence by definition.

5.5 Strong call-by-value induction and call-by-name coinduction

In the general case, we need to interleave a (positive or negative) completion while building up a (co)inductive equality candidate like $[\![{\mathrm{Nat}}]\!]_i$ or $[\![{\mathrm{Stream} \, A}]\!]_i$ . But in the specific case where the evaluation strategy lines up nicely, we get a much simpler definition for call-by-value inductive types and call-by-name coinductive types.

Lemma 5.24 (Strict Construction of Naturals).

Under call-by-value evaluation, $V ~[\![{\mathrm{Nat}}]\!]~ V'$ if and only if $V = V' = \mathrm{succ}^n\mathrm{zero}$ for some n. Furthermore, $ [\![{\mathrm{Nat}}]\!] = \mathrm{Pos}(\mathbb{N})$ under call-by-value evaluation, where $\mathbb{N}$ is the reflexive relation on only the hereditary numeric constructions, i.e., the smallest binary relation such that $ (\mathrm{succ}^n \mathrm{zero}) ~\mathbb{N}~ (\mathrm{succ}^n \mathrm{zero}) .$

Proof Let , and note that $\alpha \div \mathrm{Nat} \vdash deep_{\mathrm{Nat}} \div \mathrm{Nat}$ is a well-typed covalue, so by reflexivity it is equal to itself at type Nat. Adequacy (Theorem 5.21) then ensures that $ \alpha \div [\![{\mathrm{Nat}}]\!] \subseteq [\![{deep_{\mathrm{Nat}} = deep_{\mathrm{Nat}} \div \mathrm{Nat}}]\!] $ and since $\alpha ~ [\![{\mathrm{Nat}}]\!] ~ \alpha$ (Lemma 5.22), we know specifically that $deep_{\mathrm{Nat}} ~[\![{\mathrm{Nat}}]\!]~ deep_{\mathrm{Nat}}$ . From the soundness of $[\![{\mathrm{Nat}}]\!]$ , we know that $V ~[\![{\mathrm{Nat}}]\!]~ V'$ implies , or in other words In call-by-value, the only such values that satisfy this relationship are $V = \mathrm{succ}^n\mathrm{zero}$ and $V' = \mathrm{succ}^{n'}\mathrm{zero}$ , for some n,n’ iterations of the successor. Specifically, $\mu$ -abstractions are not values in call-by-value, and the only other choices for values all lead to computations that get stuck at some unobservable command.

To see that $n = n'$ , consider what happens if $n \neq n'$ , and suppose (without loss of generality) that $n < n'$ . Here is a family of well-typed covalues that peel off n successors:

So that, for any $m \leq m'$ , $\langle {\mathrm{succ}^{m'}\mathrm{zero}} |\!| {minus_{m}} \rangle \mathrel{\mapsto\!\!\!\!\to} \langle {\mathrm{succ}^{m'-m}\mathrm{zero}} |\!| {\alpha} \rangle$ . Note again that $\alpha\div\mathrm{Nat} \vdash minus_n \div \mathrm{Nat}$ is a well-typed covalue, so that it is equal to itself by reflexivity, and thus by adequacy (Theorem 5.21) and Lemma 5.22, $minus_n ~[\![{\mathrm{Nat}}]\!]~ minus_n$ . From soundness of $[\![{\mathrm{Nat}}]\!]$ , it follows that the following inconsistent equivalence holds

which contradicts the definition of $\sim$ . Therefore, $n = n'$ , and thus $V ~[\![{\mathrm{Nat}}]\!]~ V'$ if and only if $V = V' = \mathrm{succ}^n \mathrm{zero}$ exactly.

In other words, $\mathbb{N} = [\![{\mathrm{Nat}}]\!]^{v+}$ , and so $ \mathrm{Pos}(\mathbb{N}) = \mathrm{Pos}([\![{\mathrm{Nat}}]\!]^{v+}) = \mathrm{Pos}([\![{\mathrm{Nat}}]\!]) $ by Property 5.11, and $\mathrm{Pos}([\![{\mathrm{Nat}}]\!]) = [\![{\mathrm{Nat}}]\!]$ , because $[\![{\mathrm{Nat}}]\!]$ is already a complete equality candidate.

Lemma 5.25 (Strict Destruction of Streams). Under call-by-name evaluation, $E ~[\![{\mathrm{Stream} \, A}]\!]~ E'$ if and only if $E = \mathrm{tail}^n(\mathrm{head} \, \, E_1)$ and $E' = \mathrm{tail}^n(\mathrm{head} \, \, E_1')$ for some n and $E ~[\![{A}]\!]~ E'$ . Furthermore $ [\![{\mathrm{Stream} \, A}]\!] = \mathrm{Neg}(\mathbb{S}([\![{A}]\!]))$ under call-by-value evaluation, where $\mathbb{S}([\![{A}]\!])$ is the reflexive relation on only the hereditary stream projections, i.e., the smallest binary relation such that $ (\mathrm{tail}^n(\mathrm{head} \, \, E)) ~\mathbb{S}(\mathbb{A})~ (\mathrm{tail}^n(\mathrm{head} \, \, E'))$ if and only if $E~\mathbb{A}~E'$ .

Proof Analogous to the proof for Lemma 5.24. Using the value which has the type $x : \mathrm{Stream} \, A \vdash deep_{\mathrm{Stream} \, A}$ , we can conclude that $E ~[\![{\mathrm{Stream} \, A}]\!]~ E'$ if and only if $E = \mathrm{tail}^{n}(\mathrm{head} \, \, E_0)$ and $E' = \mathrm{tail}^{n'}(\mathrm{head} \, \, E_0')$ for some $E_0 ~[\![{A}]\!]~ E_0'$ . Furthermore, it must be that $n=n'$ , because we can peel off n tail projections using the value

which derives an inconsistent equivalence $\langle {x} |\!| {\mathrm{head} \, \, E_0} \rangle \sim \langle {x} |\!| {\mathrm{tail} \, (\mathrm{tail}^{n'-n-1}(\mathrm{head} \, \, E_0'))} \rangle$ that contradicts the definition of . Therefore, $[\![{\mathrm{Stream} \, A}]\!] = \mathrm{Neg}(\mathbb{S}([\![{A}]\!]))$ .

These simpler definitions for $[\![{\mathrm{Nat}}]\!]$ and $[\![{\mathrm{Stream} \, A}]\!]$ make it possible to verify the stronger (co)inductive rules $\sigma{\mathrm{Nat}}$ and $\sigma{\mathrm{Stream}}$ , which do not place any restrictions on the kinds of properties they may prove.

Lemma 5.26 ( $\sigma{\mathrm{Nat}}$ ).

$[\![{\Gamma, x : \mathrm{Nat} \vdash \Phi}]\!]$ if and only if $[\![{\Gamma \vdash \Phi[{{\mathrm{zero}}/{x}}]}]\!]$ and $[\![{\Gamma, x : \mathrm{Nat}, \Phi \vdash \Phi[{{\mathrm{succ} \, x}/{x}}]}]\!]$ .

Proof By Lemmas 5.15 and 5.24, the meaning of $[\![{\Gamma, x : \mathrm{Nat} \vdash \Phi}]\!]$ is equivalent to:

\begin{align*} [\![{\Gamma, x : \mathrm{Nat} \vdash \Phi}]\!] &= [\![{\Gamma}]\!], x : [\![{\mathrm{Nat}}]\!] \subseteq [\![{\Phi}]\!] \\ &= [\![{\Gamma}]\!], x : \mathrm{Pos}(\mathbb{N}) \subseteq [\![{\Phi}]\!] \\ &= [\![{\Gamma}]\!], x : \mathbb{N} \subseteq [\![{\Phi}]\!] \end{align*}

Which can be proved equivalent to $[\![{\Gamma}]\!] \subseteq [\![{\Phi[{{\mathrm{zero}}/{x}}]}]\!]$ and $[\![{\Gamma}]\!], x : \mathbb{N} \subseteq [\![{\Phi[{{\mathrm{succ} \, x}/{x}}]}]\!]$ by an ordinary induction on the numeric constructions in $\mathbb{N}$ .

Lemma 5.27 ( $\sigma{\mathrm{Stream}}$ ).

$[\![{\Gamma, \alpha \div \mathrm{Stream} \, A \vdash \Phi}]\!]$ if and only if $[\![{\Gamma, \beta \div A \vdash \Phi[{{\mathrm{head} \, \beta}/{\alpha}}]}]\!]$ and $[\![{\Gamma, \alpha \div \mathrm{Stream} \, A, \Phi \vdash \Phi[{{\mathrm{tail} \, \, \alpha}/{\alpha}}]}]\!]$ .

Proof Analogous to Lemma 5.26 by duality using Lemmas 5.15 and 5.25.

Theorem 5.28 (Soundness).

If $\Gamma \vdash \Phi$ is derivable in the strong call-by-value program logic, then $[\![{\Gamma \vdash \Phi}]\!]$ is true under call-by-value evaluation. Likewise, If $\Gamma \vdash \Phi$ is derivable in the strong call-by-name program logic, then $[\![{\Gamma \vdash \Phi}]\!]$ is true under call-by-name evaluation.

Proof The same as the proof of Theorem 5.21 with one additional case for $\sigma{\mathrm{Nat}}$ in call-by-value or $\sigma{\mathrm{Stream}}$ and $\sigma{\to}$ in call-by-name.

Theorem 5.29. In the strong call-by-value program logic and operational semantics, and in the strong call-by-name program logic and operational semantics, the following hold:

  1. 1. If $\Gamma \vdash c = c' $ then $\Gamma \vdash c \approx c' $ .

  2. 2. If $\Gamma \vdash v = v' : A$ then $\Gamma \vdash v \approx v' : A$ .

  3. 3. If $\Gamma \vdash e = e' \div A$ then $\Gamma \vdash e \approx e' \div A$ .

Theorem 3.9. The strong call-by-name and call-by-value program logics are consistent.

Proof Both Theorems 3.9 and 5.29 are proved the same as Theorems 3.7 and 5.23, using the generalized Theorem 5.28 in place of Theorem 5.21.

6 Related work

Foundations and implementations of coinduction

Coinduction has been heavily used in different domains: to prove security properties of low-level code (Leroy & Rouaix, Reference Leroy, Rouaix, MacQueen and Cardelli1998; Appel & Felty, Reference Appel, Felty, Wegman and Reps2000), to prove regular expressions containments (Henglein & Nielsen, Reference Henglein and Nielsen2011), to show language equivalence of a non-deterministic finite automata (Bonchi & Pous, Reference Bonchi, Pous, Giacobazzi and Cousot2013), to reason about software-defined networks (Foster et al., Reference Foster, Kozen, Milano, Silva, Thompson, Rajamani and Walker2015), and probabilistic functional programs (Lago et al., Reference Lago, Sangiorgi, Alberti, Jagannathan and Sewell2014). The relation between coinductive reasoning and programming languages theory has been consolidated in Hur et al. (Reference Hur, Dreyer, Neis, Vafeiadis, Field and Hicks2012).

Coq is one of the few formal verifiers with a long history of native support for coinduction (Giménez, Reference Giménez, Berardi and Coppo1996; Chlipala, Reference Chlipala2013). Yet, coinductive proof development in Coq is not easy: such proofs are not checked until they are completed, which is too late for Coq’s interactive proof development. It is often said that coinductive proofs have a very different “feel.” Much work on improving the mechanization of coinduction has been done in a form of structural coinduction in the Isabelle/HOL theorem prover (Traytel et al., Reference Traytel, Popescu and Blanchette2012) with the aim to improve the ease of use (Blanchette et al., Reference Blanchette, Hölzl, Lochbihler, Panny, Popescu, Traytel, Klein and Gamboa2014, Reference Blanchette, Popescu, Traytel, Fisher and Reppy2015). There, the built-in notion of coinductive proof is based on bisimulation, and so the implementation has support to automatically derive the bisimulation relation. In contrast, here we formulate structural coinduction directly on the shape of observations—with bisimulation as just one, optional, mode of use—so bisimulation relations never arise for many proofs. Instead, the closest direct implementation of structural coinduction as presented here is the implementation of copatterns in Agda (Abel et al., Reference Abel, Pientka, Thibodeau and Setzer2013). Coinduction has also been brought to program verification in Dafny (Leino & Moskal, Reference Leino, Moskal, Jones, Pihlajasaari and Sun2014) and Liquid Haskell (Mastorou et al., Reference Mastorou, Papaspyrou and Vazou2022).

While we focus on methods of reasoning based on computation and formal classical logic, other approaches have been employed for reasoning about corecursive programs. From the domain-theoretic approach, Scott and de Bakker’s fixed-point induction (Bakker, Reference Bakker1980) is one of the early examples. However, applying fixed-point induction is not so easy because it requires knowledge of the CPO semantics of types and their properties. In its place, other lemmas such as the take lemma (Bird & Wadler, Reference Bird and Wadler1988), and its improvement, the approximation lemma (Bird, Reference Bird1998; Hutton & Gibbons, Reference Hutton and Gibbons2001), reframes the problem of observing infinite objects through a family of more familiar questions about induction on finite objects: two streams are equal if all their finite approximations are. Similarly, Mastorou et al. (Reference Mastorou, Papaspyrou and Vazou2022) encode coinduction in terms of induction by adding an index. Gibbons & Hutton (Reference Gibbons and Hutton2005) give a survey of these other methods. The formalization here, in contrast, identifies and reifies the “inductive” nature inherent in the context of coinduction to use directly in the coinductive principle without encoding or a change of representation.

Another approach to coinduction involves the hidden algebras (Goguen & Malcolm, Reference Goguen and Malcolm1999) behind coinductive modules in the object-oriented paradigm. This has been used to formulate circular coinductive proofs for object-oriented behavior (Goguen et al., Reference Goguen, Lin and Rosu2000) and concurrent processes (Popescu & Gunter, Reference Popescu and Gunter2010). Circular coinduction has been implemented in Coq (Endrullis et al., Reference Endrullis, Hendriks and Bodin2013) and generalized to a form of parameterized coinductive proofs (Hur et al., Reference Hur, Neis, Dreyer and Vafeiadis2013).

Coinductive reasoning principles

Using coinduction makes it possible to avoid working with numbers (Gordon, Reference Gordon1994). Instead, coinductive proofs are completely based on the structure of programs, analogous to bisimulation (Sangiorgi, Reference Sangiorgi2009). Our notion of strong (co)induction also allows for local reasoning about valid applications of the (co)inductive hypothesis, which leads to a compositional development of (co)inductive proofs. Similarly, Paco (Hur et al., Reference Hur, Neis, Dreyer and Vafeiadis2013) aims to aid the development of coinductive proofs through both compositionality (local, not global, correctness criteria) and incrementality (new knowledge may be accumulated as the proof is developed). We showed how the strong version of our program logic encompasses well-known principles of strong induction and bisimulation of corecursive processes.

Corecursion—and the coinductive principles to reason about them—have also been generalized to capture common patterns that occur in programming but which make structural coinduction more difficult to verify. For example, consider the following usual definition of the infinite Fibonacci stream in Haskell:

\begin{align*} fibs &= 0 : 1 : sums ~ fibs ~ (tail ~ fibs) \\ sums ~ (x:xs) ~ (y:ys) &= (x+y) : (sums ~ xs ~ ys)\end{align*}

From experience, we know this is a well-behaved infinite stream: we can access any particular number in finite time. However, the reason why is nontrivial, which can be more easily seen when translated as follows into the abstract machine language used here:

\begin{align*} \langle {fibs} |\!| {\mathrm{head} \, \, \alpha} \rangle &= \langle {0} |\!| {\alpha} \rangle \\ \langle {fibs} |\!| {\mathrm{tail}(\mathrm{head} \, \, \alpha)} \rangle &= \langle {1} |\!| {\alpha} \rangle \\ \langle {fibs} |\!| {\mathrm{tail}(\mathrm{tail} \, \, \alpha)} \rangle &= \langle {sums} |\!| {} \rangle { \mu{\beta_1}.\langle {fibs} |\!| {\beta_1} \rangle \cdot {\mu{\beta_2}.\langle {fibs} |\!| {\mathrm{tail} \, \beta_2} \rangle \cdot {\alpha} } } \\[1ex] \langle {sums} |\!| {xs \cdot ys \cdot \mathrm{head} \, \, \alpha} \rangle &= \langle {\mathrm{head} \, \, xs + \mathrm{head} \, \, ys} |\!| {\alpha} \rangle \\ \langle {sums} |\!| {xs \cdot ys \cdot \mathrm{tail} \, \, \alpha} \rangle &= \langle {sums} |\!| {} \rangle { \mu{\beta_1}.\langle {xs} |\!| {\mathrm{tail} \, \beta_1} \rangle \cdot {\mu{\beta_2}.{\langle {xs} |\!| {\mathrm{tail} \, \beta_2} \rangle} \cdot \alpha} } \end{align*}

The trouble is that fibs’s coinductive case for $\mathrm{tail}(\mathrm{tail} \, \, \alpha)$ recursively references back to fibs with some syntactically unknown observers— $\beta_1$ and $\mathrm{tail} \, \beta_2$ , respectively—which might be far larger than the smaller case $\mathrm{tail} \, \, \alpha$ . Despite this, the reason fibs is well-founded has to do with the helper function sums: the application $\langle {sums} |\!| {xs \cdot ys \cdot \alpha} \rangle$ will replicate a stream projection of exactly $\alpha$ ’s length (i.e., the same number of tail projections) to both xs and ys. A function with this special property is known as “friendly” in Isabelle/HOL (Blanchette et al., Reference Blanchette, Popescu, Traytel, Fisher and Reppy2015, Reference Blanchette, Bouzy, Lochbihler, Popescu and Traytel2017) and “abstemious” in Dafny (Leino & Moskal, Reference Leino, Moskal, Jones, Pihlajasaari and Sun2014).

Since justifying these kinds of definitions are well-founded is already complex, reasoning about them is even more so. For example, consider this product function of two streams from Blanchette et al. (Reference Blanchette, Popescu, Traytel, Fisher and Reppy2015), also defined in terms of sums:

\begin{align*} prods ~ (x:xs) ~ (y:ys) &= (x \times y) : (sums ~ (prods ~ (x:xs) ~ ys) ~ (prods ~ xs ~ (y:ys)))\end{align*}

Both the sum and product of two streams should be commutative. It is straightforward enough to show commutativity of sums— $sums~xs~ys = sums~ys~xs$ —directly because it is defined only in terms of itself and plain addition. However, prod is defined in terms of sums, which gets in the way of a bisimulation argument. Isabelle/HOL supports the notion of coinduction “up to” (Blanchette et al., Reference Blanchette, Popescu, Traytel, Fisher and Reppy2015, Reference Blanchette, Bouzy, Lochbihler, Popescu and Traytel2017) in order to better automate the bisimulation relation for these kinds of programs. We conjecture that the notion of structural coinduction developed here—which does not require bisimulation at all—can sidestep the issue entirely in this kind of example. In particular, translating the above function to the abstract machine language looks like:

\begin{align*} \langle {prods} |\!| {xs \cdot ys \cdot \mathrm{head} \, \, \alpha} \rangle &= \langle {x \times y} |\!| {\alpha} \rangle \\ \langle {prods} |\!| {xs \cdot ys \cdot \mathrm{tail} \, \, \alpha} \rangle &= \langle {sums} |\!| {} \rangle { \mu{\beta_1}.\langle {prods} |\!| {xs \cdot \mathrm{tail} \, ys \cdot \beta_1} \rangle \cdot { \mu{\beta_2}.\langle {prods} |\!| {\mathrm{tail} \, xs \cdot ys \cdot \beta_2} \rangle \cdot {\alpha} } }\end{align*}

Suppose we accept this definition as well-founded because of sums’ properties—either marking sums “friendly” or “abstemious” as above, or using sized types (Abel, Reference Abel2006). Then we know that $\beta_1$ and $\beta_2$ will always be instantiated by another observation no bigger than $\alpha$ (i.e., we know $\beta_1 \leq \alpha$ and $\beta_2 \leq \alpha$ according to Section 4). We could then proceed to prove

\begin{align*} \langle {prod} |\!| {xs \cdot ys \cdot \alpha} \rangle &= \langle {prod} |\!| {ys \cdot xs \cdot \alpha} \rangle\end{align*}

using our notion of strong coinduction on $\alpha$ . The main case for $\alpha = \mathrm{tail} \, \, \alpha'$ would then look like the following, with a coinductive hypothesis (CIH) applicable to any observation of $\alpha'$ s size or smaller, which includes the $\beta_1 \leq \alpha'$ and $\beta_2 \leq \alpha'$ instantiated by sums:

\begin{align*} & \langle {prods} |\!| {xs \cdot ys \cdot \mathrm{tail} \, \, \alpha'} \rangle \\ &= \langle {sums} |\!| {} \rangle { \mu{\beta_1}.\langle {prods} |\!| {xs \cdot \mathrm{tail} \, ys \cdot \beta_1} \rangle \cdot { \mu{\beta_2}.\langle {prods} |\!| {\mathrm{tail} \, xs \cdot ys \cdot \beta_2} \rangle \cdot {\alpha'} } } &(prods \text{ def.}) \\ &= \langle {sums} |\!| {} \rangle { \mu{\beta_1}.\langle {prods} |\!| {\mathrm{tail} \, ys \cdot xs \cdot \beta_1} \rangle \cdot { \mu{\beta_2}.\langle {prods} |\!| {\mathrm{tail} \, xs \cdot ys \cdot \beta_2} \rangle \cdot {\alpha'} } } &(CIH, \beta_1 \leq \alpha') \\ &= \langle {sums} |\!| {} \rangle { \mu{\beta_1}.\langle {prods} |\!| {\mathrm{tail} \, ys \cdot xs \cdot \beta_1} \rangle \cdot { \mu{\beta_2}.\langle {prods} |\!| {ys \cdot \mathrm{tail} \, xs \cdot \beta_2} \rangle \cdot {\alpha'} } } &(CIH, \beta_2 \leq \alpha') \\ &= \langle {sums} |\!| {} \rangle { \mu{\beta_2}.\langle {prods} |\!| {ys \cdot \mathrm{tail} \, xs \cdot \beta_2} \rangle \cdot { \mu{\beta_1}.\langle {prods} |\!| {\mathrm{tail} \, ys \cdot xs \cdot \beta_1} \rangle \cdot {\alpha'} } } &(sums \text{ commut.}) \\ &= \langle {prods} |\!| {ys \cdot xs \cdot \mathrm{tail} \, \, \alpha'} \rangle &(prods \text{ def.})\end{align*}

Logical relation and program equality

Our overall approach to proving properties about programs using syntactic rules (the program logic) that are then shown to be part of a consistent-by-definition operational model follows the general approach of logical relations (Statman, Reference Statman1985), Tait’s method (Tait, Reference Tait1967), and realizability (Kleene, Reference Kleene1945). However, we cannot use Tait’s original method as formulated because types in our language classify both terms and coterms. Instead, we use the formulation of logical relations based on orthogonality between two opposing sets, which has been developed in multiple places including linear logic (Girard, Reference Girard1987), classical realizability (Krivine, Reference Krivine2005), and $\top\top$ -closed relations (Pitts, Reference Pitts2000, Reference Pitts1997a), and symmetric candidates (Barbanera & Berardi, Reference Barbanera and Berardi1994). A key feature of our model is the built-in notion that types are first modeled by a chosen set of values (for positive types) or covalues (for negative types). This generation from (co)values comes from a study of polarity and focusing in linear logic (Munch-Maccagnoni, Reference Munch-Maccagnoni2009), which makes similar distinctions between call-by-value and call-by-name interpretations of types as call-by-push-value (Levy, Reference Levy2001).

We also make use of the notion of candidates—an initial definition describing all possible models of types, whose interpretation will come later—as part of our proof. Traditionally, the candidate-based approach is used to prove properties about programs in languages that have impredicative polymorphism like system F (Girard, Reference Girard1972). Here, we use the same idea to construct inductive and coinductive types by quantifying over all their possible approximations: either smaller subtypes in the case of induction or larger supertypes in the case of coinduction. These approximations are then assembled into their least or greatest fixed points using both the Knaster–Tarski (Knaster, Reference Knaster1928; Tarski, Reference Tarski1955) and Kleene (Kleene, Reference Kleene1971) constructions—which are equivalent by Lemma 5.18—using the lattice structure present in the logical relations model corresponding to intersection and union types (Coppo & Dezani-Ciancaglini, Reference Coppo and Dezani-Ciancaglini1978; Sallé, Reference Sallé, Ausiello and Böhm1978; Pottinger, Reference Pottinger, Seldin and Hindley1980).

7 Conclusion

This paper defines a language for providing a computational foundation of (co)inductive reasoning principles which brings out their duality. The impact of the evaluation strategy is also illustrated. Whereas induction does not fully work in call-by-name, co-induction has the same issues in call-by-value. The (co)inductive principles are derived from the definition of types in terms of construction or destruction, using control flow instead of bisimulation to guide the coinductive hypothesis. In the end, the logical dualities in computation—between data and codata; information flow and control flow—provide a unified framework for using and reasoning with (co)inductive types.

As future work, we would like to formalize more advanced notions of coinduction and bisimilarity (Pous & Sangiorgi, Reference Pous, Sangiorgi, Sangiorgi and Rutten2012) that relax the constraint that the processes need to proceed completely in sync, thus allowing one to compare processes that"almost" compute in the same way. We would also like to show that Paco’s coinductive principles (Hur et al., Reference Hur, Neis, Dreyer and Vafeiadis2013) can also be encoded as an application of strong coinduction—giving a computational model for its proofs—where accumulated knowledge may be represented as the accumulator of a corecursive process.

Acknowledgments

This material is based upon work supported by the National Science Foundation under Grant No. 2245516.

Conflicts of interest

None.

Footnotes

1 $FV(\Phi)$ stands for the free variables in a proposition $\Phi$ , and $FV(\Delta, \Phi)$ is defined as $FV(\Delta) \cup FV(\Phi)$ with $FV(\bullet) = \emptyset$ .

2 Weakening follows by an induction on the given typing derivation and allowing for a larger $\Gamma$ in each axiom.

3 Note that while $evens~(merge~s_1~s_2)$ and $odds~(merge~s_1~s_2)$ are not syntactically values, they both simplify to a value in both call-by-value and call-by-name. So we can get the equivalent productive property by simplifying the two equations, applying $\omega{\mathrm{Stream}}$ , and then expanding back to this form.

4 Note that we denote membership of a binary relation $\mathbb{R} \subseteq \mathbb{X} \times \mathbb{Y}$ as an infix operation $x ~\mathbb{R}~ y$ instead of set membership notation $(x,y) \in \mathbb{R}$ . Furthermore, we use $Y^2$ as shorthand for the product $Y \times Y$

References

Abel, A. (2006) A Polymorphic Lambda Calculus with Sized Higher-Order Types. Ph.D. thesis, Ludwig-Maximilians-Universität München.Google Scholar
Abel, A., Pientka, B., Thibodeau, D. & Setzer, A. (2013) Copatterns: Programming infinite structures by observations. In Proceedings of the 40th Annual ACM SIGPLAN-SIGACT Symposium on Principles of Programming Languages. POPL’13. ACM, pp. 27–38.10.1145/2429069.2429075CrossRefGoogle Scholar
Appel, A. W. & Felty, A. P. (2000) A semantic model of types and machine instuctions for proof-carrying code. In Proceedings of the 27th ACM SIGPLAN-SIGACT Symposium on Principles of Programming Languages, Boston, Massachusetts, USA, January 19–21, 2000, POPL 2000, Wegman, M. N. & Reps, T. W. (eds), pp. 243–253. ACM.10.1145/325694.325727CrossRefGoogle Scholar
Bakker, J. W. d. (1980) Mathematical Theory of Program Correctness. Prentice-Hall, Inc.Google Scholar
Barbanera, F. & Berardi, S. (1994) A symmetric lambda calculus for “classical” program extraction. In Theoretical Aspects of Computer Software, International Conference TACS’94, Sendai, Japan, April 19–22, 1994, Proceedings, pp. 495515.10.1007/3-540-57887-0_112CrossRefGoogle Scholar
Barwise, J. & Moss, L. (1997) Vicious circles on the mathematics of non-wellfounded phenomena. J. Symb. Logic 62(3), 10391040.Google Scholar
Bird, R. (1998) Introduction to Functional Programming Using Haskell, 2nd ed. Prentice-Hall, Inc.Google Scholar
Bird, R. & Wadler, P. (1988) An Introduction to Functional Programming. Prentice-Hall, Inc.Google Scholar
Blanchette, J. C., Bouzy, A., Lochbihler, A., Popescu, A. & Traytel, D. (2017) Friends with benefits - implementing corecursion in foundational proof assistants. In Programming Languages and Systems - 26th European Symposium on Programming, ESOP 2017, Held as Part of the European Joint Conferences on Theory and Practice of Software, ETAPS 2017, Uppsala, Sweden, April 22-29, 2017, Proceedings, Yang, H. (ed). Lecture Notes in Computer Science, vol. 10201. Springer, pp. 111–140.Google Scholar
Blanchette, J. C., Hölzl, J., Lochbihler, A., Panny, L., Popescu, A. & Traytel, D. (2014) Truly modular (co)datatypes for isabelle/hol. In Interactive Theorem Proving - 5th International Conference, ITP 2014, Held as Part of the Vienna Summer of Logic, VSL 2014, Vienna, Austria, July 14–17, 2014. Proceedings, Klein, G. & Gamboa, R. (eds). Lecture Notes in Computer Science, vol. 8558. Springer, pp. 93–110.10.1007/978-3-319-08970-6_7CrossRefGoogle Scholar
Blanchette, J. C., Popescu, A. & Traytel, D. (2015) Foundational extensible corecursion: a proof assistant perspective. In Proceedings of the 20th ACM SIGPLAN International Conference on Functional Programming, ICFP 2015, Vancouver, BC, Canada, September 1–3, 2015, Fisher, K. & Reppy, J. H. (eds). ACM, pp. 192–204.10.1145/2784731.2784732CrossRefGoogle Scholar
Bonchi, F. & Pous, D. (2013) Checking NFA equivalence with bisimulations up to congruence. In The 40th Annual ACM SIGPLAN-SIGACT Symposium on Principles of Programming Languages, POPL ’13, Rome, Italy - January 23–25, 2013, Giacobazzi, R. & Cousot, R. (eds). ACM, pp. 457–468.10.1145/2429069.2429124CrossRefGoogle Scholar
Burstall, R. M. (1969) Proving properties of programs by structural induction. Comput. J. 12(1), 4148.10.1093/comjnl/12.1.41CrossRefGoogle Scholar
Chlipala, P. (2013) Certified Programming with Dependent Types: A Pragmatic Introduction to the Coq Proof Assistant. MIT Press.10.7551/mitpress/9153.001.0001CrossRefGoogle Scholar
Coppo, M. & Dezani-Ciancaglini, M. (1978) A new type assignment for λ-terms. Arch. Math. Log. 19(1), 139156.10.1007/BF02011875CrossRefGoogle Scholar
Curien, P.-L. & Herbelin, H. (2000) The duality of computation. In Proceedings of the Fifth ACM SIGPLAN International Conference on Functional Programming. ICFP’00. ACM, pp. 233243.10.1145/351240.351262CrossRefGoogle Scholar
Downen, P. & Ariola, Z. M. (2018) A tutorial on computational classical logic and the sequent calculus. J. Funct. Program. 28, e3.10.1017/S0956796818000023CrossRefGoogle Scholar
Downen, P. & Ariola, Z. M. (2023) Classical (co)recursion: Mechanics. J. Funct. Program. 33, e4.10.1017/S0956796822000168CrossRefGoogle Scholar
Downen, P., Ariola, Z. M. & Ghilezan, S. (2019) The duality of classical intersection and union types. Fund. Inf. 170(1-3), 3992.Google Scholar
Downen, P., Johnson-Freyd, P. & Ariola, Z. M. (2015) Structures for structural recursion. In Proceedings of the 20th ACM SIGPLAN International Conference on Functional Programming. ICFP’15. ACM, pp. 127139.10.1145/2784731.2784762CrossRefGoogle Scholar
Downen, P., Johnson-Freyd, P. & Ariola, Z. M. (2020) Abstracting models of strong normalization for classical calculi. J. Logical Algebraic Methods Program. 111, 100512.10.1016/j.jlamp.2019.100512CrossRefGoogle Scholar
Endrullis, J., Hendriks, D. & Bodin, M. (2013) Circular coinduction in Coq using bisimulation-up-to techniques. In Proceedings of the 4th International Conference on Interactive Theorem Proving. ITP’13. Springer-Verlag, pp. 354–369.10.1007/978-3-642-39634-2_26CrossRefGoogle Scholar
Foster, N., Kozen, D., Milano, M., Silva, A. & Thompson, L. (2015) A coalgebraic decision procedure for netkat. In Proceedings of the 42nd Annual ACM SIGPLAN-SIGACT Symposium on Principles of Programming Languages, POPL 2015, Mumbai, India, January 15–17, 2015, Rajamani, S. K. & Walker, D. (eds). ACM, pp. 343355.10.1145/2676726.2677011CrossRefGoogle Scholar
Gibbons, J. & Hutton, G. (2005) Proof methods for corecursive programs. Fundamenta Informaticae 66(04), 353366.Google Scholar
Giménez, E. (1996) An application of co-inductive types in coq: Verification of the alternating bit protocol. In Types for Proofs and Programs, Berardi, S. & Coppo, M. (eds). Springer Berlin Heidelberg, pp. 135–152.10.1007/3-540-61780-9_67CrossRefGoogle Scholar
Girard, J. Y. (1972) Interprétation fonctionnelle et elimination des coupures de l’arithmétique d’ordre supérieur. These d’état, Université de Paris 7.Google Scholar
Girard, J.-Y. (1987) Linear logic. Theoret. Comput. Sci. 50(1), 1101.10.1016/0304-3975(87)90045-4CrossRefGoogle Scholar
Gödel, K. (1980) On a hitherto unexploited extension of the finitary standpoint. J. Philos. Logic 9(2), 133142.10.1007/BF00247744CrossRefGoogle Scholar
Goguen, J., Lin, K. & Rosu, G. (2000) Circular coinductive rewriting. In Proceedings, Automated Software Engineering’00, 07.10.1109/ASE.2000.873657CrossRefGoogle Scholar
Goguen, J. A. & Malcolm, G. (1999) Hidden coinduction: Behavioural correctness proofs for objects. Math. Struct. Comput. Sci. 9(3), 287319.10.1017/S0960129599002777CrossRefGoogle Scholar
Gordon, A. (1994) A tutorial on co-induction and functional programming. In Proceedings of the 1994 Glasgow Workshop on Functional Programming, Ayr, Scotland. Springer London, pp. 78–95.Google Scholar
Gordon, M. (2017) Corecursion and coinduction: what they are and how they relate to recursion and induction. https://www.cl.cam.ac.uk/archive/mjcg/Blog/WhatToDo/Coinduction.pdf.Google Scholar
Hagino, T. (1987) A typed lambda calculus with categorical type constructors. In Category Theory and Computer Science. Springer Berlin Heidelberg, pp. 140157.10.1007/3-540-18508-9_24CrossRefGoogle Scholar
Harper, R. (2016) Practical Foundations for Programming Languages, 2nd ed. Cambridge University Press.10.1017/CBO9781316576892CrossRefGoogle Scholar
Henglein, F. & Nielsen, L. (2011) Regular expression containment: Coinductive axiomatization and computational interpretation. In Proceedings of the 38th Annual ACM SIGPLAN-SIGACT Symposium on Principles of Programming Languages. POPL’11. Association for Computing Machinery, pp. 385–398.10.1145/1926385.1926429CrossRefGoogle Scholar
Herbelin, H. (2005) C’est maintenant qu’on calcule: Au coeur de la dualité. Habilitation thesis, Université Paris 11.Google Scholar
Hur, C., Dreyer, D., Neis, G. & Vafeiadis, V. (2012) The marriage of bisimulations and kripke logical relations. In Proceedings of the 39th ACM SIGPLAN-SIGACT Symposium on Principles of Programming Languages, POPL 2012, Philadelphia, Pennsylvania, USA, January 22–28, 2012, Field, J. & Hicks, M. (eds). ACM, pp. 59–72.10.1145/2103656.2103666CrossRefGoogle Scholar
Hur, C.-K., Neis, G., Dreyer, D. & Vafeiadis, V. (2013) The power of parameterization in coinductive proof. In Proceedings of the 40th Annual ACM SIGPLAN-SIGACT Symposium on Principles of Programming Languages. POPL’13. Association for Computing Machinery, pp. 193–206.10.1145/2429069.2429093CrossRefGoogle Scholar
Hutton, G. & Gibbons, J. (2001) The generic approximation lemma. Inf. Process. Lett. 79(08), 197201.10.1016/S0020-0190(00)00220-9CrossRefGoogle Scholar
Kleene, S. C. (1945) On the interpretation of intuitionistic number theory. J. Symb. Logic 10(4), 109124.10.2307/2269016CrossRefGoogle Scholar
Kleene, S. C. (1971) Introduction to Metamathematics. Bibliotheca Mathematica, a Series of Monographs on Pure and. Wolters-Noordhoff.Google Scholar
Knaster, B. (1928) Un theoreme sur les functions d’ensembles. Ann. Soc. Polon. Math. 6, 133134.Google Scholar
Kozen, D. & Silva, A. (2017) Practical coinduction. Math. Struct. Comput. Sci. 27(7), 11321152.10.1017/S0960129515000493CrossRefGoogle Scholar
Krivine, J.-L. (2005) Realizability in classical logic. In Interactive Models of Computation and Program Behaviour, vol. 27. Société Mathématique de France, pp. 197–229.Google Scholar
Lago, U. D., Sangiorgi, D. & Alberti, M. (2014) On coinductive equivalences for higher-order probabilistic functional programs. In The 41st Annual ACM SIGPLAN-SIGACT Symposium on Principles of Programming Languages, POPL ’14, San Diego, CA, USA, January 20–21, 2014, Jagannathan, S. and Sewell, P. (eds). ACM, pp. 297–308.Google Scholar
Leino, K. R. M. & Moskal, M. (2014) Co-induction simply. In FM 2014: Formal Methods, Jones, C., Pihlajasaari, P.& Sun, J. (eds). Springer International Publishing, pp. 382398.10.1007/978-3-319-06410-9_27CrossRefGoogle Scholar
Leroy, X. & Rouaix, F. (1998) Security properties of typed applets. In POPL’98, Proceedings of the 25th ACM SIGPLAN-SIGACT Symposium on Principles of Programming Languages, San Diego, CA, USA, January 19–21, 1998, MacQueen, D. B. & Cardelli, L. (eds). ACM, pp. 391403.10.1145/268946.268979CrossRefGoogle Scholar
Levy, P. B. (2001) Call-By-Push-Value. PhD thesis, Queen Mary and Westfield College, University of London.Google Scholar
Mastorou, L., Papaspyrou, N. & Vazou, N. (2022) Coinduction inductively: Mechanizing coinductive proofs in liquid haskell. In Proceedings of the 15th ACM SIGPLAN International Haskell Symposium. Haskell 2022. Association for Computing Machinery, pp. 1–12.10.1145/3546189.3549922CrossRefGoogle Scholar
Munch-Maccagnoni, G. (2009) Focalisation and classical realisability. In Computer Science Logic, 23rd international Workshop, CSL 2009, 18th Annual Conference of the EACSL, Coimbra, Portugal, September 7–11, 2009. Proceedings, pp. 409423.10.1007/978-3-642-04027-6_30CrossRefGoogle Scholar
Pierce, B. C. (2002) Types and Programming Languages, 1st ed. The MIT Press.Google Scholar
Pitts, A. (1997a) A note on logical relations between semantics and syntax. Logic J. IGPL 5(4), 589601.10.1093/jigpal/5.4.589CrossRefGoogle Scholar
Pitts, A. M. (1997b) Operationally-based theories of program equivalence. Semant. Logics Comput. 14, 241.10.1017/CBO9780511526619.007CrossRefGoogle Scholar
Pitts, A. M. (2000) Parametric polymorphism and operational equivalence. Math. Struct. Comput. Sci. 10(3), 321359.10.1017/S0960129500003066CrossRefGoogle Scholar
Popescu, A. & Gunter, E. L. (2010) Incremental pattern-based coinduction for process algebra and its Isabelle formalization. In Proceedings of the 13th International Conference on Foundations of Software Science and Computational Structures. FOSSACS’10. Springer-Verlag, pp. 109–127.10.1007/978-3-642-12032-9_9CrossRefGoogle Scholar
Pottinger, G. (1980) A type assignment for the strongly normalizable $\lambda$ -terms. In To H. B. Curry: Essays on Combinatory Logic, Lambda Calculus and Formalism, Seldin, J. P. & Hindley, J. R. (eds). Academic Press, pp. 561–577.Google Scholar
Pous, D. & Sangiorgi, D. (2012) Enhancements of the bisimulation proof method. In Advanced Topics in Bisimulation and Coinduction, Sangiorgi, D. & Rutten, J. (eds). Cambridge University Press.Google Scholar
Rutten, J. (2019) The Method of Coalgebra: Exercises in Coinduction. Amsterdam, The Netherlands: CWI.Google Scholar
Sallé, P. (1978) Une extension de la théorie des types en lambda-calcul. In Fifth International Conference on Automata, Languages and Programming, Ausiello, G. & Böhm, C. (eds). Lecture Notes in Computer Science, vol. 62. Springer-Verlag, pp. 398–410.Google Scholar
Sangiorgi, D. (2009) On the origins of bisimulation and coinduction. ACM Trans. Program. Lang. Syst. 31(4), 141.10.1145/1516507.1516510CrossRefGoogle Scholar
Statman, R. (1985) Logical relations and the typed $\lambda$ -calculus. Inf. Control 65(2-3), 8597.10.1016/S0019-9958(85)80001-2CrossRefGoogle Scholar
Tait, W. W. (1967) Intensional interpretations of functionals of finite type I. J. Symb. Logic 32(2), 198212.10.2307/2271658CrossRefGoogle Scholar
Tarski, A. (1955) A lattice-theoretical fixpoint theorem and its applications. Pac. J. Math. 5(2), 285309.10.2140/pjm.1955.5.285CrossRefGoogle Scholar
Traytel, D., Popescu, A. & Blanchette, J. C. (2012) Foundational, compositional (co)datatypes for higher-order logic: Category theory applied to theorem proving. In Proceedings of the 27th Annual IEEE Symposium on Logic in Computer Science, LICS 2012, Dubrovnik, Croatia, June 25–28, 2012. IEEE Computer Society, pp. 596–605.10.1109/LICS.2012.75CrossRefGoogle Scholar
Figure 0

Fig. 1. Syntax and semantics of the uniform, (co)recursive abstract machine.

Figure 1

Fig. 2. Type system of the uniform, (co)recursive abstract machine.

Figure 2

Fig. 3. Syntactic sugar in the abstract machine language.

Figure 3

Fig. 4. Intensional equational theory of computation.

Figure 4

Fig. 5. Extensional program logic.

Figure 5

Fig. 6. Unrestricted coinduction rules $\sigma{\to}$, $\sigma{\mathrm{Stream}}$, and unrestricted induction rule $\sigma{\mathrm{Nat}}$.

Figure 6

Fig. 7. Model of observational equivalence in the abstract machine.

Submit a response

Discussions

No Discussions have been published for this article.