Controlling Effects

Andrzej Filinski May 1996

CMU–CS–96–119

School of Computer Science Carnegie Mellon University Pittsburgh, PA 15213

Submitted in partial fulfillment of the requirements for the degree of Doctor of Philosophy.

Thesis Committee:

Robert Harper, Co-Chair John Reynolds, Co-Chair Stephen Brookes Gordon Plotkin, University of Edinburgh

Copyright \begin{matrix}c \\ ◯ \end{matrix}1996 Andrzej Filinski

This research was sponsored in part by the Defense Advanced Research Projects Agency, CSTO, under the title “The Fox Project: Advanced Development of Systems Software”, under Contract F19628-95-C-0050. The research was also partially sponsored by the National Science Foundation under Grant No. CCR-94-09997.

The views and conclusions contained in this document are those of the author and should not be interpreted as representing the official policies, either expressed or implied, of the Defense Advanced Research Projects Agency, the National Science Foundation, or the U.S. Government.

Keywords: monads, continuations, computational effects, monadic reflection, logical relations, Scheme, ML.

Abstract

Many computational effects, such as exceptions, state, or nondeterminism, can be conveniently specified in terms of monads. We investigate a technique for uniformly adding arbitrary such effects to ML-like languages, without requiring any structural changes to the programs themselves. Instead, we use monadic reflection, a new language construct for explicitly converting back and forth between representations of effects as behavior and as data.

Using monadic reflection to characterize concisely all effects expressible with a given monad, we can give a precise meaning to the notion of simulating one effect by another, more general one. We isolate a simple condition allowing such a simulation, and in particular show that any monadic effect can be simulated by a continuation monad. In other words, under relatively mild assumptions on the base language (allowing formation of a suitably large answer type), control becomes a universal effect.

Concluding the development, we show that this universal effect can itself be explicitly implemented in terms of only standard first-class continuations (call/cc) and a piece of global state. This means that we can specify an effect such as nondeterminism abstractly, in terms of result lists, then directly obtain from this description a nondeterministic-choice operator performing imperatively-implemented backtracking. We include a full realization of the general construction in Standard ML of New Jersey, and give several programming examples.

Acknowledgments

I wish to thank my advisors, Robert Harper and John Reynolds, for their guidance in refining my informal ideas into mathematically quantifiable form, and for suffering through too many early drafts. Without their insistence on rigor and concreteness, I might have finished sooner, but probably with an ugly hole or two in the formal development. Any flaws that may remain in this final version are, of course, my own. Thanks are also due to my other committee members, Stephen Brookes and Gordon Plotkin, who each contributed useful insights.

My thinking and intuition was deeply influenced by a number of graduate courses I attended at CMU, especially those taught by Stephen Brookes, Robert Harper, Peter Lee, Frank Pfenning, and John Reynolds; I found that each of them gave me something that I could put to good use in some aspect of this thesis.

Several people provided very helpful comments on a previous version of this work, including notably Olivier Danvy, Matthias Felleisen, Julia Lawall, Greg Morrisett, Amr Sabry, and Phil Wadler. Many others, too numerous to mention, provided bits and pieces of advice or insights that helped me get one detail or another just right.

For their prompt and effective help with various administrative hassles, I want to thank Sharon Burks, Catherine Copetas, Monika Lekuse, Linda Melville, and Karen Olack; they saved me countless hours of fighting with practical problems when I could least afford the time.

Finally, I am grateful to a number of friends who offered encouragement and support throughout this endeavor, including Lars Birkedal, Prasad Chalasani, Rowan Davies, Kevin Lynch, Karoline Malmkjær, Greg Morrisett, Mark Wheeler, Chi Wong, and most especially Olivier Danvy and Chen Lee. And, of course, my greatest thanks go to my parents for their unwavering faith in me.

Andrzej Filinski

Edinburgh, Scotland May 1996

Contents

1 Introduction 1

1.1 Effects in functional languages . . . . . . . . . . . . . . . . . . . . . . . . 1 1.1.1 “Effects as data” . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 1.1.2 “Effects as behavior” . . . . . . . . . . . . . . . . . . . . . . . . . 4 1.1.3 A unified view . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6

1.2 Monads and monadic reflection . . . . . . . . . . . . . . . . . . . . . . . 9 1.3 Overview of the thesis . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13

2 Programming with Monadic Effects 15

2.1 The base language . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 2.1.1 Terminology . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 2.1.2 The base syntax . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 2.1.3 A denotational semantics . . . . . . . . . . . . . . . . . . . . . . . 19 2.1.4 Generalized let . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 2.1.5 Equational properties . . . . . . . . . . . . . . . . . . . . . . . . . 24 2.1.6 Encodings of implicitly-sequenced languages . . . . . . . . . . . . 27

2.2 Monads in a computational setting . . . . . . . . . . . . . . . . . . . . . 29 2.2.1 A framework for effects . . . . . . . . . . . . . . . . . . . . . . . . 29 2.2.2 Rigidity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 2.2.3 Definable monads . . . . . . . . . . . . . . . . . . . . . . . . . . . 34

2.3 Extending the language with effects . . . . . . . . . . . . . . . . . . . . . 37 2.3.1 The monadic translation . . . . . . . . . . . . . . . . . . . . . . . 37 2.3.2 Induced equational theory . . . . . . . . . . . . . . . . . . . . . . 41

2.4 Related work . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43

3 Relating Effects 45

3.1 Simulating monadic effects . . . . . . . . . . . . . . . . . . . . . . . . . . 45 3.1.1 Monad morphisms . . . . . . . . . . . . . . . . . . . . . . . . . . 45 3.1.2 The variant translation . . . . . . . . . . . . . . . . . . . . . . . . 49

3.2 The proof setting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53 3.2.1 The implementation language . . . . . . . . . . . . . . . . . . . . 53 3.2.2 Admissible relations . . . . . . . . . . . . . . . . . . . . . . . . . 55 3.2.3 Computation-extension of relations . . . . . . . . . . . . . . . . . 58

3.3 The simulation proof . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59 3.3.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60 3.3.2 Relating standard terms . . . . . . . . . . . . . . . . . . . . . . . 61 3.3.3 Relating computational structure . . . . . . . . . . . . . . . . . . 65 3.3.4 Relating monads to continuation-passing . . . . . . . . . . . . . . 68 3.3.5 Factorizing the variant translation . . . . . . . . . . . . . . . . . . 73 3.3.6 Induced relational correspondence . . . . . . . . . . . . . . . . . . 77

3.4 Related work . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78

4 Implementing Continuation-Effects 80 4.1 Continuation-reflection and composable continuations . . . . . . . . . . . 81 4.2 Level-erasure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84 4.3 Composable continuations from escapes and state . . . . . . . . . . . . . 90

4.3.1 Re-tying the recursive knot . . . . . . . . . . . . . . . . . . . . . 92 4.3.2 The continuation-state language . . . . . . . . . . . . . . . . . . . 96

4.4 Putting it all together . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99 4.5 ML implementation and examples . . . . . . . . . . . . . . . . . . . . . . 103

4.5.1 Composable continuations . . . . . . . . . . . . . . . . . . . . . . 103 4.5.2 Monadic reflection . . . . . . . . . . . . . . . . . . . . . . . . . . 104 4.5.3 Example: exceptions . . . . . . . . . . . . . . . . . . . . . . . . . 106 4.5.4 Example: state . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108 4.5.5 Example: nondeterminism . . . . . . . . . . . . . . . . . . . . . . 109 4.5.6 Example: probability . . . . . . . . . . . . . . . . . . . . . . . . . 110 4.5.7 Example: continuations . . . . . . . . . . . . . . . . . . . . . . . 112

4.6 Related work . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112

5 Conclusions 114 5.1 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114 5.2 Future work . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116 5.3 Closing remarks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117

A Properties of the Predomain Model 118 A.1 Recursive type definitions . . . . . . . . . . . . . . . . . . . . . . . . . . 118 A.2 Admissible relations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122 A.3 Isomorphisms of recursive types . . . . . . . . . . . . . . . . . . . . . . . 125 A.4 Invariant relations over recursive types . . . . . . . . . . . . . . . . . . . 128

Bibliography 134

Chapter 1

Introduction

In this chapter, we cover some general background and motivation for the work described in this document. After a brief discussion of two popular conceptual models for programming with computational effects, we introduce a new approach that in many ways combines the best aspects of both. We give an informal overview of this approach and sketch the concrete results obtained in the following chapters.

1.1 Effects in functional languages

An important topic in the field of programming language semantics is the study of computational effects. Informally, an effect is any deviation from the intuitive characterization of a program fragment as representing a simple function from inputs to outputs. Examples are numerous, including such familiar concepts as partiality, exceptions, state, computational complexity, I/O, nondeterminism, and concurrency.

The treatment of effects is particularly interesting in the context of modern functional programming languages, such as Standard ML or Haskell. Such languages have relatively simple and tractable mathematical descriptions, amenable to a formal analysis. In fact, their basic model of computation is precisely the definition and evaluation of functions, as opposed to sequential execution of program instructions.

But even though most aspects of functional programming can indeed be usefully captured with this simple declarative model, the natural formulation of many non-trivial programming tasks still tends to involve occasional uses of “imperative” concepts – whether for convenience in expressing an algorithm, or for interaction with the outside world.

The challenge to the semanticist is thus to admit the possibility of effects, while retaining as many as possible of the appealing properties of functional programming. This problem, of course, is not new; but somewhat surprisingly, two distinct schools of thought have evolved on how best to proceed, exemplified by the treatment of effects in “purely functional” languages, such as Miranda or Haskell, versus “algorithmic functional” ones, such as Scheme or ML.

The remainder of this section briefly presents and contrasts these two approaches, then introduces the basic thesis underlying this dissertation: that we can integrate key ideas from each framework to obtain a model of specifying and using effects that combines the best of both worlds.

1.1.1 “Effects as data”

When the basic ideas of denotational semantics were originally proposed, a significant challenge was to demonstrate that a very abstract mathematical model of computation based on “pure” functions could adequately model some apparently very non-functional but (at least at that time) important constructs in existing programming languages. Perhaps the most striking example of this was the use of continuation functions for modeling unstructured control constructs (gotos) [SW74, Rey93]; simpler techniques sufficed for concepts such as state or exceptions.

There is a close similarity between the metalanguage of denotational semantics (a concise notation for specifying continuous functions between domains) and actual functional programming languages. This has the fortunate consequence that often an appropriately expressed denotational definition can be directly executed to get an interpreter for the defined language [Rey72]. And in fact, many of the techniques pioneered in denotational semantics were quickly adopted for functional programs that were not in any reasonable sense language processors.

Specifically, the denotational representation of almost every computational effect leads to a characteristic pattern or style in functional programs using that effect. For example, a global store can be modeled functionally by passing an additional store argument to every function, together with returning from each function the possibly updated store; the resulting specification is commonly said to be expressed in state-passing style.

Similarly, exceptions can be modeled by tagging every function return value as either “normal” or “exceptional”; the caller of a function must then explicitly check for and propagate exceptions (exception-passing style). And, perhaps best-known, continuationpassing style (CPS) passes to every function an explicit representation of the remainder of the computation, to be invoked on the result of that function [Rey72, Fis72, Plo75].

While these techniques for modeling computational effects all share a similar feel, they do differ substantially in the details. It was therefore a remarkable observation by Moggi that they could each be seen as a particular instance of a generic schema, parameterized by a monad, a simple concept from category theory [Mog89]. This meant that much of the theory of computational effects could be derived abstractly, without reference to any specific notion of effect.

Again, it did not take long for this idea to migrate from mathematical semantics to mainstream functional programming. Work by Wadler and others [Wad90, Wad92b, PW93] established monadic style as a practical technique for structuring purely functional programs in a way that could reasonably conveniently express both program-internal effects (exceptions, state, etc.) and external ones (foreign function interfaces and monadic I/O).

The benefits of a denotational specification of an effect are substantial: we get a concise yet very precise characterization of how a program fragment can behave. For example, in a language with exceptions and non-termination as the only effects, the meaning of an integer-returning computation could be a meta-language value of type Z_{⊥} ⊕ X where X is some fixed domain of exception names.

This immediately tells us that evaluation of an expression can have only three possible outcomes: it either returns a number, raises an exception, or diverges. Thus, for example, a simple case analysis suffices to formally show that evaluating an expression twice is equivalent to evaluating it only once and duplicating the result – even if we interpose an arbitrary other computation between the two evaluations.

Analogous considerations apply to a purely functional program in exception-passing style: we can use standard reasoning principles for sum types to deduce properties of programs with exception-effects, again validating program transformations such as commonsubexpression elimination.

On the other hand, the purely-functional approach is not without problems, which become particularly evident at larger scales. One such disadvantage is that programming with effects turns into an all-or-nothing choice: to add even the most innocuous effect, such as debugging output or a “gensym” facility for generating unique names, we may have to rewrite substantial parts of the program in effect-passing style.

Monads alleviate this inconvenience somewhat, by allowing the program to be structured uniformly, independently of what effects will eventually be present. Still, we need to explicitly re-express the underlying functional program in monadic style – sometimes after the fact, duplicating effort, and sometimes preemptively, in anticipation of possibly having to add effects in the future.

Of course, since the conversion into monadic style is easily mechanizable, we can always express programs in a more concise notation, and have them automatically expanded into monadic style – either explicitly as a source transformation, or implicitly by an interpreter. But such an approach is not without problems either: by interposing a translation phase for expanding monadic effects into their denotations, we are effectively defining an entirely new programming language.

And for writing any non-trivial programs in this new language, we will want all the conveniences commonly provided by a language environment: pattern-matching function definitions, a static type system (giving meaningful error messages), a module system, a standard library, etc. Thus, the practical effort involved may be much larger than what might be expected from only looking at the core translation equations.

A related, but logically distinct, problem is that monadic-style definitions impose a substantial overhead on execution, whether implemented interpretively or compiled. Even if effects are rare (which is one of the tenets of functional programming), the infrastructure required to support an occasional imperative construct imposes a uniform burden on the entire evaluation process.

For example, for exception-passing style, the specification demands that after every subcomputation that may raise an exception, we have to check for this possibility and either proceed normally, or propagate the exception to the rest of the computation. A simple realization of exception-passing could thus spend a large fraction of its time checking for conditions that only occur very rarely. A more sophisticated implementation, especially a translation-based one, may be able to eliminate some of those checks, but in general calls to “unknown” functions (passed as parameters or separately compiled) have to be explicitly guarded by a check for exceptional returns.

Any further improvements seem to require pushing the effect down into the language implementation itself, still providing a purely functional interface to the relevant operations, but implementing them more efficiently than what could be expressed explicitly in the language. In particular, several proposals have been made for direct implementations of state in languages like Haskell [PW93, LPJ95].

Unfortunately, such a “black-box” approach negates one of the main advantages of the denotational approach: the ability to represent effect meanings explicitly as ordinary, functional constructs that can be reasoned about directly within the language. And even more significantly, these more efficient implementations are only available for a select few effects; programmer-defined, application-specific monads cannot take advantage of any non-functional implementation techniques.

1.1.2 “Effects as behavior”

As noted in the previous section, there are some compelling advantages to the denotational approach to effects, but also some significant practical problems. Whether the former outweigh the latter is still a controversial issue, especially because there is an alternative way of treating effects in programs. This approach is often referred to by the rather loaded term of “impure” functional programming; we will generally use the more neutral names behavioral or operational.

The basis of this approach is that a program expressed in terms of function definitions and applications can still be given a very natural algorithmic reading. Specifically, the fundamental principle of applicative-order reduction (namely, reducing the argument part of a β-redex before performing the substitution) can be seen as specifying a particular sequencing of evaluations. And this sequencing can serve as a robust skeleton for organizing general computational effects.

Although this idea can be traced back to early Lisp [M^{+}62], perhaps the prototypical functional language based on such an approach is Scheme [CR91]. The three key semantic differences distinguishing Scheme from a “purely functional” language are its call-by-value evaluation strategy, the presence of explicitly mutable state, and a feature known as first-class continuations.

These three characteristics make Scheme a very versatile language, but potentially significantly complicate reasoning about programs. The problem is not that call-by-value, state, or continuations are inherently particularly hard to reason about. After all, they have simple denotational counterparts, and any Scheme program can be relatively easily expanded into a “purely functional” one by a continuation-passing transform.

In fact, we do not need to explicitly translate at all; direct-style equational theories such as the computational λ-calculus are only slightly more complicated than βηconversion [Mog89]. (The situation is complicated somewhat by dynamic creation of mutable cells, but those too can be dealt with [FH92].) It would thus seem that ease of reasoning about impure functional programs should be “within a constant factor” of that about pure programs.

But there is a more subtle reason why general Scheme programs can be much harder to analyze formally than effect-free ones: because the set of computational effects is effectively fixed at two low-level but very powerful operations, the natural programming style consists of encoding higher-level effect abstractions imperatively in terms of the available effects, rather than through explicit effect-passing.

For a simple example, consider parameterization. Suppose that at a few places in a program, perhaps spread across several separately compiled modules, we need access to a parameter supplied as part of the initial expression to be evaluated. The two basic approaches for achieving this are to pass the parameter around everywhere it might eventually be needed, or to store its value in a global cell and access it only where actually used.

If the program is sufficiently large, and the accesses to the parameter sufficiently infrequent, the second approach becomes the natural choice (indeed, probably the correct choice from a software-engineering perspective). But by encoding parameterization in terms of state, we have effectively failed to represent the important fact that the parameter is immutable.

That is, suppose the program were otherwise purely functional. Then in explicitlyparameterized style, we could easily argue that the phrase f 3p + f 3p could always be safely replaced by 2 × f 3p, even when f was an unknown function. But once we admit general state, the optimization is no longer automatically valid: we need to be able to inspect f, making sure that it does not change the global value of p, before we can eliminate the common subexpressions.

In other words, where the transformation was a simple equational property in a purely functional setting, it is at best only provable in a language with effects when f and all the functions it calls are known. The reason is that the program does not explicitly embody the specification that the global state can never be modified after its initialization.

Of course, in a purely functional language, the state-based solution would not be available at all. Or, more accurately, given that all effects in a pure language must be written out in full, parameterization would naturally be expressed as such, rather than through (functional) state-passing. That is, if we have to be explicit about effects anyway, we may as well be precise.

With control effects, the problem is of course compounded. Again, an imperative realization of effects such as exceptions, nondeterminism, or concurrency may well be practically preferable to its more declarative counterpart in explicit effect-passing style, but the price we pay is in loss of simple reasoning principles.

In fact, the problem is not only in analyzing programs using the effects: it is often challenging even to show formally that the implementation of the effect itself is correct. For example, in Scheme we can encode (the control aspects of) an ML-like exception facility in a few lines of code using call/cc and a “current handler” cell. But a proof that such an implementation actually agrees with the explicit exception-passing used in the formal definition of SML [MTH90] is by no means a trivial task.

Similar considerations apply to analyzing control-based implementations of backtracking [Hay87, HDM93] or concurrency [Wan80]: while the code may be short, elegant, and intuitively plausible, formally relating it to a more abstract denotational specification, such as success lists [Wad85] or resumptions [Sch86, Mog90], is often a serious undertaking.

One could thus say that it is in this sense that “purely functional” programs offer a pragmatic reasoning advantage over “impure” ones: by penalizing all effects equally, they do not discourage the use of precise, custom-tailored effect representations. Impure languages, on the other hand, leave us an unpleasant choice, trading off precision against verbosity: if the desired effect is not already explicitly available, we must decide whether to encode it in terms of some more general standard effect, or to rewrite the program in explicit effect-passing style.

1.1.3 A unified view

As we have seen, each of the approaches has its advantages and disadvantages. The question thus naturally arises, whether there might be a way to somehow combine the best features of both. In particular, would it be possible to set things up so that we could think (both formally and informally) in terms of precise, functional denotations, but work (both when writing and executing programs) with the concise, operational behaviors?

The main goal of this thesis is to answer this question affirmatively. We will see how it is indeed possible to take a purely functional denotational specification of any monadic effect and obtain from it a directly executable operational implementation using call/cc and state. In fact, we will be able to define functions for converting back and forth between denotational and behavioral views of the same effect with no loss of information.

The fundamental idea is to distinguish carefully between transparent and opaque representations of a computational effect. The transparent representation is the explicit, denotational one: a computation that may raise an exception is represented as an effectfree computation of a sum-typed result; a computation with state-effects is represented as a pure function from old state to result and new state; a nondeterministic computation is represented as a deterministic computation of a list of results; and so forth.

On the other hand, the opaque representation is effectively an abstract data type with two operations: we can construct a trivial computation out of a value, and we can sequence two computations, where one may depend on the outcome of the other. How these operations are realized depends on the particular notion of effects, of course. But when writing the bulk of a typical program, the opaque representation is all we need. For example, a program written in monadic style would mostly use abstract unit and bind operations for structuring, regardless of what they actually expanded to.

Only when we actually wish to perform an effect, such as raising an exception, accessing the store, or making a nondeterministic choice, do we need additional operations. For such explicit effect-manipulations, we introduce two additional operations, converting between transparent and opaque representations of an effect.

That is, given an explicit representation of the effect, such as a value representing a raised exception, a function modifying the state, or a list of possibilities, we can obtain from it the corresponding opaque representation, which can then be further combined with other opaque computations in the usual way.

Conversely, and equally importantly, from an opaque representation, we can recover its transparent counterpart. For example, to handle an exception, we explicitly examine the sum-based representation of a computation and perform the appropriate action in each case. Or to determine whether a nondeterministic subcomputation has at least one successful outcome, we check if its transparent, list-based representation is non-empty.

Although this distinction may not at first appear particularly profound or useful, we now sketch three crucial observations that together summarize the main contributions of the thesis (with each one roughly corresponding to a chapter):

1. Even though opaque computations are a priori simply another abstract datatype, the two operations of value-inclusion and sequencing are exactly what forms the effect-backbone of an “imperative functional” language such as Scheme or ML. In such a language, any subcomputation may have an effect. A value such as a constant or a lambda-abstraction is therefore a special case, which must be implicitly coerced into a general computation. This corresponds to the first operation on our ADT of opaque computations.

Similarly, effects in compound computations are implicitly sequenced by the call-by-value evaluation order. For example, in an application E_{1} E_{2}, first E_{1} is evaluated to a value, then E_{2}, and finally the application is performed. Again, this corresponds to an explicit sequencing of opaque computations, where subsequent computations may depend on the values produced by earlier ones.

With this view, then, the conversions between transparent and opaque representations of computations provide an effect-introspective capability in the language, exposing the underlying notion of effects when and only when it matters. That is, the two operators convert between computations as data and as behavior within a single setting, integrating the views of effects as either being or happening. The key requirement is that the two conversion operations must be (two-sided) inverses, so that no information is lost when switching between the two view.

That is, by relaxing the relationship between transparent and opaque representations from their being identical to merely isomorphic, we have already gained something important: a model for programming in a convenient, concise ML-like language, with an intuitive imperative reading, yet at the same permitting equational reasoning about our programs as if they were written purely functionally, with explicit effect-passing. But we can actually go further:

2. Since the ultimate goal of reasoning about programs is to characterize their observable behavior, we actually have some freedom in choosing the opaque representation of effects, as long as we can guarantee that it properly tracks the transparent representation in all complete programs. In other words, we only need to ensure that the two representations are observationally isomorphic, whether or not they actually are denotationally so.

More explicitly, in addition to the canonical opaque representation, which simply encapsulates the specification monad of the transparent representation, there may also be a variant opaque representation, based on a different implementation monad. Then, as long as we choose the implementation monad such that it successfully mimics its specification counterpart in all program contexts, we can still reason about programs as if opaque effects were directly represented by the specification monad.

This is of course a well-known property of abstract data types in general. In our case, however, the ADT operations of value inclusion and sequencing are implicitly invoked at every single subcomputation (whether it actually performs any effects or not) in addition to the explicit conversions between transparent and opaque representations. Thus, efficiency of the implementation becomes a significant concern.

We will consider several examples of such effect-simulations, and give a general characterization of the relationships the two monads must satisfy in order for one to act as the opaque representation of the other. But perhaps the most remarkable and useful such instance is that, under suitable assumptions, any monad can be simulated by a continuation monad. In particular, this means that no matter how apparently complex the transparent specification may be, it can be implemented uniformly by continuation-passing.

This further adds to the attractiveness of programming with monadic effects: we can still reason about our programs as if their operational behavior were realized by explicit effect-passing according to a (potentially computationally costly) declarative specification. Yet the actual implementation only needs to incur the relatively low (and fixed) cost of continuation-passing. And we can do better still:

3. Although we nominally have a way to simulate arbitrary monadic effects with continuation monads, we are still some way off from a full implementation of our hypothetical ML-like language with behavior-data duality for user-definable effects. We have shown that continuations are in a sense a universal effect, but we still need to actually exploit this property in practice.

A key third step is therefore to note that the variant opaque representation of an effect is also the canonical opaque representation of the effect induced by the implementation monad. That is, we can define an ML-like language with a notion of native effects that directly corresponds to the continuation monads we use for implementing other monadic effects. Any language in the style of (1) above can then be directly embedded into this one language of control effects.

Moreover, we can show the perhaps equally surprising result that our universal control-effect language can itself be embedded in a language with only Schemestyle first-class continuations and mutable global variables. This could be said to validate the informal claim in the Scheme Rationale for call/cc that most useful control abstractions can be implemented explicitly, without changing or extending the language itself [CR91].

With this correspondence, we have effectively bridged the gap between the denotational and the operational view of effects: we can reason safely in terms of the former, but work in a practical, familiar programming language in terms of the latter. The general construction takes a non-trivial amount of work to develop and prove correct, but we only need to perform it once and for all, not once for every new effect we want to implement.

The presentation in this document is oriented towards call-by-value languages, which can take full advantage of point (1) above. Still, there is in principle no reason why (2) and (3) could not also be exploited in a purely functional language; efficient implementations of effects are as important for Haskell-like languages as they are for Scheme-like ones. We will not develop the details of such an application, however.

1.2 Monads and monadic reflection

In this section, we give somewhat simplified introduction to monadic effects. In particular, we will assume that the monad under investigation represents the only computational effect in the language. The formal development in the next chapter considers a more general notion of computation, where a monad serves to introduce a new effect on top of potentially already existing ones. Although the basic idea is the same, the details become substantially more involved. For the moment, let us therefore ignore the possibility of effects other than the one being introduced.

Monads originate in category theory; like many such concepts, they have several equivalent definitions. For our purposes, the following variant (usually known as the Kleisli triple formulation) seems most convenient:

Definition 1.1 (preliminary) A monad T in a functional language consists of the following:

• A type constructor T .

• For any type α, a function η_{α} : α → T α (the unit function at α).

• For any function f : α_{1} → T α_{2}, a function f^{∗} : T α_{1} → T α_{2} (the extension of f).

These components must further satisfy the three monad laws:

f^{∗} ∘ η_{α} = f  η_{α}^{∗} = id_{T α}  (f^{∗} ∘ g)^{∗} = f^{∗} ∘ g^{∗}

Remark 1.2 In category theory, the a monad is conventionally defined in terms of a functor T and natural transformations η : Id → T and μ : T^{2} → T satisfying certain equalities [ML71, VI.1]. (In the context of functional programming, the corresponding operations are usually referred to as map, unit, and join [Wad92a].)

It is easy to see, however, that the two formulations are equivalent: every Kleisli triple (T, η, ^{∗}) determines a monad (T, η, μ) by

T f = (η ∘ f)^{∗} and μ_{α} = id_{T_{α}}^{∗} .

Conversely, every monad determines a Kleisli triple by

f^{∗} = μ_{β} ∘ T (f) ,

and moreover these assignments are inverses. In the following, we will therefore use the terms “Kleisli triple” and “monad” synonymously.

A simple syntactic variation on Kleisli triples, popularized by Wadler [Wad92b], uses a binary infix operator to denote application of an extended function, writing t ‘bind‘ f or t ⋆ f for our f^{∗} t. This “continuation last” notation is usually preferable for writing actual functional programs in monadic style, but the formulation in the definition is more convenient for our purposes.

It should also be mentioned that our monads are properly called strong monads in category theory [Mog89], essentially because the f being extended need not be closed. (The monad laws must then also hold for open terms; the formal definition in the next chapter will reflect this.) We will use the “functional programming” rather than the “categorical” terminology throughout this document.

Monads provide a uniform framework for reasoning about computational effects (such as state, exceptions, or I/O) in applicative programming languages [Mog89, Mog91]. Informally, η a represents a “pure” (i.e., effect-free) computation yielding a, while f^{∗}t represents the computation consisting of t’s effects followed by an application of f to the result (if any) computed by t. A concrete instance may help clarify this:

Example 1.3 For any fixed type χ, the monad of χ-carrying exceptions is given by

T α = α + χ,  η = λa. inl a,  f^{∗} = λt.case t of inl a ⇒ f a [] inr e ⇒ inr e

Here, a computation of type α is either a value a of type α (the left summand), denoting a successful computation of a, or a value e of type χ (right summand), representing a specific failure. The unit and extension operations capture the expected operational behavior of exceptions; in particular, if evaluation of a function argument t raises an exception e, that exception is simply propagated without ever applying f. It is easy to check that these definitions do in fact satisfy the equations in Definition 2.15.

The use of monads for structuring purely functional programs – as opposed to language semantics – is by now quite commonplace [Wad92b, PW93]. Of course, those same structuring techniques can usually also be used with Scheme-like languages (only rarely do monadic-style programs rely on lazy evaluation in a fundamental way), but the benefits seem less clear: often a “mostly pure” program with a few isolated effects (e.g., a gensym or occasional output) is both more efficient and easier to understand at a glance than an equivalent “completely pure” program expressed in monadic style throughout.

There is a more interesting way, however, of explicitly using monads as a structuring tool for programs in “impure” functional languages, one that takes full advantage of an eager evaluation strategy instead of trying to ignore it. The study of this alternative is the main focus of this thesis. Specifically, our development is based on a simple functional language based on “Moggi’s principle”:

Computations of type α correspond to values of type T α.

As also noted by Moggi, this abstract correspondence principle can be embodied into a concrete language construct which we will call monadic reflection (by analogy to the more general notion of computational reflection [Smi82, WF88]). Specifically, we take:

Definition 1.4 (preliminary) A reflection of a monad T in a language is given by two operators

\frac{Γ ⊢ V : T α}{Γ ⊢ μ(V) : α}  and  \frac{Γ ⊢ E : α}{Γ ⊢ [E] : T α}

satisfying that for any expression E : α (possibly with computational effects) and any value V : T α,

[E] is a value,  μ([E]) = E,  and [μ(V)] = V

Although the presence of these two operators arises naturally from the monadic framework, little is generally said about their computational interpretation, let alone their usefulness in actual functional programming. As it turns out, however, monadic reflection provides exactly what we need to program with monadic effects without having to rewrite the code in monadic style.

In operational terms, for any value V : T α, μ(V) reflects V as an “effectful” computation of type α: we can construct an explicit representation of the effect, then perform or execute it by passing it to μ( ). Conversely, given a general computation E : α, [E] reifies it as the corresponding effect-free value of type T α, which can then be further inspected and analyzed like any other inert piece of data.

Although it is possible to write programs using the reflection and reification operators directly, an actual programming language would typically define a collection of more convenient operations in terms of μ( ) and [ ]:

Example 1.5 Consider again the exception monad from Example 1.3. We can express the usual exception-raising construct directly as

raise E =^{def} let e = E in μ(inr e)

where E is an expression – typically just a value – of type χ. That is, we explicitly construct a right-tagged value in the explicit representation of computations, then pass it to μ( ) to perform the effect.

Conversely, [E] reifies a possibly exception-raising α-expression E into a value of type α + χ, so we can define an exception-handling construct like this:

try E_{1} handle e ⇒ E_{2} =^{def} case [E_{1}] of inl a ⇒ a [] inr e ⇒ E_{2}

That is, if E_{1} returns normally, E_{2} is ignored, but if E_{1} raises an exception, the handler E_{2} is invoked with e bound to the exception data; a general pattern-matching handle construct as found in SML can easily be expressed in terms of this one.

Example 1.6 For any type σ, the σ-state monad is defined by:

T α = σ → α × σ,  η = λa. λs. ⟨a, s⟩,  f^{∗} = λt. λs. let ⟨a, s′⟩ = ts in f as′

Here, a computation t is represented as a (pure) function accepting a current state s and returning a value a and a new state s′; an effect-free computation passes the state along without modifying or reading it; and the extension of f first evaluates t in the current state s and then f a in the state s′ resulting from evaluation of t.

Using reflection, we can define operators for updating and reading the state:

state := E =^{def} let v = E in μ(λs. ⟨⟨⟩, v⟩)
!state =^{def} μ(λs. ⟨s, s⟩)

That is, state := E is the effect represented by a function replacing the state with the value of E (and returning ⟨⟩ as the result of the operation), while !state denotes the effect of reading the current state without modifying it. Neither of these definitions explicitly uses the reification operator. That one is only used implicitly at the top level: if E is a program with state effects, then

run E =^{def} let ⟨a, s′⟩ = [E]s_{0} in a

is the result of evaluating E starting with an initial state s_{0} and discarding the final state. A simple refinement is of course to permit the state to persist across a sequence of top-level evaluations, as, in the interactive read-eval-print loops of ML or Scheme.

More generally, if we take σ to be a whole store (a finite map from locations to values), we can define, for any mutable variable x,

x:= E =^{def} let v ⇐ E in μ(λs. ⟨⟨⟩, s{ℓ_{x} ← v}⟩)
!x =^{def} μ(λs. ⟨sℓ_{x}, s⟩)

where ℓ_{x} is the location corresponding to the cell x.

Note that the state-accessing operations export only a subset of the functionality of the state monad. To express general reflection/reification in the store case, we need access to a “first-class store” mechanism. This can actually be implemented reasonably efficiently using version trees, without requiring the whole store to be copied [JD88, Mor93], but it does impose some overhead.

The latter example illustrates that it may not always be feasible or desirable to export the full reflection/reification pair for a monad in a real programming language. Nevertheless, it will be important for analysis purposes to consider the fully general formulation of an effect in terms of μ( ) and [ ], with any restrictions on accessible functionality viewed as purely pragmatic considerations.

This is not to trivialize such concerns, only to emphasize that they are an orthogonal issue. Reflection and reification expose exactly the range of effects expressible in the corresponding state-passing formulation – much as a traditional denotational semantics of a language with a store does not formally enforce that the store is used in a singlethreaded way.

The exception and state monads by no means exhaust the interesting possibilities. Some other examples of simple monadic effects are listed in Table 1.1; we will encounter many of these in more detail later. And although this collection may still seem limited, we have not even considered all the combinations that encode multiple effects. For example, T α = σ → (α + χ) × σ represents computations with both exceptions and state.

(How to combine specifications of individual effects into composites is actually a non-trivial problem. Although we will not develop the details in full generality, the

Common name Functor, T α Unit, η_{α}  Extension, f^{∗}
Identity  α  λa. a  λt. f t
Partiality  α_{⊥}  λa. up a  λt. case t of up a ⇒ f a [] ⊥ ⇒ ⊥
Exception  α + χ  λa. inl a  λt. case t of inl a ⇒ f a [] inr e ⇒ inr e
State  σ → α × σ λa. λs. ⟨a, s⟩ λt. λs. let ⟨a, s′⟩ = ts in f as′
Environment  ϵ → α  λa. λe. a  λt. λe. f (te)e
Complexity  α × N  λa. ⟨a, 0⟩  λ⟨a, n⟩. let ⟨b, n′⟩ = f a in ⟨b, n + n′⟩
List-nondeterm. α^{∗}  λa. [a]  λ[a_{1}, …, a_{n}]. f a_{1} {+ \atop +} ⋯ {+ \atop +} f a_{n}
Set-nondeterm. P_{fin}α  λa. {a}  λ{a_{1}, …, a_{n}}. f a_{1} ∪ ⋯ ∪ f a_{n}
Continuation  (α → o) → o λa. λk. k a λt. λk. t(λa.f ak)

Table 1.1: Some simple monads

incremental approach used in the next chapter to layer a new effect on top of an existing one illustrates the basic principle: we must refine the definition of a monad to explicitly account for the original effects in the new specification.)

1.3 Overview of the thesis

1. This Introduction presents some background material about computational effects and informally introduces the notion of monadic reflection as the bridge between the denotational and operational view of monadic effects.

2. In Programming with Monadic Effects, we first specify a simple functional base language with some notion of ambient effects, such as partiality. We then formally define monads in this setting and show how a monad T induces an extension of the base language with a new focus effect. We specify the semantics of this effectenriched language by a simple but somewhat impractical (definitional) monadic translation back the original language, for which we already have a semantics. Our task in the remaining chapters will then be to devise and prove correct an alternative implementation of the extended language.

3. Relating Effects contains the main technical contribution of the thesis. We consider a specification monad T and an implementation monad U, and investigate when U can be said to simulate T. We first show how, given some data connecting T and U, we can define a variant translation from the T-extended language to the base language, using U-effects to perform T-effects (but without any correctness guarantees yet).

After some technical preliminaries, setting up the proof context, we then introduce the concept of a monad relation between T and U, and show that given such a relation, the definitional and the variant translation agree on complete T-programs. In many cases we can obtain the required monad relation directly from existence of a monad morphism from T to U, but the general continuation-simulation of T with a continuation monad involves an additional twist to capture the parametricity properties of the “final answer” type.

We conclude the chapter by showing that the correspondence between monads and continuations also allows us to define the monadic reflection operators for T directly in terms of those for the continuation monad U. Thus, it suffices to provide an implementation of the language with continuations as the notion of focus effect.

4. Completing the construction, Implementing Continuation-Effects shows how the effects corresponding to a continuation monad can be embedded into a Scheme-like language. The proof can be broken into three distinct steps. First, we show that the monadic effects for continuations can be expressed in terms of a control abstraction called composable continuations, which can be further decomposed into three even simpler control operators.

Second, we show that the distinction between ambient and focus effects introduced by the definitional monadic translation does not actually affect evaluation, thus leaving us to implement a language with a single level of effects. And third, we show that this language can be implemented by embedding in language with firstclass continuations and state. We conclude by showing a concrete implementation of the construction and a few programming examples.

5. Finally, the Conclusion summarizes the results and outlines some promising directions for further work.

Programming with Monadic Effects

In this chapter, we introduce a simple functional programming language that will serve as a concrete framework for the results and proofs throughout the thesis. We also formally define the notion of monad in this setting, and show how a monad allows us to systematically define a extended language with a new notion of effects.

2.1 The base language

2.1.1 Terminology

A language consists of a syntax L and a semantics L. The syntax defines the sets of wellformed types and of well-typed terms of a given type by means of a language signature, i.e., a set of type constructors and (typed) term constructors from which language phrases are built up inductively.

The semantics assigns some notion of meaning to the terms. As a practical minimum, we expect a semantics to provide a notion of program evaluation, i.e., a partial function Eval_{L} from a suitable subset of L-terms (e.g., closed terms of base type) to some set of observable results, say natural numbers. An evaluation semantics induces a notion of observational equivalence on terms, where two terms are considered equivalent if they can be substituted for each other in any program context without changing the observable outcome of the program. It is easy to see that this relation on terms is in fact a congruence wrt. all term constructors of the language.

A denotational semantics provides more, namely a model. That is, for every type, a set of meanings of terms of that type, and to every term constructor, a meaning of the constructed term expressed as a function of the meanings of the subterms. In particular, this provides a notion of equality, denotational equivalence, where two terms are equal iff they denote the same element of the model; because of the compositionality requirement, denotational equivalence is likewise a congruence. Two terms may be observationally equivalent without being denotationally so, but it is usually simpler to reason about denotational equivalence.

We obtain an evaluation semantics from a denotational semantics by defining a function from the meanings of closed terms to observable results. For example, the denotation of a program could be an element of the flat domain N_{⊥} of lifted natural numbers; the induced evaluation semantics is then given simply by the evident partial function mapping every lifted natural number to itself, and undefined on the ⊥-element of the domain. Two different semantics for a syntax (say, direct and continuation) may determine the same evaluation semantics (and hence the same notion of observational equivalence), but induce different notions of denotational equivalence.

We say that a language (L, L) is a member of a language class (e.g., the class of lambda-calculi) if its signature contains some specified set of type constructors (product, function space, etc.) and term constructors (abstraction, application, etc.), and the meanings of these types and terms in L satisfy some equational constraints (congruences, β-conversion, etc.). Often we can prove a result for an entire class of languages by showing that it holds generically in any model of the equations.

2.1.2 The base syntax

We now present a concrete language, in which we will be doing most of the formal development. Its syntax and semantics are very similar to PCF [Plo77] (even more so to PCF with lifted types [Mit96], except that the effect structure is made more explicit. We call it Effect-PCF. We present the syntax and informal operational interpretation in this section, with a precise denotational semantics in the next.

Effect-PCF is somewhat more verbose than a typical practical programming language, because all computation sequencing is made explicit in the syntax. For example, in an application, both the function and the argument must be explicitly evaluated if they are not already values.

Although we could have worked in an ML-like CBV language directly, the general treatment of monadic effects becomes awkward when the sequencing is left implicit. The present formulation allows us to cleanly separate out the handling of effects from the “purely functional” structure (exponentials, products, etc.).

Moreover, there is a simple, effect-independent elaboration of a standard, ML-like syntax into Effect-PCF, so we can view the implicit sequencing of computations in callby-value languages as merely convenient shorthand for the corresponding Effect-PCF terms. We will return to this elaboration in Section 2.1.6.

The base signature is displayed in Figure 2.1.

Type structure

The most notable characteristic of the syntax is the division of the types into two classes: value types (α), and a subset called (generalized) computation types (β). The operational significance of this division is to make the possibility of effects explicit in the types, separating trivial or manifestly effect-free from serious computations.

We make value types properly include computation types by taking one possibility for a value to be an unevaluated computation, represented by a value of type ˚ α. For example, a closed expression of type ι will always be equivalent to a numeral; a general expression expected to yield a natural number, but which may diverge (or have some other effect) has type˚ ι.

2.1. THE BASE LANGUAGE 17 Types:

α ::= a | ι | 1 | α_{1} ×^{v} α_{2} | α_{1} + α_{2} | β
β ::= ˚ α | α → β | 1 | β_{1} ×^{c} β_{2}

Terms:

M ::= x | z | s M | ifz(M, M_{1}, x. M_{2}) | ⟨⟩ | ⟨M_{1}, M_{2}⟩ | fst M | snd M
| inl M | inr M | case(M, x_{1}.M_{1}, x_{2}.M_{2}) | λx^{α}.M | M_{1} M_{2}
|˚ M | let˚ x ⇐ M_{1} in M_{2} | fix_{β} M

Typing: Γ is a type assignment x_{1}: α_{1}, …, x_{n}: α_{n} (with all x_{i} distinct).

\frac{(x: α) ∈ Γ}{Γ ⊢ x : α} \overline{Γ ⊢ z : ι} \frac{Γ ⊢ M : ι}{Γ ⊢ s M : ι}
\frac{Γ ⊢ M : ι Γ ⊢ M_{z} : α Γ, x: ι ⊢ M_{s} : α}{Γ ⊢ ifz(M, M_{1}, x. M_{2}) : α}  \overline{Γ ⊢ ⟨⟩ : 1}
\frac{Γ ⊢ M_{1} : α_{1} Γ ⊢ M_{2} : α_{2}}{Γ ⊢ ⟨M_{1}, M_{2}⟩ : α_{1} ×^{v} α_{2}} \frac{Γ ⊢ M : α_{1} ×^{v} α_{2}}{Γ ⊢ fst M : α_{1}} \frac{Γ ⊢ M : α_{1} ×^{v} α_{2}}{Γ ⊢ snd M : α_{2}}
\frac{Γ ⊢ M : α_{1}}{Γ ⊢ inl M : α_{1} + α_{2}}  \frac{Γ ⊢ M : α_{2}}{Γ ⊢ inr M : α_{1} + α_{2}}

\frac{Γ ⊢ M : α_{1} + α_{2} Γ, x_{1}: α_{1} ⊢ M_{1} : α Γ, x_{2}: α_{2} ⊢ M_{2} : α}{Γ ⊢ case(M, x_{1}.M_{1}, x_{2}.M_{2}) : α}

\frac{Γ, x: α ⊢ M : β}{Γ ⊢ λx^{α}.M : α → β}  \frac{Γ ⊢ M_{1} : α → β  Γ ⊢ M_{2} : α}{Γ ⊢ M_{1} M_{2} : β}
\frac{Γ ⊢ M_{1} : β_{1} Γ ⊢ M_{2} : β_{2}}{Γ ⊢ ⟨M_{1}, M_{2}⟩ : β_{1} ×^{c} β_{2}}  \frac{Γ ⊢ M : β_{1} ×^{c} β_{2}}{Γ ⊢ fst M : β_{1}}  \frac{Γ ⊢ M : β_{1} ×^{c} β_{2}}{Γ ⊢ snd M : β_{2}}
\frac{Γ ⊢ M : α}{Γ ⊢˚ M :˚ α}  \frac{Γ ⊢ M_{1} :˚ α_{1} Γ, x: α_{1} ⊢ M_{2} :˚ α_{2}}{Γ ⊢ let˚ x ⇐ M_{1} in M_{2} :˚ α_{2}}  \frac{Γ ⊢ M : β → β}{Γ ⊢ fix_{β} M : β}

Figure 2.1: Base signature, L_{0}

A computation of type α →β is often more conveniently thought of as β-computation parameterized by an α-value, rather than as a function from values to computations. In particular, when β is itself an arrow type, no actual evaluation occurs until all the parameters are present.

Similarly, a computation of type β_{1} ×^{c} β_{2} can be viewed as a single computation implicitly parameterized by the choice of which component to evaluate, not as two unrelated computations. (In the case where β_{1} and β_{2} are the same type β, this is reflected in the isomorphism β ×^{c} β ≅ (1 + 1) → β.)

There are actually two product-type constructors, one for value types and one for computation-types only. Although their typing and equational properties are identical (and in the standard semantics, they are even interpreted by the same cpo constructor), the two variants are logically distinct. Still, we generally omit the superscripts when it is clear which one is meant – i.e., when one of the factors is a value type, or the context requires a computation-type.

(Analogous consideration apply to the unit type, of course, but there we can simply assume that the two types are identical without unduly constraining the semantics.)

The set of value-types also includes a countable set of type variables. (There are no type variables for the computation-types.) When Δ = {a_{1}, …, a_{n}} is a finite set of type variables, we write

⊢_{Δ} α type  and  ⊢_{Δ} β ctype

if all type variables occurring free in α and β are in Δ; in this case we say that α or β is a type over Δ. Clearly when Δ′ ⊇ Δ then also ⊢_{Δ′} α type and ⊢_{Δ′} β ctype. We say that α and β are (type-)closed when Δ is empty. A type over Δ determines a type family consisting of all types obtained by substituting closed types for type variables in Δ.

Note that there are no constructs within the language for explicitly binding type variables. (There will be, however, in an extension of L_{0} with recursively-defined types in Section 3.2.1.)

The canonical model of the language is given by the category of “bottomless” cpos (predomains) and continuous functions, with computation-types interpreted by pointed cpos and the˚ -operator on types corresponding to lifting. See Section 2.1.3 for details.

Terms

The term structure and associated typing rules are again mostly conventional. Similarly to the parameterization of types by type variables, we write

Γ ⊢_{Δ} M : α

if all type variables occurring free in Γ, M, and α are listed in Δ. M is then said to be a term over Δ; again it is called type-closed when Δ is empty, and the set of type-closed instances of a term over Δ forms a term family.

Complementing the˚ -operator on types, there is a term operator˚ M, which constructs an effect-free computation returning M, and let˚ x ⇐ M_{1} in M_{2}, which constructs a computation consisting of evaluating M_{1} and M_{2} in sequence, with x in the second evaluation bound to the result of the first one. A computation is treated as a first-class object, and is not actually performed until its value is explicitly requested, either directly by a top-level program evaluation, or through evaluation of an enclosing let.

We generally omit the type tags in terms when they are clear from the context. Also, we will occasionally use pattern-matching syntax in let- and lambda-bindings, with the usual expansions, e.g.,

(let˚ ⟨x_{1}, x_{2}⟩ ⇐ M in M′) =^{def} (let˚ x ⇐ M in M′{fst x/x_{1}, snd x/x_{2}}) (x∉F V (M′))

(Note that the projections are considered to be trivial by the typing rules, so the result of the substitution is still well-typed.)

The constructs associated with type ι allow us to program with natural numbers using a zero-constant, a successor function, and a combined zero-test/predecessor operation. Given general recursion, we can construct the standard arithmetic operations out of those primitives. For example, we can define addition as:

plus : ι × ι →˚ ι = λ⟨n_{1}, n_{2}⟩. fix_{ι→˚ ι} (λf.λn. ifz(n,˚ n_{2}, n′. let˚ r ⇐ f n′ in˚ (s r)))n_{1}

Note, however, that because of the use of fix, the result of an addition is an ι-computation, even though the addition function happens to be total. (We could of course extend the language with additional primitives for arithmetic or a primitive-recursion construct, which could then be given pure value-types.)

Having fixed points at all computation-types β also allows us to express mutual recursion easily, as in:

even : ι →˚ 2

= fst (fix_{(ι→˚ 2)×(ι→˚ 2)} (λ⟨e, o⟩.⟨λn. ifz(n,˚ (inl ⟨⟩), n′. on′), λn. ifz(n,˚ (inr ⟨⟩), n′. en′)⟩))

where 2 =^{def} 1 + 1 is the type of Boolean values.

On the other hand, we cannot write down a term corresponding to a fixed point of the pure successor function; indeed, the type ι→ι is not even expressible in the language. (We can write fix_{˚ ι} (λl^{˚ ι}.let˚ n ⇐ l in˚ (s n)) :˚ ι. Not surprisingly, this denotes a diverging computation in the intended interpretation of fix.)

Finally, we occasionally use the standard abbreviations:

\overline{Γ ⊢ id_{β} : β → β}  \frac{Γ ⊢ M_{1} : α → β_{1} Γ ⊢ M_{2} : β_{1} → β_{2}}{Γ ⊢ M_{2} ∘ M_{1} : α → β_{2}}
\overline{Γ ⊢ ⊥_{β} : β}  \overline{Γ ⊢ \underline{n} : ι} (n∈N)

with id_{β} =^{def} λx^{β}.x, M_{2} ∘ M_{1} =^{def} λx^{α}.M_{2} (M_{1} x), ⊥_{β} =^{def} fix_{β} id_{β}, and \underline{n} =^{def} s^{n} z.

2.1.3 A denotational semantics

A program in our base language is a closed term of type˚ ι; if in the semantics that term is equivalent to˚ \underline{n} for some n, the program denotes a successful computation with result n; otherwise, a diverging computation. A denotational semantics gives the meaning of a complete program by induction on its syntactic structure.

Preliminaries We give a simple model of the base language in the setting of bottomless cpos (also called pre-domains), i.e., complete partial orders not necessarily having a least element. For completeness, we review the associated terminology and constructions.

Definition 2.1 A cpo A is a set equipped with an ω-complete partial order ⊑, i.e., such that every countable chain a_{1} ⊑ a_{2} ⊑ ⋯ of elements in A has a least upper bound, written ⨆_{i} a_{i}. A monotone (= order-preserving) function from A to A′ is called continuous if for every chain (a_{i})_{i∈ω} in A, f(⨆_{i} a_{i}) = ⨆_{i} f(a_{i}).

A cpo B is called pointed if it has a least element ⊥_{B}, i.e., if ⊥_{B} ⊑ b for every b in B. A function between pointed cpos B and B′ is called strict if f(⊥_{B}) = ⊥_{B′}. We use the name domain synonymously with pointed cpo.

There are a number of standard cpos and cpo constructions:

• base types. Any set, such as the natural numbers N, can be organized as a cpo by equipping it with the discrete ordering, n ⊑_{N} n′ iff n = n′.

• unit type. The one-element set 1 = {∗} is trivially a cpo. It is even degenerately pointed, with ⊥_{1} = ∗.

• products. A_{1} × A_{2} is the cartesian product of cpos, ordered componentwise (i.e., (a_{1}, a_{2}) ⊑_{A_{1}×A_{2}} (a′_{1}, a′_{2}) iff a_{1} ⊑_{A_{1}} a′_{1} and a_{2} ⊑_{A_{2}} a′_{2}). If B_{1} and B_{2} are pointed then so is B_{1} × B_{2}, with ⊥_{B_{1}×B_{2}} = (⊥_{B_{1}}, ⊥_{B_{2}}).

• sums. A_{1} + A_{2} is the disjoint union of cpos (note: not the “separated sum” from standard domain-theoretic notation),

A_{1} + A_{2} = {(1, a_{1}) | a_{1} ∈ A_{1}} ∪ {(2, a_{2}) | a_{2} ∈ A_{2}}

ordered inject-wise, i.e., (i, a) ⊑_{A_{1}+A_{2}} (i′, a′) iff i = i′ and a ⊑_{A_{i}} a′. Such a cpo is in general not pointed, even if the summands are.

• function space. A_{1} → A_{2} is the cpo of continuous functions from A_{1} to A_{2}, with f ⊑_{A_{1}→A_{2}} f′ if ∀a ∈ A_{1}. f(a) ⊑_{A_{2}} f′(a). To minimize confusion with abstraction and application in the language, we write \underline{λ}x.φ(x) and f(a) for abstraction and application in the cpo model. A → B is pointed when B is, with ⊥_{A→B} = \underline{λ}x.⊥_{B}.

• lifting. For any cpo A, we define the lifted cpo,

A_{⊥} = {{a} | a ∈ A} ∪ {∅}

ordered such that {a} ⊑_{A_{⊥}} {a′} if a ⊑_{A} a′, and ∅ ⊑ {a} for any a. We write up(a) for {a} and ⊥ for ∅. Naturally, A_{⊥} is pointed.

The strict extension of a function f : A→A′_{⊥} is the function f^{†} : A_{⊥} →A′_{⊥} given by f^{†}(up(a)) = f(a) and f^{†}(⊥) = ⊥. More generally, for any pointed B and f : A→B, f^{‡} : A_{⊥} → B maps up(a) to f(a) and ⊥_{A_{⊥}} to ⊥_{B}.

• reflexive types. Finally, the CPO model allows us to construct solutions (up to isomorphism) to recursive type equations. While we will not need this immediately, it will become important in Section 3.2.1.

Given a finite set I, we write ∏_{i∈I} A_{i} for the I-indexed product of cpos, i.e., the set of functions ρ : I → ⋃_{i∈I} A_{i} such that for each i ∈ I, ρ(i) ∈ A_{i}. Such a function is usually called an environment. Environments can be naturally ordered pointwise, with ρ ⊑ ρ′ iff ∀i ∈ I. ρ(i) ⊑_{A_{i}} ρ′(i).

Finally, we write • for the empty environment; ρ[i′ ↦ a] ∈ ∏_{i∈I∪{i′}} A_{i} (where i′ may or may not already be a member of I) for the function mapping i′ to a ∈ A_{i′} and every other i ∈ I to ρ(i); and ρ \ i for the function ρ restricted to I \ {i}.

Base effects To give a semantics to our language in the predomain model, we first need to choose a notion of base or ambient effects, to be denoted by the computationtype constructor. The canonical example of such an effect is partiality, but the structure of the later proofs is largely independent of the exact choice; we only need to show that it satisfies a few simple relational properties.

We treat the case of partiality formally, and sketch how the setup generalizes to other ambient effects where appropriate. We do not develop the semantics of ambient effects in detail, however; where possible, it is more convenient to treat ambient effects more uniformly, using the monadic translations to be introduced a little later.

A valid reason for considering more complicated base effects, however, is to model language features that cannot be eliminated by a source-to-source transform. For example, “true” non-determinism (as opposed to a finitary variant, which can be defined by a backtracking transformation), can be modelled by a powerdomain or similar construct. Similarly, any notion of I/O operations or other extra-linguistic effects must somehow be accounted for in the semantics rather than at source level. We will not treat any of those formally, however.

Definition 2.2 An ambient-effect monad for the cpo semantics is given by the following data:

• A cpo constructor T , such that for any cpo A, T A is a pointed cpo.

• A family of continuous functions ξ_{A} : A → T A.

• An assignment to any continuous function f : A→T A′, a strict continuous function f^{⋄} : T A → T A′. This assignment must itself be continuous, i.e., satisfy the equality (⨆_{i} f_{i})^{⋄} = ⨆_{i} f_{i}^{⋄}.

Further, the components must satisfy the three monad laws:

f^{⋄} ∘ ξ_{A} = f  ξ_{A}^{⋄} = id_{T A}  f^{⋄} ∘ g^{⋄} = (f^{⋄} ∘ g)^{⋄}

Definition 2.3 The partiality semantics is given by taking T A = A_{⊥} (pointed, as required), ξ(a) = up(a), and f^{⋄} = f^{†} (strict by definition). It is easy to check that the monad laws hold for this triple.

(Incidentally, the requirement that f^{⋄} be strict ensures that ξ_{A}^{‡} : A_{⊥} →T A is a monad morphism from the partiality monad to T . We will phrase this in more general terms in Definitions 2.11, 2.15, and 3.1, and in Example 3.2.)

Semantics of types To every well-formed ⊢_{Δ} α type, we assign a cpo, and to every ⊢_{Δ} β ctype, a pointed cpo:

L[[a]] = θa
L[[ι]] = N
L[[1]] = 1
L[[α_{1} ×^{v} α_{2}]] = L[[α_{1}]] × L[[α_{2}]]
L[[α_{1} + α_{2}]] = L[[α_{1}]] + L[[α_{2}]]
L[[α → β]] = L[[α]] → L[[β]]
L[[β_{1} ×^{c} β_{2}]] = L[[β_{1}]] × L[[β_{2}]]
L[[˚ α]] = T (L[[α]])

Semantics of terms To every well-typed Γ ⊢_{Δ} M : α, we assign an element L[[M]]^{θ} ∈ (∏_{(x_{i}:α_{i})∈Γ} L[[α_{i}]]^{θ}) → L[[α]]^{θ}:

L[[x]](ρ) = ρ(x)
L[[z]](ρ) = 0
L[[s M]](ρ) = L[[M]](ρ) + 1
L[[ifz(M, M_{z}, x. M_{s})]](ρ) = \{ \begin{matrix}L[[M_{z}]](ρ) & when L[[M]](ρ) = 0 \\ L[[M_{s}]](ρ[x ↦ n]) & when L[[M]](ρ) = n + 1 \end{matrix}
L[[⟨⟩]](ρ) = ∗
L[[⟨M_{1}, M_{2}⟩]](ρ) = (L[[M_{1}]](ρ), L[[M_{2}]](ρ))
L[[fst M]](ρ) = a_{1} when L[[M]](ρ) = (a_{1}, a_{2})
L[[snd M]](ρ) = a_{2} when L[[M]](ρ) = (a_{1}, a_{2})
L[[inl M]](ρ) = (1, L[[M]](ρ))
L[[inr M]](ρ) = (2, L[[M]](ρ))
L[[case(M, x_{1}.M_{1}, x_{2}.M_{2})]](ρ) = \{ \begin{matrix}L[[M_{1}]](ρ[x_{1} ↦ a_{1}]) & when L[[M]](ρ) = (1, a_{1}) \\ L[[M_{2}]](ρ[x_{2} ↦ a_{2}]) & when L[[M]](ρ) = (2, a_{2}) \end{matrix}
L[[λx.M]](ρ) = \underline{λ}a.L[[M]](ρ[x ↦ a])
L[[M_{1} M_{2}]](ρ) = L[[M_{1}]](ρ)(L[[M_{2}]](ρ))
L[[˚ M]](ρ) = ξ(L[[M]](ρ))
L[[let˚ x ⇐ M_{1} in M_{2}]](ρ) = (\underline{λ}a.L[[M_{2}]](ρ[x ↦ a]))^{⋄}(L[[M_{1}]](ρ))
L[[fix_{β} M]](ρ) = ⨆_{i}(L[[M]](ρ))^{i}(⊥_{L[[β]]})

(where for any i ≥ 0, f^{i} is the i-th iterate of f, i.e., f^{0}(a) = a and f^{i+1}(a) = f(f^{i}(a))).

Figure 2.2: Denotational semantics L_{(T ,ξ, ^{⋄})}^{θ} of the base language

Other examples can be easily adapted from the source-level monads to be presented in Section 2.2.3; for example, we obtain a notion of ambient state by taking T A = N → (A × N)_{⊥}, cf. Example 2.18. However, such L_{0}-definable ambient effects are more conveniently dealt with at the syntactic level, through an explicit monadic translation.

Although the only explicitly accessible effect in our base language is divergence (via fix), it is still useful to consider more general effect-structures in the semantics. For example, a continuation semantics may well be of interest even for a language that does not contain explicit control operators.

We can now give a denotational semantics of the base language (parameterized by the choice of effect structure) in Figure 2.2. Let Δ be a finite set of type variables, and θ be a mapping of type variables in Δ to cpos. The semantics then assigns to every Δ-type ⊢_{Δ} α, a cpo L[[α]]^{θ} (pointed if α is computational), and to every Δ-term Γ ⊢_{Δ} M : α, a continuous function L[[M]]^{θ} from L[[Γ]]^{θ} = Π_{(x_{i}:α_{i})∈Γ}L[[α_{i}]]^{θ} to L[[α]]^{θ}. (We usually omit θ when it is clear from context. In particular, since there are no language constructs for binding type variables, θ stays constant throughout the semantic equations, and is omitted throughout the figure to reduce clutter.)

Although the denotational semantics thus assigns meanings to types and terms over arbitrary Δs (interpreting type variables by arbitrary cpos), for most purposes we will not use this generality; syntactic substitutions of closed types for type variables suffice. The only uses of the θ-parameterized semantics are in showing that a syntactic monad may be used to express a semantic one in Proposition 2.20, and when introducing recursivelydefined types in Section 3.2.1.

2.1.4 Generalized let

Definition 2.4 For any computation-type β of L_{0}, we define a derived term constructor let_{β̊}, the generalized let with typing rule

\frac{Γ ⊢ M_{1} :˚ α Γ, x: α ⊢ M_{2} : β}{Γ ⊢ let_{˚ β} x ⇐ M_{1} in M_{2} : β}

by induction on the structure of β:

let_{˚˚ α} x ⇐ M_{1} in M_{2} = let˚ x ⇐ M_{1} in M_{2}
let_{˚ 1} x ⇐ M_{1} in M_{2} = ⟨⟩
let_{˚ β_{1}×β_{2}} x ⇐ M_{1} in M_{2} = ⟨let_{˚ β_{1}} x ⇐ M_{1} in fst M_{2}, let_{˚ β_{2}} x ⇐ M_{1} in snd M_{2}⟩
let_{˚ α→β} x ⇐ M_{1} in M_{2} = λa^{α}.let_{˚ β} x ⇐ M_{1} in M_{2} a

In the predomain semantics (for any T ), two particular consequences of this definition are:

L[[let_{˚ β} x ⇐ M_{1} in M_{2}]](ρ) = \{ \begin{matrix}L[[M_{2}]](ρ[x ↦ a]) & when L[[M_{1}]](ρ) = ξ(a) \\ ⊥_{L[[β]]} & when L[[M_{1}]](ρ) = ⊥ \end{matrix}

In the case of the partiality semantics, these are in fact the only two possibilities for L[[M_{1}]](ρ). We thus obtain a natural generalization of the existing “strict extension” let, in that M_{2} can now be of a type interpreted by any pointed cpo, not necessarily a directly lifted one. Note, however, that we still need to restrict M_{1} to be of type˚ α, not a general computation type: while strictness makes sense for functions B_{1} → B_{2} between arbitrary pointed cpos, the strict extension operation can only extend a function A → B to A_{⊥} → B, not “strictify” an arbitrary function B_{1} → B_{2}.

It is important to note the difference between, say, a computation returning a pair of integers:

let_{˚˚ (ι×ι)} x ⇐ M in˚ ⟨x, \underline{5}⟩

(where M is evaluated exactly once, yielding a pair of numbers), and the construction of a pair of computations:

let_{˚˚ ι×˚ ι} x ⇐ M in ⟨˚ x,˚ \underline{5}⟩

(where M is evaluated when either component of the result is requested). Similarly, we distinguish between the diverging computation of a function:

let_{˚˚ (ι→˚ ι)} y ⇐ ⊥_{˚ ι} in˚ (λx.˚ \underline{3}) :˚ (ι →˚ ι)

and a successful computation yielding a function which diverges for all inputs:

˚ (let_{˚ ι→˚ ι} y ⇐ ⊥_{˚ ι} in (λx.˚ \underline{3})) :˚ (ι →˚ ι)

2.1.5 Equational properties

When the semantics L is fixed and clear from context, it is often preferable to reason about programs at the level of terms, rather than explicitly about their denotations in the semantics. More generally, we can often isolate a set of reasoning principles that hold for a large variety of interpretations, then check that our specific semantics L verifies those principles.

As mentioned before, for our purposes, it will suffice to consider equational properties of type-closed terms (i.e., with no free type variables), although the following should extend naturally to type and term families over a nonempty set of type variables.

Definition 2.5 A signature L consists of a set of type and term constructors. An interpretation L of L assigns to every type ⊢_{∅} α type of L, a set Val(α); and to every L-term Γ ⊢_{∅} M : α and finite function ρ with ∀(x_{i}: α_{i}) ∈ Γ. ρ(x_{i}) ∈ Val(α_{i}), an element Int(M)^{ρ} ∈ Val(α).

An equational theory E for L is a set of typed equalities between (type-closed) Lterms, Γ ⊢ M = M′ : α. A model of an equational theory is an interpretation that satisfies all the equations of the theory, i.e., whenever Γ ⊢ M = M′ : α is provable and ρ is a Γ-environment, then Int(M)^{ρ} = Int(M′)^{ρ} as elements of Val(α).

It is clear that the predomain semantics (for any notion of ambient effects T ) determines an interpretation of L_{0}, by taking Val(α) as the set underlying L[[α]] and Int(M)^{ρ} as L[[M]](ρ), forgetting continuity of L[[M]] (as a function from environments to values). We present an equational theory E_{0} for L_{0} and simultaneously argue that the predomain interpretation is a model of that theory. For particular classes of T s, additional equations may be axiomatizable, for example that ambient effects are commutative or idempotent; we do not consider such extensions, however.

In most cases, the axioms listed below can be immediately verified by referring to the semantics; we often omit the details where they can be easily filled in. Also, since equality judgments are always about type-closed terms, we omit the implicit ⊢_{∅} in all typing assumptions in the rules.

Lemma 2.6 In the semantics L of Figure 2.2, for any terms M and M′, the following weakening and substitution principles hold:

L[[M]](ρ) = L[[M]](ρ \ x) if x ∉ F V (M)
L[[M{M′/x}]](ρ) = L[[M]](ρ[x ↦ L[[M′]](ρ)])

Proof. Routine, by induction on M.

Given this lemma, the verification of the following equations is straightforward.

Congruences, substitutions By the denotational assumption, our notion of equivalence is inherently a congruence wrt. all the term constructors of the language. We also have general principles of closure under weakening and substitution:

\frac{Γ ⊢ M = M′ : α}{Γ, x : α_{1} ⊢ M = M′ : α}  \frac{Γ, x : α_{1} ⊢ M = M′ : α Γ ⊢ M_{1} = M′_{1} : α_{1}}{Γ ⊢ M\{M_{1}/x\} = M′\{M′_{1}/x\} : α}

which follow directly from Lemma 2.6:

L[[M]](ρ) = L[[M]](ρ \ x) = L[[M′]](ρ \ x) = L[[M′]](ρ)

L[[M{M_{1}/x}]](ρ) = L[[M]](ρ[x ↦ L[[M_{1}]](ρ)]) = L[[M′]](ρ[x ↦ L[[M′_{1}]](ρ)])
= L[[M′{M′_{1}/x}]](ρ)

Natural numbers

\frac{Γ ⊢ M_{z} : α Γ, x: ι ⊢ M_{s} : α}{Γ ⊢ ifz(z, M_{z}, x. M_{s}) = M_{z} : α}  \frac{Γ ⊢ M : ι Γ ⊢ M_{z} : α Γ, x: ι ⊢ M_{s} : α}{Γ ⊢ ifz(s M, M_{z}, x. M_{s}) = M_{s}\{M/x\} : α}

\frac{Γ ⊢ M : ι Γ, x: ι ⊢ M′ : α}{Γ ⊢ ifz(M, M′\{z/x\}, x′. M′\{s x′/x\}) = M′\{M/x\} : α}

Unit type

\frac{Γ ⊢ M : 1}{Γ ⊢ M = ⟨⟩ : 1}

(1 is a terminal object). Note that this equation means that there is only one value of type 1; there may well be different computations of that value, i.e., terms of type ˚ 1. Already in the partiality case there are two such closed terms: termination (˚ ⟨⟩) and divergence (⊥_{˚ 1}).

Products

\frac{Γ ⊢ M_{1} : α_{1} Γ ⊢ M_{2} : α_{2}}{Γ ⊢ fst ⟨M_{1}, M_{2}⟩ = M_{1} : α_{1}} (+symm) \frac{Γ ⊢ M : α_{1} ×^{v} α_{2}}{Γ ⊢ ⟨fst M, snd M⟩ = M : α_{1} ×^{v} α_{2}}

\frac{Γ ⊢ M_{1} : β_{1} Γ ⊢ M_{2} : β_{2}}{Γ ⊢ fst ⟨M_{1}, M_{2}⟩ = M_{1} : β_{1}} (+symm) \frac{Γ ⊢ M : β_{1} ×^{c} β_{2}}{Γ ⊢ ⟨fst M, snd M⟩ = M : β_{1} ×^{c} β_{2}}

(Both are products in the categorical sense.) Although in the predomain semantics the two notions of products are interpreted by the same object, we do not actually require this in general.

Sums

\frac{Γ ⊢ M : α_{1} Γ, x_{1}: α_{1} ⊢ M_{1} : α Γ, x_{2}: α_{2} ⊢ M_{2} : α}{Γ ⊢ case(inl M, x_{1}.M_{1}, x_{2}.M_{2}) = M_{1}\{M/x_{1}\} : α}(+symm)

\frac{Γ ⊢ M : α_{1} + α_{2} Γ, x: α_{1} + α_{2} ⊢ M′ : α}{Γ ⊢ case(M, x_{1}.M′\{inl x_{1}/x\}, x_{2}.M′\{inr x_{2}/x\}) = M′\{M/x\} : α}

(Sums are coproducts in the categorical sense.) Verification of the first law is immediate, given Lemma 2.6. For the second, we rely on the fact that L[[M]](ρ) must be a value of the form (i, a); the equation would not be sound if + were interpreted by, e.g., a separated sum and M denoted a diverging computation. A useful consequence of the above equations is that for any h (not necessarily denoting a strict function),

h(case(M, x_{1}.M_{1}, x_{2}.M_{2})) = h(case(x, x_{1}.M_{1}, x_{2}.M_{2})){M/x}

= case(M, x_{1}.h(case(inl x_{1}, x_{1}.M_{1}, x_{2}.M_{2})), x_{2}.h(case(inr x_{2}, x_{1}.M_{1}, x_{2}.M_{2})))

= case(M, x_{1}.hM_{1}, x_{2}.hM_{2})

Function space

\frac{Γ, x: α ⊢ M_{1} : β  Γ ⊢ M_{2} : α}{Γ ⊢ (λx^{α}.M_{1})M_{2} = M_{2}\{M_{1}/x\} : β}  \frac{Γ ⊢ M : α → β}{Γ ⊢ (λx^{α}.M x) = M : α → β}

(Categorically, α → β is an exponentiation of α and β, but we do not require existence of exponentiations with arbitrary codomain types, so the category of types and terms is not quite a ccc.)

Computations

\frac{Γ ⊢ M_{1} : α_{1}  Γ, x: α_{1} ⊢ M_{2} :˚ α_{2}}{Γ ⊢ (let˚ x ⇐˚ M_{1} in M_{2}) = M_{2}\{M_{1}/x\} :˚ α_{2}}  \frac{Γ ⊢ M :˚ α}{Γ ⊢ (let˚ x ⇐ M in˚ x) = M :˚ α}
\frac{Γ ⊢ M_{1} :˚ α_{1}  Γ, x_{1}: α_{1} ⊢ M_{2} :˚ α_{2}  Γ, x_{2}: α_{2} ⊢ M_{3} :˚ α_{3}}{Γ ⊢ (let˚ x_{2} ⇐ (let˚ x_{1} ⇐ M_{1} in M_{2}) in M_{3}) = (let˚ x_{1} ⇐ M_{1} in let˚ x_{2} ⇐ M_{2} in M_{3}) :˚ α_{3}}

Each of these corresponds directly to one of the monad equations governing the ambient-effect monad (T , ξ,^{⋄}). For example, for the first one we verify:

L[[let˚ x ⇐˚ M_{1} in M_{2}]](ρ) = (\underline{λ}a.L[[M_{2}]](ρ[x ↦ a]))^{⋄}(ξ(L[[M_{1}]](ρ)))
= (\underline{λ}a.L[[M_{2}]](ρ[x ↦ a]))(L[[M_{1}]](ρ)) = L[[M_{2}]](ρ[x ↦ L[[M_{1}]](ρ)])

Fixed points

\frac{Γ ⊢ M : β → β}{Γ ⊢ fix_{β} M = M (fix_{β} M) : β}

This suffices for evaluation, but for more general formal reasoning we will need additional properties; the details of this are covered in the next chapter. (Actually, none of the results in the thesis depend on the fixed-point equation being in E_{0}, so in principle we could safely omit it without affecting correctness.)

We have thus established:

Proposition 2.7 For any ambient-effect monad, the predomain semantics L_{(T ,ξ,^{⋄})} is a model of E_{0}, the equational theory generated by the inference rules listed above.

2.1.6 Encodings of implicitly-sequenced languages

In actual programming languages there is often no explicit syntactic or typing distinction between values and general terms. Rather, the grammar of types and terms is of the form:

σ ::= ι | 1 | σ_{1} × σ_{2} | σ_{1} + σ_{2} | σ_{1} → σ_{2}
E ::= x | z | s M | ⟨⟩ | ⟨E_{1}, E_{2}⟩ | fst E | snd E | inl E | inr E
| case(E, x_{1}.E_{1}, x_{2}.E_{2}) | λx^{σ}.E | E_{1} E_{2} | fix E

with trivial and serious computations a priori occupying the same type.

Our explicitly-sequenced syntax simplifies formal manipulation of programs, but it is somewhat inconvenient for actual programming. (However, in common practice, it is relatively uncommon to see, e.g., applications of the form E_{1} E_{2} where one or both of E_{1} and E_{2} themselves have effects; in particular, evaluation of E_{1} only very rarely has effects). Nevertheless, typical programs in ML-like languages do have some sequencing left implicit, and it would be too burdensome to force them to always be explicitly sequenced.

Fortunately, we can treat the more compact general syntax as merely shorthand for explicitly-sequenced terms, with programs first being desugared or elaborated into sequenced form, and only then given an operational or denotational semantics. We present two such elaborations, leading to either a call-by-value (CBV) or a call-by-name (CBN) interpretation of the implicitly-sequenced language.

CBV Translation on types. If σ is a type of the CBV language, ((σ))^{v} is a type of L_{0}.

((ι))^{v} = ι
((1))^{v} = 1
((σ_{1} × σ_{2}))^{v} = ((σ_{1}))^{v} ×^{v} ((σ_{2}))^{v}
((σ_{1} + σ_{2}))^{v} = ((σ_{1}))^{v} + ((σ_{2}))^{v}
((σ_{1} → σ_{2}))^{v} = ((σ_{1}))^{v} →˚ ((σ_{2}))^{v}

Translation on terms. If Γ ⊢ E : σ in the source language, then ((Γ))^{v} ⊢ ((E))^{v} :˚ ((σ))^{v} in L_{0}:

((x))^{v} = ˚ x
((z))^{v} = ˚ z
((s E))^{v} = let˚ x ⇐ ((E))^{v} in˚ (s x)
((ifz(E, E_{z}, x. E_{s})))^{v} = let˚ n ⇐ ((E))^{v} in ifz(n, ((E_{z}))^{v}, x. ((E_{s}))^{v})
((⟨⟩))^{v} = ˚ ⟨⟩
((⟨E_{1}, E_{2}⟩))^{v} = let˚ x_{1} ⇐ ((E_{1}))^{v} in let˚ x_{2} ⇐ ((E_{2}))^{v} in˚ ⟨x_{1}, x_{2}⟩
((fst E))^{v} = let˚ x ⇐ ((E))^{v} in˚ (fst x)
((snd E))^{v} = let˚ x ⇐ ((E))^{v} in˚ (snd x)
((inl E))^{v} = let˚ x ⇐ ((E))^{v} in˚ (inl x)
((inr E))^{v} = let˚ x ⇐ ((E))^{v} in˚ (inr x)
((case(E, x_{1}.E_{1}, x_{2}.E_{2})))^{v} = let˚ x ⇐ ((E))^{v} in case(x, x_{1}.((E_{1}))^{v}, x_{2}.((E_{2}))^{v})
((λx^{σ}.E))^{v} = ˚ (λx^{((σ))^{v}}.((E))^{v})
((E_{1} E_{2}))^{v} = let˚ f ⇐ ((E_{1}))^{v} in let˚ a ⇐ ((E_{2}))^{v} in f a
((fix_{σ} E))^{v} = let˚ F ⇐ ((E))^{v} in˚ (fix_{((σ))^{v}} (λf.let_{˚ ((σ))^{v}} f′ ⇐ F f in f′))

where for the CBV fix, σ must be a functional type, so that ((σ))^{v} is a computation-type. (We can actually also allow it to be a product of computation-types, if in the context of mutually-recursive definitions we interpret × as the computation-product ×^{c} .) The explicit let_{((σ))^{v}} is necessary because the type of F is ((σ → σ))^{v} = ((σ))^{v} →˚ ((σ))^{v}.

When E is syntactically a value V, we have ((V))^{v} = ˚ M for some M. Thus, for example, we get validity of beta-value reduction because

(((λx.E)V))^{v} = let˚ f ⇐ ((λx.E))^{v} in let˚ a ⇐ ((V))^{v} in f a

= let˚ f ⇐˚ (λx.((E))^{v}) in let˚ a ⇐˚ M in f a = (λx.((E))^{v})M = ((E))^{v}{M/x}

= ((E{V/x}))^{v}

Similarly, in general we have

((λx.E x))^{v} =˚ (λx.let˚ f ⇐ ((E))^{v} in f x) =^{?} ((E))^{v}

but when ((E))^{v} =˚ M for some M, the equality does hold.

CBN A CBN interpretation gives a language essentially identical to PCF with product and sum types. The type translation is now:

((ι))^{n} = ˚ ι
((1))^{n} = 1
((σ_{1} × σ_{2}))^{n} = ((σ_{1}))^{n} ×^{c} ((σ_{2}))^{n}
((σ_{1} + σ_{2}))^{n} = ˚ (((σ_{1}))^{n} + ((σ_{2}))^{n})
((σ_{1} → σ_{2}))^{n} = ((σ_{1}))^{n} → ((σ_{2}))^{n}

Since the interpretation of every type is computational, we can form exponentials between any pair of types exponentials, and thus the source language forms a ccc. When the elaboration is composed with our predomain semantics, every type is interpreted by a (proper) domain; in fact, for a partiality semantics, this gives exactly the standard domain-theoretic model of PCF.

Translation on terms. If Γ ⊢ E : σ in the source language, then ((Γ))^{n} ⊢ ((E))^{n} : ((σ))^{n} in L_{0}.

((x))^{n} = x
((z))^{n} = ˚ z
((s E))^{n} = let˚ x ⇐ ((E))^{n} in˚ (s x)
((ifz(E, E_{z}, x. E_{s})))^{n} = let_{˚ ((σ))^{n}} n ⇐ ((E))^{n} in ifz(n, ((E_{z}))^{n}, x. ((E_{s}))^{n}{˚ x/x})
((⟨⟩))^{n} = ⟨⟩
((⟨E_{1}, E_{2}⟩))^{n} = ⟨((E_{1}))^{n}, ((E_{2}))^{n}⟩
((fst E))^{n} = fst ((E))^{n}
((snd E))^{n} = snd ((E))^{n}
((inl E))^{n} = ˚ (inl ((E))^{n})
((inr E))^{n} = ˚ (inr ((E))^{n})
((case(E, x_{1}.E_{1}, x_{2}.E_{2})))^{n} = let_{˚ ((σ))^{n}} x ⇐ ((E))^{n} in case(x, x_{1}.((E_{1}))^{n}, x_{2}.((E_{2}))^{n})
((λx^{σ}.E))^{n} = λx^{((σ))^{n}}.((E))^{n}
((E_{1} E_{2}))^{n} = ((E_{1}))^{n} ((E_{2}))^{n}
((fix_{σ} E))^{n} = fix_{((σ))^{n}} ((E))^{n}

where we now have fixed points at all source types, including ι. Note also that numbers are still represented by a flat domain (as opposed to the lazy natural numbers, which also include partially-defined values, such as s ⊥).

2.2 Monads in a computational setting

In this section, we present a formal definition of monads, suitable for a language that already has a notion of ambient effects. This definition is phrased in terms of few basic concepts, which we need to introduce first.

2.2.1 A framework for effects

The ultimate goal of the line of research presented here is a framework for computational effects which makes it possible to describe effects in a modular way. Specifically, we want the ability to add effects incrementally: the resulting language is specified by a sequence of definitional translations, each one of which “translates away” one level of effects. For example, we can have a language with exceptions and state, specified as a composition of an exception-passing and a state-passing transform.

For now, however, we only consider the two-level case, with a notion of ambient effects (possibly already a combination of several primitive ones), specified by the “semantic” monad T used in the denotational semantics); and a focus effect, specified by a “syntactic” monad T.

In order to define the notion of a monad that interacts in a suitable way with ambient effects, we need some amount of structure in the language. The following provides what we will need:

Definition 2.8 We say that a language (L, L) is a computational lambda-language (cll) if it has a class of computation types β, forming a (not necessarily proper) subset of all types α, and with the following properties:

• There are computations at any type, and the set of computation-types is closed under finite products and function spaces (with arbitrary domain):

\frac{α type}{˚ α ctype}  \overline{1 ctype}  \frac{β_{1} ctype  β_{2} ctype}{β_{1} ×^{c} β_{2} ctype}  \frac{α type  β ctype}{α → β ctype}

We write ⊢_{Δ} α type and ⊢_{Δ} β ctype for types over a set of type variables Δ, but do not require L to assign any meaning to such types when Δ is nonempty.

• The syntax L includes at least the following terms and term constructors:

\frac{(x: α) ∈ Γ}{Γ ⊢ x : α}  \frac{Γ ⊢ M : α}{Γ ⊢˚ M :˚ α}  \frac{Γ ⊢ M_{1} :˚ α  Γ, x: α ⊢ M_{2} : β}{Γ ⊢ let_{˚ β} x ⇐ M_{1} in M_{2} : β}

(that is, we have variables, computation-inclusions, and a generalized let), together with the term constructors for products and function spaces. Again, we write Γ ⊢_{Δ} M : α for a term over Δ, not necessarily given a meaning by L.

• In the semantics L, the following equations hold (between type-closed terms):

\frac{Γ ⊢ M_{1} : α  Γ, x: α ⊢ M_{2} : β}{Γ ⊢ (let_{˚ β} x ⇐˚ M_{1} in M_{2}) = M_{2}\{M_{1}/x\} : β}  \frac{Γ ⊢ M :˚ α}{Γ ⊢ (let_{˚˚ α} x ⇐ M in˚ x) = M :˚ α}

\frac{Γ ⊢ M_{1} :˚ α_{1}  Γ, x_{1}: α_{1} ⊢ M_{2} :˚ α_{2}  Γ, x_{2}: α_{2} ⊢ M_{3} : β}{Γ ⊢ (let_{˚ β} x_{2} ⇐ (let_{˚˚ α_{2}} x_{1} ⇐ M_{1} in M_{2}) in M_{3}) = (let_{˚ β} x_{1} ⇐ M_{1} in let_{˚ β} x_{2} ⇐ M_{2} in M_{3}) : β}

together with the congruence and substitution rules, as well as the axioms for unit, products, and functions (as listed in Section 2.1.5). And finally, the generalized let must satisfy (not necessarily directly by definition) the equations in Definition 2.4.

For example, in any cll, for every type α, there exists a (computation-)type α → α → ˚ α ×˚ α. Note that this is a slightly stronger requirement than Moggi’s T -exponentials [Mog89], which only guaranteed existence of all function spaces of the form α_{1} →˚ α_{2}.

A weaker notion would be to take the computation types to be exactly the set of types of the form˚ α. However, requiring computation-types to be closed under products and (especially) function spaces will allow us to give a uniform treatment of definable computational effects.

Proposition 2.9 Our base language (with any model L satisfying the equations in Section 2.1.5) can be organized as a cll by defining let_{β} inductively as in Definition 2.4.

Proof. We only need to verify that the equational properties of the generalized let hold for the definition. The proof is a simple induction on β. We show two sample cases; the others are very similar.

let_{˚ β_{1}×β_{2}} x ⇐˚ M_{1} in M_{2} = ⟨let_{˚ β_{1}} x ⇐˚ M_{1} in fst M_{2}, let_{˚ β_{2}} x ⇐˚ M_{1} in snd M_{2}⟩

=^{ih} ⟨fst M_{2}{M_{1}/x}, snd M_{2}{M_{1}/x}⟩ = ⟨fst M_{2}, snd M_{2}⟩{M_{1}/x} = M_{2}{M_{1}/x}

let_{˚ α→β} x_{2} ⇐ (let_{˚˚ α_{2}} x_{1} ⇐ M_{1} in M_{2}) in M_{3} = λa.let_{˚ β} x_{2} ⇐ (let_{˚˚ α_{2}} x_{1} ⇐ M_{1} in M_{2}) in M_{3} a

=^{ih} λa.let_{˚ β} x_{1} ⇐ M_{1} in let_{˚ β} x_{2} ⇐ M_{2} in M_{3} a

= λa.let_{˚ β} x_{1} ⇐ M_{1} in (λa.let_{˚ β} x_{2} ⇐ M_{2} in M_{3} a)a = λa.let_{˚ β} x_{1} ⇐ M_{1} in (let_{˚ α→β} x_{2} ⇐ M_{2} in M_{3})a = let_{˚ α→β} x_{1} ⇐ M_{1} in let_{˚ α→β} x_{2} ⇐ M_{2} in M_{3}

Other ways of constructing computation-types may be possible, depending on the actual set of types available. For example, in a language with explicit polymorphism, it seems natural to take β ::= ⋯ | ∀a. β, with the generalized let extended accordingly.

For lack of a better name, we say that a computational λ-language is effect-free if α and˚ α are actually the same type (with˚ M = M); in this case, the cll requirements degenerate to those of a ccc. But effect-freeness should not be confused with existence of a type α → α′ (with associated abstraction and application operations) for all α′ – we can have the latter without the former.

(We do not actually work with any concrete effect-free languages; the concept is mainly used to show that various definitions and results reduce to their more familiar counterparts in the existing work on monads for computational effects.)

Remark 2.10 The essence of a generalized let at a computation-type β can be expressed simply as existence of the function

ζ_{β} :˚ β → β =^{def} λm.let_{˚ β} x ⇐ m in x

satisfying the equations

x: β ⊢ ζ_{β} (˚ x) = x : β

and

m:˚ (˚ β) ⊢ ζ_{β} (let˚ x ⇐ m in x) = ζ_{β} (let˚ x ⇐ m in˚ (ζ_{β} x)) : β

(In category-theoretic terms, this says that ζ_{β} is the structure map of an algebra (β, ζ) for the monad underlying ˚ [ML71, VI.2].) Specifically, given such a function, we can define a generalized-let operator by

let_{˚ β} x ⇐ M_{1} in M_{2} = ζ_{β} (let˚ x ⇐ M_{1} in˚ M_{2})

However, the generalized-let formulation is more convenient to work with, its equational properties being a natural generalization of the existing let, as formalized in Definition 2.8. Remember also that our generalized let (or, equivalently, ζ) is characterized uniquely by Definition 2.4.

Among other applications, the generalized let-operation can be used to define a simple “effect-theoretic” generalization of strictness, which in turn plays a key role in the definition of layerable monads.

2.2.2 Rigidity

Definition 2.11 We say that a term Γ ⊢ M : β → β′ in a cll is a rigid function between computation-types β and β′ if

Γ, m:˚ β ⊢ M (let_{˚ β} x ⇐ m in x) = (let_{˚ β′} x ⇐ m in M x) : β′

We write this as Γ ⊢ M : β →^{r} β′.

Rigidity is a purely equational property; as such, we distinguish between provable rigidity (i.e., when the above equation is derivable in an equational theory) and semantic rigidity (when the equation holds in a model); the former implies the latter.

As an immediate consequence of the definition, we get:

Lemma 2.12 An application of a rigid function can be “moved through” an arbitrary let-binding:

\frac{Γ ⊢ M : β →^{r} β′  Γ ⊢ M_{1} :˚ α  Γ, x: α ⊢ M_{2} : β}{Γ ⊢ M (let_{˚ β} x ⇐ M_{1} in M_{2}) = (let_{˚ β′} x ⇐ M_{1} in M M_{2}) : β′}

(i.e., the above is derivable in E_{0} and hence true in any model.)

Proof. Simple verification:

M (let_{˚ β} x ⇐ M_{1} in M_{2}) = M (let_{˚ β} x ⇐ M_{1} in let_{˚ β} y ⇐˚ M_{2} in y)
= M (let_{˚ β} y ⇐ (let_{˚˚ β} x ⇐ M_{1} in˚ M_{2}) in y)
= (M (let_{˚ β} y ⇐ m in y)){(let_{˚˚ β} x ⇐ M_{1} in˚ M_{2})/m}
=^{†} (let_{˚ β′} y ⇐ m in M y){(let_{˚˚ β} x ⇐ M_{1} in˚ M_{2})/m}
= let_{˚ β′} y ⇐ (let_{˚˚ β} x ⇐ M_{1} in˚ M_{2}) in M y
= let_{˚ β′} x ⇐ M_{1} in let_{˚ β′} y ⇐˚ M_{2} in M y = let_{˚ β′} x ⇐ M_{1} in M M_{2}

where † marks the application of rigidity of M.

In particular, for any M′ :˚ α and rigid M :˚ α →˚ α′,

M M′ = M (let˚ x ⇐ M′ in˚ x) = let˚ x ⇐ M′ in M (˚ x)

Operationally, this says that an argument to a rigid function can be evaluated before the call and the result coerced into a trivial computation, instead of the nominal CBN evaluation for parameters of˚ -type. This is usually a property associated with strictness. And indeed we have

Proposition 2.13 In the predomain model (for any T ) of our base language, a rigid function is necessarily strict. In the particular case of the partiality semantics, the converse also holds, i.e., any strict function is rigid.

Proof. First, it is easy to check the following equational reasoning principle:

\frac{Γ, x: α ⊢ M : β}{Γ ⊢ (let_{˚ β} x ⇐ ⊥_{˚ α} in M) = ⊥_{β} : β}

(because for any f, f^{⋄} is strict, and L[[⊥_{β}]](ρ) = ⊥_{L[[β]]}). Now, let h : β → β′ be a rigid function. Then

h⊥_{β} = h(let_{˚ β} x ⇐ ⊥_{˚ 1} in ⊥_{β}) = let_{˚ β′} x ⇐ ⊥_{˚ 1} in h⊥_{β} = ⊥_{β′}

Conversely, let h be strict and let m : ˚ β. When T A = A_{⊥}, there are only two possibilities for m:

• m = ⊥_{˚ β}. Then

h(let_{˚ β} x ⇐ m in x) = h(let_{˚ β} x ⇐ ⊥_{˚ β} in x) = h⊥_{β} = ⊥_{β′}

= let_{˚ β′} x ⇐ ⊥_{˚ β} in hx = let_{˚ β′} x ⇐ m in hx

• m =˚ b for some b : β:

h(let_{˚ β} x ⇐ m in x) = h(let_{˚ β} x ⇐˚ b in x) = hb = let_{˚ β′} x ⇐˚ b in hx

= let_{˚ β′} x ⇐ m in hx

In general, a function is rigid if it uses its argument exactly once, and before any other serious computation. But in the particular case of partiality, a function like h = λx. ⊥ (= λx. let˚ y ⇐ x in ⊥ in the model) also qualifies as rigid, even though it does not explicitly reference its argument.

It is easy to check that identity and composition of two rigid functions are rigid; so are fst, snd, λf. f a for any a, and λx.let˚ a ⇐ x in f a for any f. Likewise, if f_{1} and f_{2} are rigid, so is λx.⟨f_{1} x, f_{2} x⟩, and if f a is rigid for every a, so is λx.λa.f ax. These are well-known properties of strictness, but also hold for general rigidity.

In the effect-free case (˚ α = α), every function is trivially rigid.

Remark 2.14 We can give an alternative, equivalent characterization of rigidity. There is a natural functorial action of˚ , mapping a function f : β → β′ to

f^{♯} :˚ β →˚ β′ = λm.let˚ x ⇐ m in˚ (f x)

Further, recall from remark 2.10 that for any β, we can define

ζ_{β} :˚ β → β = λm.let_{˚ β} x ⇐ m in x .

Then a function f : β → β′ is rigid iff ζ_{β′} ∘ f^{♯} = f ∘ ζ_{β} (i.e., if f is a morphism of the corresponding˚ -algebras), because

(ζ_{β′} ∘ f^{♯})m = ζ_{β′} (let˚ x ⇐ m in˚ (f x)) = let_{˚ β′} y ⇐ (let˚ x ⇐ m in˚ (f x)) in y

= let_{˚ β′} x ⇐ m in let_{˚ β′} y ⇐˚ (f x) in y = let_{˚ β′} x ⇐ m in f x

while

(f ∘ ζ_{β})m = f (let_{˚ β} x ⇐ m in x) .

2.2.3 Definable monads

The notion of a monad in a language (L, L) consists of both a syntactic and a semantic aspect. Syntactically, we exhibit a type constructor T and term families η and ^{∗} in L. Semantically, we establish that certain equational properties hold among these terms in L (but not necessarily in any particular equational theory for L). The separation is important – we will eventually have to consider interpretations of the η and ^{∗} in a semantics where they do not necessarily satisfy the monad laws.

When ⊢_{Δ} α type is a type over Δ in L and θ is a substitution of (closed) L-types for variables in Δ, α{θ} is itself a (closed) type of L. In particular, a type constructor F a = α (where a may occur in α) can be identified with a type schema ⊢_{{a}} α type. Analogously, given a term Γ ⊢ M : α, θ determines a (type-closed) L-term Γ{θ} ⊢ M{θ} : α{θ}.

We can now give a formal definition of a monad (in the Kleisli-triple formulation):

Definition 2.15 Let L be a signature of a cll. A monad-triple T in L consists of the following items:

• A computation-type constructor, ⊢_{{a}} T a ctype. We write T α for T a{α/a}.

• A term family of unit functions, given as instances of a term ⊢_{{a}} η_{a} : a → T a. We write η_{α} for η_{a}{α/a}.

• A term family of extension operators, f: a_{1} → T a_{2} ⊢_{{a_{1},a_{2}}} f^{∗} : T a_{1} → T a_{2} (strictly speaking, type-indexed as above, but we always omit the type indices).

Such a triple is an actual monad in the cll (L, L) if in L the following equations hold at all closed type instances:

0. f: α_{1} → T α_{2} ⊢ f^{∗} : T α_{1} →^{r} T α_{2}.

1. f: α_{1} → T α_{2} ⊢ f^{∗} ∘ η_{α_{1}} = f : α_{1} → T α_{2}.

2. ⊢ η_{α}^{∗} = id_{T α} : T α → T α.

3. f: α_{1} → T α_{2}, g: α_{2} → T α_{3} ⊢ f^{∗} ∘ g^{∗} = (f^{∗} ∘ g)^{∗} : T α_{1} → T α_{3}.

(Note that (0) is an equational condition like the others, because of its expansion in Definition 2.11. Conditions (0–3) also cover equations between non-variable terms, such as M_{1}^{∗} (η M_{2}) = M_{1} M_{2}, because of closure under substitution of terms for variables.)

Actually the above definition is more akin to that of a monad constructor than of a simple monad; the necessary information for composition is implicit in the representation of the monad in the computational language. Nevertheless, we will refer to it as a monad over L, since that is where the monad laws are required to hold – as opposed to being provable in some equational theory for L. Of course, showing the monad laws in the equational theory is sufficient to establish them for a model of that theory.

Note that condition (0) only makes sense because both T α_{1} and T α_{2} are required to be computation types. When L is effect-free, the rigidity requirement is vacuous, and the definition reduces to that of an ordinary monad.

Although it is important for our concrete language that rigidity implies strictness, the rigidity requirement for f^{∗} is not merely present for domain-theoretic reasons; it is crucial for composing effects in general, and would be present even in a purely settheoretic formulation of composable monads in a setting without general recursion. In practice, natural monad extensions always seem to be rigid anyway.

Example 2.16 (Identity) Perhaps the simplest possible monad, definable in any cll, is given by:

T α =˚ α,  η = λa.˚ a,  f^{∗} = λt.let˚ a ⇐ t in f a

The verification of the monad laws is straightforward. The identity monad is actually a degenerate case of many others; for example, we obtain it by specializing the exception monad below to χ = 0 (a type with no values, hence no possibility of raising an exception) or the state monad to σ = 1 (a type with one value, hence an information-free state).

Example 2.17 (Exceptions) Let χ be some fixed type of exception names (exn in SML). We then obtain a monad by:

T α =˚ (α + χ),  η = λa.˚ (inl a),  f^{∗} = λt.let˚ v ⇐ t in case(v, a.f a, e.˚ (inr e))

For completeness, we show the complete verification, since it is slightly more involved than for an exception monad over an effect-free language:

f^{∗} (let_{˚ T α_{1}} x ⇐ m in x) = let˚ v ⇐ (let_{˚˚ (α_{1}+χ)} x ⇐ m in x) in case(v, a.f a, e.˚ (inr e))

= let˚ x ⇐ m in let˚ v ⇐ x in case(v, a.f a, e.˚ (inr e)) = let_{˚ T α_{2}} x ⇐ m in f^{∗} x

f^{∗} ∘ η = λa.f^{∗} (η a) = λa.let˚ v ⇐˚ (inl a) in case(v, a.f a, e.˚ (inr e))

= λa. case(inl a, a.f a, e.˚ (inr e)) = λa.f a = f

η^{∗} = λt.let˚ v ⇐ t in case(v, a.˚ (inl a), e.˚ (inr e)) = λt.let˚ v ⇐ t in˚ v = λt.t = id

g^{∗} ∘ f^{∗} = λt.g^{∗} (f^{∗} t) = λt.g^{∗} (let˚ v ⇐ t in case(v, a.f a, e.˚ (inr e)))

= λt.let˚ v ⇐ t in g^{∗} (case(v, a.f a, e.˚ (inr e)))
= λt.let˚ v ⇐ t in case(v, a.g^{∗} (f a), e.g^{∗} (˚ (inr e)))
= λt.let˚ v ⇐ t in case(v, a.g^{∗} (f a), e.let˚ w ⇐˚ (inr e) in case(w, b.g b, e.˚ (inr e)))
= λt.let˚ v ⇐ t in case(v, a.g^{∗} (f a), e.case(inr e, b.g b, e.˚ (inr e)))
= λt.let˚ v ⇐ t in case(v, a.g^{∗} (f a), e.˚ (inr e)) = (λa.g^{∗} (f a))^{∗} = (g^{∗} ∘ f)^{∗}

(Note that the type and term constructors are in the image of the CBN translation; thus exceptions also form a monad in a language like Haskell, where the language-level sum type is actually a “separated sum” in domain terminology.)

Example 2.18 (State) Let σ be any type. Then the σ-state monad is defined by:

T α = σ →˚ (α × σ),  η = λa.λs.˚ ⟨a, s⟩,  f^{∗} = λt.λs.let˚ ⟨a, s′⟩ ⇐ ts in f as′

Again, the verification is fairly simple:

f^{∗} (let_{˚ T α_{1}} x ⇐ m in x) = λs.let˚ ⟨a, s′⟩ ⇐ (let_{˚ σ→˚ (α_{1}×σ)} x ⇐ m in x)s in f as′
= λs.let˚ ⟨a, s′⟩ ⇐ (let_{˚˚ (α_{1}×σ)} x ⇐ m in xs) in f as′
= λs.let˚ x ⇐ m in let˚ ⟨a, s′⟩ ⇐ xs in f as′ = λs.let˚ x ⇐ m in f^{∗} xs
= let_{˚ T α_{2}} x ⇐ m in f^{∗} x

f ∘ η = λa.f^{∗} (η a) = λa.λs.let˚ ⟨a, s′⟩ ⇐ [λs.˚ ⟨a, s⟩]s in f as′ = λa.λs.let˚ ⟨a, s′⟩ ⇐˚ ⟨a, s⟩ in f as′ = λa.λs.f (fst ⟨a, s⟩)(snd ⟨a, s⟩) = λa.λs.f as

= λa.f a = f

η^{∗} = λt.λs.let˚ ⟨a, s′⟩ ⇐ ts in [λa.λs.˚ ⟨a, s⟩]as′ = λt.λs.let˚ ⟨a, s′⟩ ⇐ ts in˚ ⟨a, s′⟩

= λt.λs.let˚ p ⇐ ts in˚ p = λt.λs.ts = λt.t = id

g^{∗} ∘ f^{∗} = λt. g^{∗} (f^{∗} t) = λt. g^{∗} (λs.let˚ ⟨a, s_{1}⟩ ⇐ ts in f as_{1})

= λt. λs.let˚ ⟨b, s_{2}⟩ ⇐ (let˚ ⟨a, s_{1}⟩ ⇐ ts in f as_{1}) in g bs_{2}
= λt. λs.let˚ ⟨a, s_{1}⟩ ⇐ ts in let˚ ⟨b, s_{2}⟩ ⇐ f as_{1} in g bs_{2}

= λt. λs.let˚ ⟨a, s_{1}⟩ ⇐ ts in g^{∗} (f a)s_{1} = λt. λs.let˚ ⟨a, s_{1}⟩ ⇐ ts in [λa.g^{∗} (f a)]as_{1}

= (λa.g^{∗} (f a))^{∗} = (g^{∗} ∘ f)^{∗}

Although most practically useful monads over (L_{0}, L) are actually monads in any model of the equational theory E_{0}, there are two important reasons to only require the monad laws to hold with respect to specific interpretations. First, since the monad components may be defined using fix, it can be arbitrarily hard to show that a given monad-triple is actually a monad; certainly E_{0} alone will not always be sufficient. We only need E_{0} to validate a few equational properties that will be used frequently in the proofs later; the results do not rely on the monad laws for particular monads being provable in E_{0}.

The second, and more fundamental, reason is that certain very useful notions of computation do not actually form monads in the presence of arbitrary ambient effects. Perhaps the best known such example [KW93] is the list monad T α = ˚ (α list), used to model nondeterminism. It turns out to only be a monad if the ambient effects are commutative, i.e., if the equation

let˚ x_{1} ⇐ M_{1} in let˚ x_{2} ⇐ M_{2} in M = let˚ x_{2} ⇐ M_{2} in let˚ x_{1} ⇐ M_{1} in M

(where neither x_{i} occurs free in an M_{j}) holds in L. Partiality satisfies the above equation, but many other possible notions of ambient effects, such as state or continuations, do not. Other examples of “fragile” monads require the ambient effects to also be idempotent, a property shared by few effects other than partiality.

Thus, distinguishing between satisfaction of the monad laws in the equational theory and in a specific model (such as the partiality semantics) makes our results applicable to list-like monads as well as the “robust” ones (such as exceptions or state), that satisfy the monad laws for any notion of ambient effect.

Let us finally note that given a semantics that also assigns a meaning to type-open types and terms, a stronger definition of monad is possible:

Definition 2.19 When T is a monad in (L, L) where L is the predomain interpretation for any T , T is said to be uniform if its equations also hold for type-open terms. That is, for each of the four monad laws Γ ⊢ M_{1} = M_{2} : α in Definition 2.15, if we allow the types and terms to contain type variables from Δ, θ assigns a cpo to each a ∈ Δ, and ρx_{i} ∈ L[[α_{i}]]^{θ} for each (x_{i}: α_{i}) ∈ Γ then L[[M_{1}]]^{θ}(ρ) = L[[M_{2}]]^{θ}(ρ) as elements of L[[α]]^{θ}.

We can then state the simple consequence:

Proposition 2.20 Let T be a uniform monad in the predomain semantics (for some notion of ambient effect). Then the following determines a new ambient-effect monad in the sense of Definition 2.2:

T A = L[[T a]]^{a ↦ A}  ξ_{A} = L[[η_{a}]]^{a ↦ A}(•)  f^{⋄} = L[[x^{∗}]]^{a_{1} ↦ A_{1},a_{2} ↦ A_{2}}(•[x ↦ f])

Proof. We first note that because computation-types were interpreted as pointed cpos by L, T A is pointed as required. Similarly, because arrow-types are interpreted as continuous-function spaces, ξ_{A} and f^{⋄} are continuous; and because L[[M]] is a continuous function from environments to values, so is the mapping f ↦ f^{⋄}. Finally, by Proposition 2.13, we get strictness of f^{⋄} from rigidity of x^{∗}. The verification of the monad laws is also straightforward given uniformity of T.

However, usually there is no need to modify the semantic characterization of ambient effects explicitly; we can define a language with a new notion of ambient effects via iterated monadic translation, in which case it is sufficient for the monad laws to hold only for type-closed instances.

2.3 Extending the language with effects

2.3.1 The monadic translation

We now show how a monad in a language allows us to define a new language with a richer set of computational effects.

Definition 2.21 Let T = (T, η, ^{∗}) be a monad-triple over a cll signature L. Then the signature L^{T} consists of L extended with a new computation-type constructor,

\frac{⊢_{Δ} α type}{⊢_{Δ} α ctype}

and new term constructors:

\frac{Γ ⊢ M_{1} :˚ α_{1} Γ, x: α_{1} ⊢ M_{2} : α_{2}}{Γ ⊢ let˚ x ⇐ M_{1} in M_{2} : α_{2}}

\frac{Γ ⊢ M : α}{Γ ⊢ M : α}  \frac{Γ ⊢ M_{1} : α_{1} Γ, x: α ⊢ M_{2} : α_{2}}{Γ ⊢ let x ⇐ M_{1} in M_{2} : α_{2}}

\frac{Γ ⊢ M : α}{Γ ⊢ [M] : T α}  \frac{Γ ⊢ M : T α}{Γ ⊢ μ(M) : α}

(Note that we overload the syntactic construct let˚ to represent two distinct term constructors: the existing one, where M_{2} :˚ α_{2} and the new one defined above, where M_{2} : α_{2}. It will always be clear from context which one is meant.)

There are now two basic notions of computation: the original ˚ α (e.g., partiality) and α which also includes T -effects (e.g., raising exceptions). As before, the set of computation-types is closed under products and function spaces.

Because we have extended the signature (rather than merely the unstructured set of types), every type constructor of L is still a type constructor of L^{T}. In particular, for any L^{T} -type α, T α is a well-formed L^{T} -type. Generalized let (still for binding results of ˚ -computations) is also definable at all computation-types, with the new clause for let_{˚ α} using the mixed-level let.

Reflection (μ(M)) and reification ([M]) establish a correspondence between opaque and transparent representation of computations: opaque computations may only be constructed and sequenced using and let , while transparent ones may be manipulated using the full range of operations available on the type T α, such as injections, case analysis, etc. See Example 2.26 below.

Together with the extension, we define a canonical or definitional translation of the extended signature back into the original one.

Since we will be dealing with several source-to-source translations, let us introduce the following shorthand:

Definition 2.22 (Translation convention) When specifying a translation [[ ]] from a signature L to L′ that share a lot of operations, we generally omit clauses of the form

[[φ(X_{1}, …, X_{n})]] = φ([[X_{1}]], …, [[X_{n}]])

where the construct φ in L is translated to the same-named construct in L′. (We do occasionally include selected clauses of this form for emphasis or clarity; but no formal distinction should be attached to whether a clause is included or not.)

Definition 2.23 The monadic translation [[ ]]_{T} maps types and terms of L^{T} to their L-counterparts, such that:

• For any ⊢_{Δ} α type in L^{T}, ⊢_{Δ} [[α]]_{T} type in L.

• For any ⊢_{Δ} β ctype in L^{T}, ⊢_{Δ} [[β]]_{T} ctype in L.

• For any Γ ⊢_{Δ} M : α in L^{T}, [[Γ]]_{T} ⊢_{Δ} [[M]]_{T} : [[α]]_{T} in L.

The translation on types merely replaces α with its definitional expansion:

[[ α]]_{T} = T [[α]]_{T}

Other type constructors are left intact, as are type variables (i.e., [[a]]_{T} = a). Similarly, the term translation expands away the new term constructors:

[[ M]]_{T} = η [[M]]_{T}
[[let˚ x ⇐ M_{1} in M_{2} (: α)]]_{T} = let_{˚ T [[α]]_{T}} x ⇐ [[M_{1}]]_{T} in [[M_{2}]]_{T}
[[let x ⇐ M_{1} in M_{2}]]_{T} = (λx.[[M_{2}]]_{T})^{∗} [[M_{1}]]_{T}
[[μ(M)]]_{T} = [[M]]_{T}
[[[M]]]_{T} = [[M]]_{T}

with variables and other term constructors of L translated into themselves (but with any type-annotations expanded according to the type translation).

Although the monadic translation is simply a definitional extension, rather than a full syntactic transformation, we adopt the translation formulation to get an explicit syntactic handle on the expansion. In particular, when we later consider alternative ways of translating away the new constructs of L^{T}, it will be convenient to have an concise notation for referring to the different expansions.

Note also that because type and term variables are translated into themselves, the translations are compositional in the sense that

[[α{α′/a}]]_{T} = [[α]]_{T} {[[α′]]_{T} /a} and [[M{M′/x}]]_{T} = [[M]]_{T} {[[M′]]_{T} /x} .

In particular,

[[T α]]_{T} = [[T a{α/a}]]_{T} = [[T a]]_{T} {[[α]]_{T} /a} = T a{[[α]]_{T} /a} = T [[α]]_{T} ,

and likewise for the term translations of the monad components: [[η_{α}]]_{T} = η_{[[α]]_{T}} and [[f^{∗}]]_{T} = f^{∗} (with the implicit type-tags on ^{∗} appropriately translated).

The translation of the “mixed let” may need a little explanation. Consider the case where T is the state monad, and the base effect is partiality. Then if in the extended language, Γ ⊢ M_{1} : ˚ α_{1} (i.e., evaluation of M_{1} may diverge, but has no state effects), [[M_{1}]]_{T} :˚ [[α_{1}]]_{T} does not take a state argument, nor does it return a new state. On the other hand, when Γ, x: α ⊢ M_{2} : α_{2} (i.e., M_{2} may both diverge and access the store), [[M_{2}]]_{T} : σ →˚ ([[α_{2}]]_{T} × σ), so the translation of M_{2} should be passed the current state, and the new state it returns is the state returned by the whole let-expression. The appropriate state-passing translation is therefore

[[let˚ x ⇐ M_{1} in M_{2}]]_{T} = λs.let˚ x ⇐ [[M_{1}]]_{T} in [[M_{2}]]_{T} s

which is precisely what the generalized let expands to.

More generally, it is easy to check the following derived rule, where β is an L^{T} - computation type (i.e., may contain ):

[[let_{˚ β} x ⇐ M_{1} in M_{2}]]_{T} = let_{˚ [[β]]_{T}} x ⇐ [[M_{1}]]_{T} in [[M_{2}]]_{T}

In the definitional translation, the opaque and transparent T -computations are represented by the same underlying L-type; consequently, the term translations for reflection and reification are trivial. Later, when we consider a different representation of effects, the two operators will have more interesting definitions.

This syntactic translation also determines a semantics:

Definition 2.24 Given a semantics L for our base language L, we obtain a semantics L^{T} of the extended language L^{T} by taking

L^{T} [[α]]^{θ} = L[[[[α]]_{T}]]^{θ} and L^{T} [[M]]^{θ} = L[[[[M]]_{T}]]^{θ}

In fact, this semantics extends the standard monadic semantics for the new ambienteffect monad induced by T:

Proposition 2.25 Let L be a predomain semantics of L_{0} with some underlying ambienteffect monad, T a uniform monad in that semantics (Definition 2.19), and let | | : L_{0} → L_{0}^{T} be the syntactic transformation replacing every˚ in types and terms with .

Then L^{T} [[| |]] = L_{T} [[ ]] (for types and terms) where L_{T} is the monadic semantics of L_{0} for the ambient-effect monad T given by interpreting in L the components of T, as shown in Proposition 2.20.

Proof. Induction on the structure of the types and terms. Most cases are immediate; for computations, we get:

L^{T} [[|˚ α|]]^{θ} = L^{T} [[ |α|]]^{θ} = L[[[[ |α|]]_{T}]]^{θ} = L[[T [[|α|]]_{T}]]^{θ} = L[[(T a){[[|α|]]_{T} /a}]]^{θ}

= L[[T a]]^{θ[a ↦ L[[[[|α|]]_{T}]]^{θ}]} = L[[T a]]^{a ↦ L[[[[|α|]]_{T}]]^{θ}} =^{ih} L[[T a]]^{a ↦ L_{T} [[α]]^{θ}} = T (L_{T} [[α]]^{θ})

= L_{T} [[˚ α]]^{θ}

L^{T} [[|˚ M|]]^{θ}(ρ) = L^{T} [[ |M|]]^{θ}(ρ) = L[[[[ |M|]]_{T}]]^{θ}(ρ) = L[[η_{[[|α|]]_{T}} [[|M|]]_{T}]]^{θ}(ρ)

= L[[η_{[[|α|]]_{T}}]]^{θ}(ρ)(L[[[[|M|]]_{T}]]^{θ}(ρ)) = L[[η_{a}{[[|α|]]_{T} /a}]]^{θ}(ρ)(L[[[[|M|]]_{T}]]^{θ}(ρ))

= L[[η_{a}]]^{θ[a ↦ L[[[[|α|]]_{T}]]^{θ}]}(ρ)(L[[[[|M|]]_{T}]]^{θ}(ρ)) = L[[η_{a}]]^{a ↦ L[[[[|α|]]_{T}]]^{θ}}(•)(L[[[[|M|]]_{T}]]^{θ}(ρ))

=^{ih} L[[η_{a}]]^{a ↦ L_{T} [[α]]^{θ}}(•)(L_{T} [[M]]^{θ}(ρ)) = ξ_{L_{T} [[α]]^{θ}}(L_{T} [[M]]^{θ}(ρ)) = L_{T} [[˚ M]]^{θ}(ρ)

The case for let˚ x ⇐ M_{1} in M_{2} is similar.

Similarly, given an evaluation semantics for L (i.e., a computable partial function Eval_{L} from closed L-terms of type˚ ι to natural numbers), we get an evaluation semantics for L^{T} by taking Eval_{L^{T}} (M) = Eval_{L}([[M]]_{T}). (We can do this directly, regardless of T, because the T -translation of a term of type˚ ι is itself a term of type˚ ι.)

It is worth remarking that when T is the “identity” monad (T α = ˚ α, η = λx.˚ x, f^{∗} t = let˚ a ⇐ t in f a), the translation effectively replaces all occurrences of in the source term with˚ :

[[ M]]_{T} = (λx.˚ x)[[M]]_{T} =˚ [[M]]_{T} = [[˚ M]]_{T}

[[let x ⇐ M_{1} in M_{2}]]_{T} = (λx.[[M_{2}]]_{T})^{∗} [[M_{1}]]_{T} = let˚ a ⇐ [[M_{1}]]_{T} in (λx.[[M_{2}]]_{T})a

= let˚ x ⇐ [[M_{1}]]_{T} in [[M_{2}]]_{T} = [[let˚ x ⇐ M_{1} in M_{2}]]_{T}

so when we later exhibit a relation between the T -translation and a monadic translation for a continuation monad, we will get a relation between direct and continuation-passing style in the presence of arbitrary (sufficiently well-behaved) ambient effects by simply taking T to be the identity monad.

Defining T -specific operators The reflection and reification primitives allow us to define the meanings of effectful terms as abbreviations within the extended language, instead of through additional clauses in the translation equations.

Example 2.26 When T is the exception monad, we can define the usual ML-like exception primitives

\frac{Γ ⊢ M : χ}{Γ ⊢ raise M : α}  \frac{Γ ⊢ M_{1} : α Γ, x: χ ⊢ M_{2} : α}{Γ ⊢ try M_{1} handle x ⇒ M_{2} : α}

as follows:

raise M =^{def} let e ⇐ M in μ(˚ (inr e))
try M_{1} handle x ⇒ M_{2} =^{def} let˚ t ⇐ [M_{1}] in case(t, a. a, x.M_{2})

That is, to raise an exception, we explicitly construct its sum-representation as a value in the right inject, then “activate” it by reflecting it into the process of computation. Conversely, to handle a potential exception in a computation M_{1}, we first reify M_{1} and then inspect it, taking the appropriate action for either of the two possibilities (normal or exceptional value).

And in fact, expanding the definitions using the monadic translation gives the expected results:

[[raise M]]_{T} = [[let e ⇐ M in μ(˚ (inr e))]]_{T} = (λe.[[μ(˚ (inr e))]]_{T})^{∗} [[M]]_{T}

= let˚ t ⇐ [[M]]_{T} in case(t, e.[[˚ (inr e)]]_{T} , e.˚ (inr e))
= let˚ t ⇐ [[M]]_{T} in case(t, e.˚ (inr e), e.˚ (inr e))
[[try M_{1} handle x ⇒ M_{2}]]_{T} = ⋯ = [[let˚ t ⇐ [M_{1}] in case(t, a. a, x.M_{2})]]_{T}
= let˚ t ⇐ [[[M_{1}]]]_{T} in case(t, a.[[ a]]_{T} , x.M_{2})
= let˚ t ⇐ [[M_{1}]]_{T} in case(t, a.˚ (inl a), x.[[M_{2}]]_{T})

2.3.2 Induced equational theory

The translation induces a natural equational theory on terms of the extended language:

Definition 2.27 Given an equational theory E (including the cll axioms) for L and a monad-triple T in L, the equational theory E^{T} for L^{T} consists of E extended with the following rules (where we write ^{0} for˚ and ^{1} for ):

\frac{Γ ⊢ M_{1} : α_{1}  Γ, x: α_{1} ⊢ M_{2} : ^{j} α_{2}}{Γ ⊢ (let^{i} x ⇐ ^{i} M_{1} in M_{2}) = M_{2}\{M_{1}/x\} : ^{j} α_{2}} (i≤j)

\frac{Γ ⊢ M : ^{i} α}{Γ ⊢ (let^{i} x ⇐ M in ^{i} x) = M : ^{i} α}

\frac{Γ ⊢ M_{1} : ^{i} α_{1}  Γ, x_{1}: α_{1} ⊢ M_{2} : ^{j} α_{2}  Γ, x_{2}: α_{2} ⊢ M_{3} : ^{k} α_{3}}{Γ ⊢ (let^{j} x_{2} ⇐ (let^{i} x_{1} ⇐ M_{1} in M_{2}) in M_{3}) = (let^{i} x_{1} ⇐ M_{1} in let^{j} x_{2} ⇐ M_{2} in M_{3}) : ^{k} α_{3}} (i≤j≤k)

\frac{Γ ⊢ M : α}{Γ ⊢ μ([M]) = M : α}  \frac{Γ ⊢ M : T α}{Γ ⊢ [μ(M)] = M : T α}

\frac{Γ ⊢ M_{1} :˚ α_{1} Γ, x: α_{1} ⊢ M_{2} : α_{2}}{Γ ⊢ [let˚ x ⇐ M_{1} in M_{2}] = (let_{˚ T α_{2}} x ⇐ M_{1} in [M_{2}]) : T α_{2}}

\frac{Γ ⊢ M : α}{Γ ⊢ [ M] = η M : T α}  \frac{Γ ⊢ M_{1} : α_{1}  Γ, x: α_{1} ⊢ M_{2} : α_{2}}{Γ ⊢ [let x ⇐ M_{1} in M_{2}] = (λx.[M_{2}])^{∗}[M_{1}] : T α_{2}}

(The instance i = j = k = 0 in the first three rules is already part of E.)

Proposition 2.28 The equational theory is sound for the monadic translation, in the sense that if M = M′ is provable in E^{T}, then [[M]]_{T} = [[M′]]_{T} is provable in E extended with the monad laws for T (which may or may not already be provable in E). This again implies that L[[[[M]]_{T}]] = L[[[[M′]]_{T}]] in any model L of E in which T is a monad.

Proof. Simple equational reasoning, using Proposition 2.9 (the derivable equational properties for the generalized let). For example,

[[let˚ x ⇐˚ M_{1} in M_{2} : α_{2}]]_{T} = let_{˚ T [[α_{2}]]_{T}} x ⇐˚ [[M_{1}]]_{T} in [[M_{2}]]_{T} = [[M_{2}]]_{T} {[[M_{1}]]_{T} /x}

= [[M_{2}{M_{1}/x}]]_{T}

[[let x ⇐ M_{1} in M_{2}]]_{T} = (λx.[[M_{2}]]_{T})^{∗} (η [[M_{1}]]_{T}) = (λx.[[M_{2}]]_{T})[[M_{1}]]_{T}

= [[M_{2}]]_{T} {[[M_{1}]]_{T} /x} = [[M_{2}{M_{1}/x}]]_{T}

[[let˚ x_{2} ⇐ (let˚ x_{1} ⇐ M_{1} in M_{2}) in M_{3} : α_{3}]]_{T}

= let_{˚ T [[α_{3}]]_{T}} x_{2} ⇐ (let˚ x_{1} ⇐ [[M_{1}]]_{T} in [[M_{2}]]_{T}) in [[M_{3}]]_{T}
= let_{˚ T [[α_{3}]]_{T}} x_{1} ⇐ [[M_{1}]]_{T} in let_{˚ T [[α_{3}]]_{T}} x_{2} ⇐ [[M_{2}]]_{T} in [[M_{3}]]_{T}
= [[let˚ x_{1} ⇐ M_{1} in let˚ x_{2} ⇐ M_{2} in M_{3}]]_{T}

[[let x_{2} ⇐ (let˚ x_{1} ⇐ M_{1} in M_{2}) in M_{3}]]_{T}

= (λx_{2}.[[M_{3}]]_{T})^{∗} (let_{˚ T [[α_{2}]]_{T}} x_{1} ⇐ [[M_{1}]]_{T} in [[M_{2}]]_{T})
= let_{˚ T [[α_{3}]]_{T}} x_{1} ⇐ [[M_{1}]]_{T} in (λx_{2}.[[M_{3}]]_{T})^{∗} [[M_{2}]]_{T}
= [[let˚ x_{1} ⇐ M_{1} in let x_{2} ⇐ M_{2} in M_{3}]]_{T}

[[[ M]]]_{T} = [[ M]]_{T} = η [[M]]_{T} = [[η M]]_{T}

The first three rules of Definition 2.27 say that let-elimination and let-flattening are valid even for mixed levels, as long as the types match. That is, there is a single notion of computation-sequencing shared by all effects; the level-tags merely keep track of which kinds of effects can happen where.

The next two express that reflection and reification are exact inverses. For example, in the exception case, there is a one-to-one correspondence between “dynamic”, effectful computations of type α, that may raise exceptions, and “static”, exception-free values of type˚ (α + χ).

The remaining three equations show that [ ] acts as a “shallow” version of the monadic translation. According to the first one, terms with ambient effects only are unaffected by reification for the focus effect, and may hence move across a [ ]-barrier freely. For example, if the ambient effect is state and focus effect is exceptions, a computation that cannot raise an exception can be moved out of a try-handle. (The equation is necessarily satisfied when the base language is effect-free, because in that case let˚ is simply a substitution of M_{1} for x in M_{2}.)

The final two rules make it explicit how T -computations are realized in terms of the monad operations, enabling us to reason “locally” about propagation of effects entirely at the extended-language level.

Moreover, for all the rules it is the case that when the LHS is type-correct, then so is the RHS. Thus, we can always use the equations from left to right without worrying about type preservation.

As a simple consequence of the proposition, we get:

Corollary 2.29 Let | | : L_{0} → L_{0}^{T} be the translation replacing every occurrence of ˚ in types and terms by . Let L be a model of E_{0}. Then the interpretation L_{0}^{|T |} of L_{0}, given by L_{0}^{|T |}[[ ]] = L_{0}^{T} [[| |]] is also a model of E_{0} (and hence in particular itself a computational lambda-language).

Proof. The equations for numbers, products, sums, functions, and fixed points follow immediately from the translation. For computations, take i = j = k = 1 in the first three rules of E^{T} and use Proposition 2.28.

Note also that E^{T} implicitly asserts that (T, η, ^{∗}) form a monad in L^{T}, because it is easy to see that the equalities

η = λa.[ a]
f^{∗} = λt.[let a ⇐ μ(t) in μ(f a)]

are derivable, and the monad laws for η and f^{∗} then follow from the equations, e.g.,

f^{∗} (η a) = (λt.[let a ⇐ μ(t) in μ(f a)])[ a] = [let a ⇐ μ([ a]) in μ(f a)]

= [let a ⇐ a in μ(f a)] = [μ(f a)] = f a

and for rigidity of f^{∗}:

f^{∗} (let_{˚ T α_{1}} x ⇐ M in x) = [let a ⇐ μ(let_{˚ T α_{1}} x ⇐ M in x) in μ(f a)]

= [let a ⇐ μ(let_{˚ T α_{1}} x ⇐ M in [μ(x)]) in μ(f a)]
= [let a ⇐ μ([let˚ x ⇐ M in μ(x)]) in μ(f a)]
= [let a ⇐ (let˚ x ⇐ M in μ(x)) in μ(f a)]
= [let˚ x ⇐ M in let a ⇐ μ(x) in μ(f a)]
= let_{˚ T α_{2}} x ⇐ M in [let a ⇐ μ(x) in μ(f a)] = let_{˚ T α_{2}} x ⇐ M in f^{∗} x

Example 2.30 We can use the extended-language equations to verify the following ηlike rule for exceptions:

(try M handle x ⇒ raise x)

= let˚ t ⇐ [M] in case(t, a. a, x.let e ⇐ x in μ(˚ (inr e)))

= let˚ t ⇐ [M] in case(t, a.μ([ a]), x.μ(˚ (inr x)))

= let˚ t ⇐ [M] in case(t, a.μ(˚ (inl a)), x.μ(˚ (inr x))) = let˚ t ⇐ [M] in μ(˚ t)

= μ([let˚ t ⇐ [M] in μ(˚ t)]) = μ(let˚ t ⇐ [M] in [μ(˚ t)]) = μ(let˚ t ⇐ [M] in˚ t) = μ([M]) = M

This identity is crucial for pattern-matching exception handlers, where an exception is implicitly re-raised if it does not match any of the clauses in a handler; we want to ensure that such a handler has no effect on the result of the program.

2.4 Related work

There has already been much work on combining monadic effects, e.g., [Mog90, KW93, CM93, Ste94, LHJ95, Esp95], of varying degrees of generality and formality. None of these approaches, however, were particularly concerned about nonstandard implementations of the newly-specified effects; effectively, they all interpret programs using the modular specification directly, often at a significant cost in execution time.

It seems likely that the framework outlined here for the two-level case generalizes to multiple, explicitly-specified effects, each with a full reflection and reification operator. However, the primary constraint was not only to define a workable notion of layered effect, but also to ensure that it could be simulated in a strong sense by continuation-passing, and further by escapes and state, as detailed in the next two chapters. Consequently, any broader modularity aspects of the approach have not been properly developed.

Relating Effects

It is part of continuation folklore that continuations provide a very general notion of effects, in that many others (such as partiality, exceptions, or state) can be expressed as a continuation semantics with a suitable answer type. In the presence of higher-order functions, however, proving correctness of a continuation-based simulation is decidedly non-trivial [Rey74a, Sto81, MW85], even for a “purely functional” language with partiality as the only notion of computational effect.

In this chapter, we will consider the relationship between a direct and a continuation semantics for arbitrary monadic effects. In fact, the continuation semantics can itself be conveniently cast in the monadic mold, making the result a particular instance of simulating one monadic effect with another. However, the continuation-passing case is especially complicated, and a significant part of the proof consists of establishing the general framework and necessary lemmas for this case.

Very broadly, the general idea is as follows: assume we have two monads T and U over a base language, where U is in a suitable sense “more general” than T. We can then give two different translations from L^{T} to L: the original monadic translation for T and a variant translation using U-representations of T -effects. Moreover, we can exhibit a type-indexed family of relations ⊴_{α} with the property that the two translations of an L^{T} -term of type α are related by ⊴_{α}, and such that the relation at base types is the identity. Thus, the two translations induce the same evaluation semantics.

3.1 Simulating monadic effects

In this section, we present a general principle for relating effects, introduce the variant translation, and argue informally for its correctness. The actual simulation proof, however, will be postponed until Section 3.3.

3.1.1 Monad morphisms

A natural way of relating two monads consists of exhibiting a function mapping one to the other:

Definition 3.1 Let T = (T, η, ^{∗}) and U = (U, ε, ^{+}) be monads over a cll (L, L). A (definable) monad morphism from T to U is a type-indexed family of L-terms,

⊢_{{a}} i_{a} : T a → Ua ,

respecting the monad structure, i.e., such that the following holds in L for all closed L-types:

0. ⊢ i_{α} : T α →^{r} Uα.

1. ⊢ i_{α} ∘ η_{α} = ε_{α} : α → Uα.

2. f : α_{1} → T α_{2} ⊢ i_{α_{2}} ∘ f^{∗} = (i_{α_{2}} ∘ f)^{+} ∘ i_{α_{1}} : T α_{1} → Uα_{2}.

We can think of i as converting T -representations of effects to U-representations. Condition (0) is a technical constraint, ensuring essentially that the conversion of focus effects respects any underlying ambient effects (for example, a nonterminating T -computation must be represented by a nonterminating U-computation). More explicitly, (1) says that a trivial T -computation is mapped into a trivial U-computation. Condition (2) may look somewhat arbitrary at first, but note that it can be written in the form of a conditional equality emphasizing the parallel to (1):

i_{α_{2}} ∘ f = g ⇒ i_{α_{2}} ∘ f^{∗} = g^{+} ∘ i_{α_{1}}

(where f : α_{1} → T α_{2} and g : α_{1} → Uα_{2}). It expresses the requirement that if g is the Ucounterpart of an α_{1}-parameterized T -computation f, then T -extending f and converting its output is equivalent to applying the U-extended g to the conversion of the input.

Example 3.2 For any monad U, the function family

h_{α} :˚ α → Uα = λt^{˚ α}.let_{˚ Uα} a ⇐ t in εa

is a monad morphism from the identity monad I (Example 2.16) to U:

h (let˚ x ⇐ m in x) = let_{˚ Uα} a ⇐ (let˚ x ⇐ m in x) in εa

= let_{˚ Uα} x ⇐ m in let_{˚ Uα} a ⇐ x in εa = let_{˚ Uα} x ⇐ m in hx

h (η a) = h(˚ a) = let_{˚ Uα} a ⇐˚ a in εa = εa

h (f^{∗} t) = h (let˚ a_{1} ⇐ t in f a_{1}) = let˚ a_{2} ⇐ (let˚ a_{1} ⇐ t in f a_{1}) in εa_{2}

= let˚ a_{1} ⇐ t in let˚ a_{2} ⇐ f a_{1} in εa_{2} = let˚ a_{1} ⇐ t in h(f a_{1})
= let˚ a_{1} ⇐ t in (h ∘ f)^{+} (εa_{1}) = (h ∘ f)^{+} (let˚ a_{1} ⇐ t in εa_{1}) = (h ∘ f)^{+} (h t)

In fact, it is the only such morphism. This is immediate when the base language is effect-free (condition (1) with η = id), but it also holds in general: Suppose h′ :˚ α → Uα is another monad morphism from I to U. Then

h′ t = h′ (let˚ x ⇐ t in˚ x) = let_{˚ Uα} x ⇐ t in h′ (˚ x) = let_{˚ Uα} x ⇐ t in h′ (η x)
= let_{˚ Uα} x ⇐ t in εx = h t

This captures the intuitive notion that any effect can simulate the absence of effects, which we would probably consider a minimal requirement for any notion of effect simulation.

A more interesting example is provided by the following:

Example 3.3 For simplicity, assume that our base language includes a term constructor + : ι × ι → ι, satisfying equations \underline{0} + M = M + \underline{0} = M and M_{1} + (M_{2} + M_{3}) = (M_{1} + M_{2}) + M_{3} in the model, i.e., such that (ι, \underline{0}, +) forms a monoid. (We could of course have defined an addition operator in the existing language using recursion, but that would necessarily give it the type ι × ι →˚ ι, cluttering up the terms with explicit sequencing of the additions. Still, it is easy to check that everything does work out correctly even for a defined +.)

Then the following determines a monad, usually called the complexity monad:

T α = ˚ (α × ι)
η = λa.˚ ⟨a, \underline{0}⟩
f^{∗} = λt.let˚ ⟨a_{1}, n_{1}⟩ ⇐ t in let˚ ⟨a_{2}, n_{2}⟩ ⇐ f a_{1} in˚ ⟨a_{2}, n_{1} + n_{2}⟩

Here, a computation of type α is represented by a base-computation yielding a value of type α together with some notion of the cost involved in computing it, such as the number of floating-point operations performed (perhaps using an encoding of floatingpoint numbers in terms of ι), or the amount of I/O (assuming our ambient effects include some notion of communication with the outside world). A trivial computation, η a, resulting from viewing an already given value as a computation, has zero cost; the cost of evaluating f^{∗} t is the sum of the cost n_{1} of computing the value a_{1} of t and the cost n_{2} of evaluating f at a_{1}.

The complexity monad works by summing the complexities of each subcomputation. But if most subcomputations do not invoke the operation being counted, this is potentially wasteful, since we will be adding zeros most of the time. Even more important, complexity is a fairly “ad hoc” monad, so that we will most likely have to perform an actual translation to get a language with the corresponding monadic effects.

There is an alternative way to keep track of complexity, however: maintain a running total, which is updated only by the cost-incurring operations themselves, and passively transmitted everywhere else. We achieve this using the ι-state monad (Example 2.18):

Uα = ι →˚ (α × ι),  ε = λa.λs.˚ ⟨a, s⟩,  f^{+} = λu.λs.let˚ ⟨a, s′⟩ ⇐ us in f as′

We can then represent a computation of a with complexity n as a function adding n to the current total, in addition to returning a. And in fact,

i_{α} = λt^{T α}.λs^{ι}.let˚ ⟨a, n⟩ ⇐ t in˚ ⟨a, s + n⟩

is a monad morphism from T to U.

We now formally define a very important class of monads:

Definition 3.4 Let o be any computation-type. Then the continuation monad with answer type o, K_{o} = (K_{o}, ε,^{+} ) is given by:

K_{o}α = (α → o) → o,  ε = λa.λk.k a,  f^{+} = λu.λk.u(λa.f ak)

It is easy to check that this actually determines a monad. For rigidity of f^{+}, we have:

f^{+} (let_{˚ Uα} x ⇐ m in x) = λk.(let_{˚ Uα} x ⇐ m in x)(λa.f ak)

= λk.let_{˚ o} x ⇐ m in x(λa.f ak) = λk.let_{˚ o} x ⇐ m in f^{+} xk
= let_{˚ Uα} x ⇐ m in f^{+} x

Satisfaction of the other three equations is completely straightforward.

The importance of continuation monads stems from the following property:

Lemma 3.5 Let T = (T, η, ^{∗}) be a monad in a computational λ-language, and let γ be an arbitrary type (not necessarily computational). Take U as K_{T γ}, the continuation monad with answer type T γ. Then the family of functions

i_{α} : T α → Uα = λt^{T α}.λk^{α→T γ}.k^{∗} t

forms a monad morphism from T to U.

Proof. Straightforward verification:

i(let_{˚ T α} t ⇐ m in t) = λk.k^{∗} (let_{˚ T α} t ⇐ m in t) =^{†} λk.let_{˚ T γ} t ⇐ m in k^{∗} t
= λk.let_{˚ T γ} t ⇐ m in itk = let_{˚ Uα} t ⇐ m in it
i(η a) = λk.k^{∗} (η a) =^{†} λk.k a = εa
i(f^{∗} t) = λk.k^{∗} (f^{∗} t) =^{†} λk.(λa.k^{∗} (f a))^{∗} t = λk.it(λa.k^{∗} (f a)) = λk.it(λa.i(f a)k)
= λk.it(λa.[λa.i(f a)]ak) = (λa.i(f a))^{+} (it) = (i ∘ f)^{+} (it)

(where the equations marked with ^{†} signify application of the monad laws of T from Definition 2.15).

When T is the identity monad, this (necessarily) degenerates to an instance of Example 3.2. More interestingly,

Example 3.6 For exceptions, T α = ˚ (α + χ), the monad morphism from T to K_{T γ} specializes to:

i_{α} = λt^{˚ (α+χ)}.λk^{α→˚ (γ+χ)}.let˚ v ⇐ t in case(v, a.k a, e.˚ (inr e))

Recall that the T -representation of a successful computation of type α is an included value a in the left inject of α + χ. The corresponding continuation-passing computation should immediately apply its continuation to a. And in fact, we have

i(˚ (inl a)) = λk.let˚ v ⇐˚ (inl a) in case(v, a.k a, e.˚ (inr e))
= λk. case(inl a, a.k a, e.˚ (inr e)) = λk.k a

Similarly, a computation that terminates with a raised exception e is represented by a value in the right inject; the continuation-passing analog simply discards the current continuation and returns the exceptional value as the result:

i(˚ (inr e)) = λk.let˚ v ⇐˚ (inr e) in case(v, a.k a, e.˚ (inr e))
= λk. case(inr e, a.k a, e.˚ (inr e)) = λk.˚ (inr e)

And finally, a nonterminating computation is represented by a non-terminating computation (for any continuation):

i⊥ = λk.let˚ v ⇐ ⊥ in case(v, a.k a, e.˚ (inr e)) = λk.⊥

This monad morphism from an arbitrary monad T to a continuation monad K_{T γ} will form the core of our simulation result. However, the fact that the continuationbased representation is in a sense parametric in the choice of γ cannot be captured equationally in our setting. (It might be possible in a language with F_{2}-polymorphism [Gir72, Rey74b].) For the formal proof in Section 3.3, we will therefore need a stronger, relational characterization of i to accurately express this property.

3.1.2 The variant translation

In this section, we show how to actually exploit the existence of a monad morphism (with some further properties) to simulate one kind of effects with another. Specifically, we will show how to interpret our T -enriched effect language in terms of U-effects. The exposition is slightly simplified in that we consider only a single semantics for the base language — the actual proof in Section 3.3 distinguishes between a specification and the implementation semantics, mostly to make get a result of sufficient strength to support Chapter 4. However, the formal definitions we give are general enough for both cases.

As motivated in the previous section, monad morphisms give us a simple way of relating two notions of effects. Nevertheless, a monad morphism by itself does not guarantee that U-effects simulate T -effects in any useful sense. For example, for any T there is a (unique) monad morphism from T to the degenerate monad, (Uα = 1, ε = λa.⟨⟩, f^{+} = id_{1}). To get a proper simulation, we also need a way to recover the T -representation of an effect from its U-representation:

Definition 3.7 Let i be a monad morphism from T to U. A monad retraction at type α is a left inverse of i_{α}, i.e., a term j_{α} : Uα → T α such that j_{α} ∘ i_{α} = id_{T α}. We say that such a retraction is schematic if all the j_{α} are themselves members of a term family, i.e., if j_{α} = j_{a}{α/a}.

We usually expect at least j_{ι} to exist; this gives us a way of extracting meanings of complete programs. In many cases, however, it is easy to find a suitable inverse at all types:

Example 3.8 For the complexity-state simulation from Example 3.3, where in particular the monad morphism was given by

i_{α} = λt^{˚ (α×ι)}.λs^{ι}.let˚ ⟨a, n⟩ ⇐ t in˚ ⟨a, s + n⟩ ,

taking

j_{α} = λu^{ι→˚ (α×ι)}.u\underline{0}

determines a monad retraction at all types. That is, to actually extract the complexity of a computation from its state-passing representation, we simply initialize the state to zero, perform the computation, and read off the complexity as the final state.

It is easy to see that this j is a schematic left inverse of i. It is not, however, a monad morphism from U to T: it does not in any meaningful sense simulate arbitrary statepassing computations using complexity-effects.

Given terms typed like the monad morphisms and retractions, we can give a different translation of our effect-enriched language L^{T}, back into L, using a U-based representation of T -effects instead of the T -representation from the definitional translation. To define the translation itself, of course, we do not need to assume any equational properties of the terms involved:

Definition 3.9 In L, let T and U be monad-triples, and let i be a family of terms such that for any L^{T} -type α, i_{[[α]]_{U}} : T [[α]]_{U} → U[[α]]_{U}. Further, let ℵ be a set (finite or infinite) of L^{T} -types and for every α in ℵ, a term j_{[[α]]_{U}} : U[[α]]_{U} → T [[α]]_{U}.

Now, let L^{T [ℵ]} be L^{T} but with reification restricted to ℵ-types, i.e., with [ ] : α → T α only for α in ℵ. We then define the variant or implementation translation from L^{T [ℵ]} to L as follows. For types, we take

[[ α]]′_{T} = U[[α]]′_{T}

(so for the type translation we have [[α]]′_{T} = [[α]]_{U}), and for terms,

[[ M]]′_{T} = ε[[M]]′_{T}
[[let x ⇐ M_{1} in M_{2}]]′_{T} = (λx.[[M_{2}]]′_{T})^{+} [[M_{1}]]′_{T}
[[let˚ x ⇐ M_{1} in M_{2} : α]]′_{T} = let_{˚ U[[α]]′_{T}} x ⇐ [[M_{1}]]′_{T} in [[M_{2}]]′_{T}
[[μ^{T}(M)]]′_{T} = i_{[[α]]′_{T}} [[M]]′_{T}
[[[M]^{T}]]′_{T} = j_{[[α]]′_{T}} [[M]]′_{T}

(We write μ^{T}( ) and [ ]^{T} to emphasize that these are reflection and reification operators for T, not U.) Like the definitional translation (Definition 2.23), [[ ]]′_{T} is easily seen to preserve types, i.e., if Γ ⊢_{Δ} M : α in L^{T} then [[Γ]]′_{T} ⊢_{Δ} [[M]]′_{T} : [[α]]′_{T} in L.

Of course, when U = T, with i_{α} = j_{α} = id_{T α} (trivially a monad morphism with a schematic retraction), we get exactly the original [[ ]]_{T} -translation as a special case. In general, however, we now have [[T α]]′_{T} = T [[α]]′_{T} ≠ U[[α]]′_{T} = [[ α]]′_{T}: the transparent and opaque representations of a computation with T -effects are different. This is why for reflection we need to internalize a T -representation of an effect into a U-representation that fits with the rest of the U-passing translation. Conversely, for reification, we externalize the U-representation into the definitional T -representation of the effect.

Although the definitional and the variant translation of a type are in general different, they do agree on base types, so in particular the results of transforming complete programs (closed terms of type˚ ι) are directly comparable. And in fact, will show in Proposition 3.29 that the two translations of a closed L_{0}^{T} -term of type˚ ι are indeed equal in our partiality semantics (and appropriately related for other notions of ambient effects).

In the monad-continuation case of a monad morphism (Lemma 3.5), it is not obvious how to define j_{α} in general. We will see in Section 3.3.4 how to achieve this. For the purposes of this section, however, let us simply restrict ourselves to performing T - reification at a single L-type γ (as opposed to at arbitrary L^{T} -types, as the standard T -translation allows us to). That is, we take ℵ = {γ}.

If we then let U be the continuation monad with answer type T γ, we can directly take j_{γ} : Uγ → T γ = λu.uη_{γ}, which gives us

j_{γ} (i_{γ} t) = (λu.uη_{γ})(λk.k^{∗} t) = (λk.k^{∗} t)η_{γ} = η_{γ}^{∗} t = t

i.e., that j_{γ} is a monad retraction at γ.

Example 3.10 Let T α =˚ (α +χ) be the exception monad, with the continuation-based representation Uα = K_{T γ}α = (α →˚ (γ + χ)) →˚ (γ + χ) from Example 3.6. In this case, the translation equations specialize to:

[[ M]]′_{T} = λk.k [[M]]′_{T}
[[let x ⇐ M_{1} in M_{2}]]′_{T} = λk.[[M_{1}]]′_{T} (λx.[[M_{2}]]′_{T} k)
[[let˚ x ⇐ M_{1} in M_{2} : α]]′_{T} = λk.let_{˚˚ (γ+χ)} x ⇐ [[M_{1}]]′_{T} in [[M_{2}]]′_{T} k
[[μ^{T}(M)]]′_{T} = λk.let˚ t ⇐ [[M]]′_{T} in case(t, a.k a, e.˚ (inr e))
[[[M]^{T}]]′_{T} = [[M]]′_{T} (λa.˚ (inl a))

(where, for the third equation, we have used Definition 2.4 to expand out the generalized let in Definition 3.9). The continuation-passing analogs of raise and handle, as given by the expansions in Example 2.26 then work out to:

[[raise M]]′_{T} = [[let e ⇐ M in μ^{T}(˚ (inr e))]]′_{T} = λk.[[M]]′_{T} (λe.[[μ^{T}(˚ (inr e))]]′_{T} k) = λk.[[M]]′_{T} (λe.let˚ t ⇐ [[˚ (inr e)]]′_{T} in case(t, a.k a, e.˚ (inr e))) = λk.[[M]]′_{T} (λe.let˚ t ⇐˚ (inr e) in case(t, a.k a, e.˚ (inr e))) = λk.[[M]]′_{T} (λe. case(inr e, a.k a, e.˚ (inr e))) = λk.[[M]]′_{T} (λe.˚ (inr e))

[[try M_{1} handle x ⇒ M_{2}]]′_{T} = [[let˚ t ⇐ [M_{1}]^{T} in case(t, a. a, x.M_{2})]]′_{T} = λk.let˚ t ⇐ [[[M_{1}]^{T}]]′_{T} in case(t, a.[[ a]]′_{T} , x.[[M_{2}]]′_{T})k

= λk.let˚ t ⇐ [[M_{1}]]′_{T} (λa.˚ (inl a)) in case(t, a.(λk.k a)k, x.[[M_{2}]]′_{T} k)

= λk.let˚ t ⇐ [[M_{1}]]′_{T} (λa.˚ (inl a)) in case(t, a.k a, x.[[M_{2}]]′_{T} k)

(where handle can only be used with expressions of type γ). This again should match the operational intuition that to raise an exception determined by M, we simply return name directly as an answer (tagged as a right inject, so that an enclosing handle can tell the difference). Conversely, to handle a potential exception in M_{1}, we invoke it with the left injection as the continuation. If M_{1} returns normally i.e., by returning inl a, we pass a to the continuation of the handle. On the other hand, if M_{1} raises an exception e, i.e., returns inr e, we instead evaluate M_{2} with x bound to e, again in the control context of the handle.

Comparison Suppose the restrictions on reification were not an issue, for example if we were content to only allow uses of handle at a single base type (not an entirely unreasonable restriction; we still have raise at all types). Then given the fairly simple correspondence between “direct” and “continuation-passing” definitions of exceptions, one might reasonably ask why we formalize the T -translation at all – why not simply take the continuation-based [[ ]]′_{T} as the “official” definition of exceptions? Then we could view exceptions as simply syntactic sugar for the corresponding continuation effects.

The problem is that the CPS translation does not satisfy the desirable equational reasoning principles that pure exception-passing does. For example, consider again the reasoning principle

(try M handle x ⇒ raise x) = M

We saw in Example 2.30 that the T -translation verifies this law; indeed, it is provable in E_{0}^{T}. But with the continuation-based semantics we get:

[[try M handle x ⇒ raise x]]′_{T} = [[let˚ t ⇐ [M] in case(t, a. a, x.μ(˚ (inr x)))]]′_{T}

= λk.let˚ t ⇐ [[[M]]]′_{T} in case(t, a.[[ a]]′_{T} k, x.[[μ(˚ (inr x))]]′_{T} k)
= λk.let˚ t ⇐ j[[M]]′_{T} in case(t, a.k a, x.i(˚ (inr x))k)
= λk.let˚ t ⇐ [[M]]′_{T} η in case(t, a.k a, x.k^{∗} (˚ (inr x)))
= λk.let˚ t ⇐ [[M]]′_{T} (λa.˚ (inl a)) in case(t, a.k a, x.˚ (inr x)) =^{?} [[M]]′_{T}

It is easy to check that this does in fact hold when [[M]]′_{T} is of the form λk.k a for some a, corresponding to an effect-free computation of a. Similarly, the equation is satisfied when [[M]]′_{T} = λk.˚ (inr e) for some e, corresponding to a computation raising the exception e. Even when [[M]]′_{T} = λk.⊥, representing a non-terminating computation, the terms have equal denotations. But there is no simple guarantee that [[M]]′_{T} is in fact in one of those forms, especially when M may call an “unknown” function.

For example, consider the case γ = ι. Then one element of the type [[ α]]′_{T} = ([[α]]′_{T} → ˚ (ι + χ)) →˚ (ι + χ), is λk.˚ (inl \underline{42}), which we could call an exotic T -computation: it represents neither a normal value, nor a raised exception, nor divergence. And in fact, if [[M_{0}]]′_{T} = λk.˚ (inl \underline{42}), our desired reasoning principle fails because we get

[[try M_{0} handle x ⇒ raise x]]′_{T} = λk.k \underline{42} ≠ λk.˚ (inl \underline{42}) = [[M_{0}]]′_{T}

The presence of such computations means that we cannot derive the identity directly in the U-model – we need a much more elaborate argument, involving at least an induction over all syntactic terms in the language, and further complicated by the presence of higher-order functions.

An analogous situation holds for the complexity-state simulation from Example 3.3. It is easy to see that if the ambient effects are commutative, then so are the -effects defined by the complexity monad. General state passing, on the other hand, is not commutative, so again we lose a useful equational property by specifying complexity-effects directly in terms of state-passing.

That does not mean, however, that using [[ ]]′_{T} inherently presents a problem for formal reasoning. Recall that we will show independently that the [[ ]]_{T} -translation and the [[ ]]′_{T}-translation do agree on complete L_{0}^{T} -programs. Since the equations induced by the T -translation are (by definition) valid for observational equivalence in L_{0}^{T}, and the evaluation semantics induced by the two translations is the same, we can thus reason about effects in terms of their (relatively) declarative T -specification, rather than their derived U-implementation.

Remark 3.11 In the particular case of exceptions, we could actually construct an adhoc continuation semantics where the translation of a term M : α takes both a normal continuation (of type [[α]]→o) and an exceptional one (of type χ→o), invoking whichever is appropriate. Such a translation does verify the handle/re-raise equation above, and it does not have a problem with the choice of answer type.

However, such a scheme requires all translation equations to be modified to pass the extra continuation along, so we cannot use a standard cps transform for the bulk of the language. And even more importantly, this two-continuations trick does not generalize, because it relies on the isomorphism ((α + χ) → o) → o ≅ ((α → o) × (χ → o)) → o, which does not have a counterpart for other monadic effects.

3.2 The proof setting

This section establishes the general framework for the simulation proof in the next section. Much of the material is relatively standard, and has consequently been relegated to an appendix.

3.2.1 The implementation language

The base signature L_{0}, and the derived L_{0}^{T} need to be tightly constrained because we will rely on induction over L_{0}^{T} -types and -terms in the proof. The target language for the variant translation, on the other hand, need not be restricted to simple types. And in fact, to obtain the simulation result for continuations in full generality, we will need more of the structure of our predomain model to be denotable in the implementation language. Accordingly, we now define the required extensions for expressing (1) a weak notion of infinitary sums and (2) recursively-defined types.

Embedding-types

To simulate T -reification using continuations, we will need to embed several different types into a single type of answers. A suitable construct for expressing this is given by the following:

Definition 3.12 The signature L_{0}^{Σ} extends L_{0} with a new type constructor Σ:

\frac{∀i ∈ I. ⊢_{Δ} ℵ(i) type}{⊢_{Δ} Σ_{i}ℵ(i) type}

where (ℵ(i))_{i∈I} is any countable family of L_{0}^{Σ}-types (possibly with repetitions); we usually abbreviate Σ_{i}ℵ(i) as Σℵ. The associated term constructors are:

\frac{Γ ⊢ M : ℵ(i)}{Γ ⊢ in_{i} M : Σℵ} (i∈I)  and  \frac{Γ ⊢ M : Σℵ}{Γ ⊢ outd_{i} M : ℵ(i) + 1} (i∈I)

for injecting into and projecting from the embedding-type. Correspondingly, E_{0}^{Σ} extends E_{0} with the equations

\frac{Γ ⊢ M : ℵ(i)}{Γ ⊢ outd_{i} (in_{i} M) = inl M : ℵ(i) + 1} (i∈I)

and

\frac{Γ ⊢ M : ℵ(i)}{Γ ⊢ outd_{i′} (in_{i} M) = inr ⟨⟩ : ℵ(i′) + 1} (i,i′∈I; i′≠i) .

From outd_{i}, we can define a derived term constructor,

\frac{Γ ⊢ M : Σℵ}{Γ ⊢ out_{i} M :˚ ℵ(i)} (i∈I)

by out_{i} M =^{def} case(outd_{i} M, a.˚ a, u.⊥_{˚ ℵ(i)}). Then we easily get the following derived inference rule in E_{0}^{Σ}:

\frac{Γ ⊢ M : ℵ(i)}{Γ ⊢ out_{i} (in_{i} M) =˚ M :˚ ℵ(i)} (i∈I).

In this chapter, in_{i} and out_{i} with the above equation will suffice (in particular, we will not use that out_{i′} (in_{i} M) = ⊥ when i ≠ i′), but in Chapter 4, an explicit outd_{i}, not tied to any particular notion of ambient effects, will be more convenient.

It is important that even for infinite index sets, embedding types do not introduce any circularity: each summand ℵ(i) must already be a well-defined type before we can form Σℵ.

When the index set is finite, {i_{0}, …, i_{n−1}}, we can simply take

Σℵ = ℵ(i_{0}) + (⋯ + (ℵ(i_{n−1}) + 1)···)

(the terminating 1 merely ensures a uniform encoding for all summands) with the corresponding operations:

in_{i_{0}} M = inl M
in_{i_{k+1}} M = inr (in_{i_{k}} M)

outd_{i_{0}} M = case(M, a_{0}.inl a, s.inr ⟨⟩)
outd_{i_{k+1}} M = case(M, a.inr ⟨⟩, s.outd_{i_{k}} s)

which are easily seen to satisfy the required equations.

In the general case, we obtain a model by a straightforward extension of the predomain semantics to I-indexed coproducts:

L[[Σ_{i}ℵ(i)]]^{θ} = {(i, a) | i ∈ I, a ∈ L[[ℵ(i)]]^{θ}}
L[[in_{i} M]]^{θ}(ρ) = (i, L[[M]]^{θ}(ρ))
L[[outd_{i} M]]^{θ}(ρ) = \begin{cases}(1, a) & when L[[M]]^{θ}(ρ) = (i, a) \\ (2, ∗) & when L[[M]]^{θ}(ρ) = (i′, a′), i′ ≠ i \end{cases}

It is immediate to check that this interpretation validates the equations.

Recursive type specifications

Independently of the embedding-types, to express the continuation-based variant translation of reification at types containing , we will need a recursively-defined answer type. Accordingly, we take:

Definition 3.13 For a signature L, L^{μ} extends L with a new type constructor μa. α with well-formedness rule

\frac{⊢_{\{a\}} α type}{⊢_{∅} μa. α type} ,

and new term constructors roll_{a.α} and unroll_{a.α} with typings

\frac{Γ ⊢ M : α\{(μa. α)/a\}}{Γ ⊢ roll_{a.α} M : μa. α} and \frac{Γ ⊢ M : μa. α}{Γ ⊢ unroll_{a.α} M : α\{(μa. α)/a\}} .

Likewise, E^{μ} extends E with the isomorphism equations

\frac{Γ ⊢ M : α\{(μa. α)/a\}}{Γ ⊢ unroll_{a.α} (roll_{a.α} M) = M : α\{(μa. α)/a\}} and \frac{Γ ⊢ M : μa. α}{Γ ⊢ roll_{a.α} (unroll_{a.α} M) = M : μa. α} .

(For simplicity we do not allow parameterized recursive types, although it would probably do no harm to include them.)

Unlike the case for domains, not every predomain equation expressed in terms of the standard cpo constructors has a solution. (For example, consider the equation V ≅ V →0, where 0 is the empty set organized as a cpo; both assuming V empty and non-empty lead to a contradiction.) But equations arising from interpretations of L_{0}^{Σ}-types (which notably require codomains of arrow types to be computational, thus ruling out the above counterexample) do have solutions, essentially because we can extend the interpretation of a parameterized type to a functor in a suitable category. We will need the following result:

Theorem 3.14 Let ⊢_{{a}} α be a parameterized type of L_{0}^{Σ}. Then there exists a cpo A with an isomorphism i : L[[α]]^{a↦A} →^{∼} A.

Proof. See Corollary A.8 in the appendix (ignoring for now the additional minimalinvariant property of i).

Then with the interpretation of μa. α as the A in the theorem, roll_{a.α} as i, and unroll_{a.α} as i^{−1}, our predomain semantics (for any T ) becomes a model of E_{0}^{Σ,μ}.

3.2.2 Admissible relations

Much as an equational theory allows us to reason about equivalence of terms axiomatically, rather than about equality of their denotations in a specific interpretation, we can reason about more general relations between terms at the syntactic level. That is, we first establish a set of generic relational reasoning principles, validated by a wide range of interpretations. If we then confine our reasoning about programs to those principles, the results will necessarily hold in each particular relational interpretation.

Our denotational semantics associates to every open term a (continuous) function from the meanings assigned to the free variables to the meaning of the resulting term. When the semantics L is fixed, we use the term constructors of L directly to denote this semantic function. For example, for any element a of Val_{L}(α) (not necessarily denotable by a closed L-term), we write inl a for the element (1, a) of Val_{L}(α + α′).

Further, when σ = (a_{1}/x_{1}, …, a_{n}/x_{n}) assigns to every variable x_{i}: α_{i} in Γ a value a_{i} ∈ Val(α_{i}) = L[[α_{i}]], we write M{σ} for the value L[[M]](•[x_{1} ↦ a_{1}, …, x_{1} ↦ α_{1}]). (To improve readability, we will usually write M{σ} as M^{σ}; the two notations are equivalent.)

Unlike the equational case, we can talk about relations between terms of two different languages. That is, given (L, L) and (L′, L′) we say that R is a relation between types α of L and α′ of L′ if it is a relation between the sets Val_{L}(α) and Val_{L′}(α′). When the languages are fixed, we write simply Rel(α, α′) for the set of all such relations. (Actually, we will only be interested in the set of all admissible relations; see Definition 3.16 below. But sometimes it is useful to classify a relation wrt. types before we have established that it is admissible.)

The motivation for considering different languages is that when implementing a monadic effect, we may need different resources than when specifying it. In particular, L′ may contain constructs not in L, with L′ providing an interpretation for those. Moreover, L and L′ may arise from different choices of the base-effect monad T .

This means that we can specify a monad T over (L, L) (say, with only the constructs of L_{0} and with partiality as the only ambient effect), and show how to implement L^{T} using a T′-translation into (L′, L′) (say, L_{0} extended with recursive types, and a continuation semantics for ambient effects) – even if T does not satisfy the monad laws in L′.

For a relation R ∈ Rel(α, α′), we often write ∀a R a′. P (a, a′) as shorthand for ∀a ∈ Val_{L}(α), a′ ∈ Val_{L′}(α′). a R a′ ⇒ P (a, a′). Similarly, ∃a R a′. P (a, a′) abbreviates ∃a ∈ Val_{L}(α), a′ ∈ Val_{L′}(α′). a R a′ ∧ P (a, a′).

We can now isolate the subset of relations we will be working with. First, we define a relational analog of (pointed) cpos:

Definition 3.15 A binary relation R between (the sets underlying) cpos A and A′ is called chain-complete if for any pair of chains a_{1} ⊑ a_{2} ⊑ ⋯ in A and a′_{1} ⊑ a′_{2} ⊑ ⋯ in A′ with a_{i} R a′_{i} for each i, it also holds that (⨆_{i} a_{i}) R (⨆_{i} a′_{i}). A relation between pointed cpos B and B′ is called pointed if ⊥_{B} R ⊥_{B′}.

We can then define a suitable notion of relations for our predomain semantics:

Definition 3.16 Let there be given languages (L, L) and (L′, L′), where L and L′ are extensions of L_{0}, and L and L′ are the corresponding extensions of the predomain semantics from Section 2.1.3 (with possibly different ambient-effect monads).

We say that a relation between types α of L and α′ of L′ is admissible if it is interpreted as a chain-complete relation between Val_{L}(α) and Val_{L′}(α′); we write ARel(α, α′) for the set of such relations. Similarly, a computation-admissible relation between types β and β′ is one whose interpretation is chain-complete and pointed; we use CARel(β, β′) for the corresponding set.

We will refer to such a pair of languages with type-indexed sets of (computation-) admissible relations as a relational correspondence. (This is for conciseness only; the correspondence is already fully determined by the languages themselves and the above definitions of ARel(α, α′) and CARel(β, β′).)

In the following, we enumerate some properties of (computation-)admissible relations, especially that certain stylized methods of constructing them are available. The proofs are all fairly simple and can be found in the Appendix.

Given these properties, we can reason about related terms entirely within the base language, without referring to the semantic equations, chains, continuity, etc. explicitly. That is, although we will not consider other notions of (computation-)admissibility than (pointed) chain-completeness, the remainder of this section establishes all we actually need to require of admissible relations for establishing the results in Section 3.3.

Lemma 3.17 Admissible relations are closed under inverse image by term contexts and under arbitrary intersection. That is,

1. When R ∈ ARel(α, α′), (x_{1}: α_{1}, …, x_{n}: α_{n}) ⊢ M : α and (x′_{1}: α′_{1}, …, x′_{n′}: α′_{n′}) ⊢ M′ : α′ are terms of L and L′ respectively, and for all i ≥ 2, σx_{i} ∈ Val_{L}(α_{i}) and σ′x′_{i} ∈ Val_{L′}(α′_{i}), the relation R_{1} ∈ Rel(α_{1}, α′_{1}) given by

a_{1} R_{1} a′_{1} ⟺ M^{(a_{1}/x_{1},σ)} R M^{′(a′_{1}/x′_{1},σ′)}

is admissible.

Moreover, when α_{1} and α′_{1} are computation-types, R is computation-admissible, and the functions λx_{1}.M^{σ} and λx′_{1}.M^{′σ′} are rigid, then R_{1} is also computationadmissible.

2. When (R_{j})_{j∈J} is an arbitrary (not necessarily finite or even countable) family of admissible relations between α and α′, the relation ⋂_{j∈J} R_{j} is admissible, where

a (⋂_{j∈J} R_{j}) a′ ⟺ ∀j ∈ J. a R_{j} a′

Moreover, if each R_{j} is computation-admissible then so is ⋂_{j∈J} R_{j}.

Proof. See Lemma A.9.

We also have a simple way of combining existing relations on individual types into relations on constructed ones:

Lemma 3.18 The standard relational actions of the type constructors, defined by

i ι^{r} i′ ⟺ ∃n ∈ N. i = \underline{n} ∧ i′ = \underline{n}
u 1^{r} u′ ⟺ true
p (R_{1} ×^{r} R_{2}) p′ ⟺ fst p R_{1} fst p′ ∧ snd p R_{2} snd p′
s (R_{1} +^{r} R_{2}) s′ ⟺ (∃a_{1} R_{1} a′_{1}. s = inl a_{1} ∧ s′ = inl a′_{1})
∨ (∃a_{2} R_{2} a′_{2}. s = inr a_{2} ∧ s′ = inr a′_{2})
f (R_{1} →^{r} R_{2}) f′ ⟺ ∀a R_{1} a′. f a R_{2} f′ a′

are admissible. Specifically, when all the R’s are admissible, so are ι^{r}, 1^{r}, R_{1} ×^{r} R_{2}, R_{1} +^{r} R_{2}, and R_{1} →^{r} R_{2}. Moreover, when the S’s are computation-admissible, so are 1^{r}, S_{1} ×^{r} S_{2}, and R →^{r} S.

(We often omit the ^{r} when it is clear form the context that the action is on relations.) Note that the relational action of˚ is not in general explicitly definable within the language; we characterize it in Definition 3.20.

Proof. We can actually show admissibility of 1^{r}, R_{1} ×^{r} R_{2} and R_{1} →^{r} R_{2} using only Lemma 3.17. The first case is simply an empty intersection of (computation-)admissible relations. The constructed relation for products is the intersection of the two admissible relations obtained by inverse images of the projections on the admissible relations R_{1} and R_{2}. Moreover, since projections are rigid, R_{1} × R_{2} is also computation-admissible if both R_{1} and R_{2} are. Finally, R_{1}→R_{2} can be expressed as an intersection of the family (R′_{j})_{j∈R_{1}} of admissible relations, where each R′_{j} is given by an inverse-image construction: f R′_{(a,a′)} f′ ⟺ f a R_{2} f′ a′. And again, since application is rigid, computation-admissibility of R_{2} implies computation-admissibility of R_{1} → R_{2}.

The cases for natural numbers and sums, on the other hand, depend on the specifics of the model; see Lemma A.10(1,2).

The reason for restricting attention to (computation-)admissible relations is that they validate the following binary version of fixed-point induction:

Lemma 3.19 Let S ∈ CARel(β, β′), and let f ∈ Val_{L}(β → β) and f′ ∈ Val_{L′}(β′ → β′) be such that ∀b S b′. f b S f′ b′. Then fix_{β} f S fix_{β′} f′.

Proof. See Lemma A.11.

Effectively, this is saying that for any computation-admissible relation S, the two interpretations of fix are related by (S →^{r} S) →^{r} S.

3.2.3 Computation-extension of relations

A key concept we will make use of in the following is the extension of a value-relation to a relation on computations. Intuitively, two computations are considered related if they both have the same (or, more generally, related) effects, and if any results they pass on to further computations are related by the original relation.

For example, in the case of partiality, two computations are related if they either both diverge, or both converge to related values. For exceptions, two computations are related if they produce related successful answers, or raise the same exception. For state (given a fixed relation on states), they must map related initial states to related values and related final states. And for two control-computations to be related, when invoked with related continuations (i.e., mapping related values to related final answers), they must themselves produce related final answers.

Much as monads abstract out the common equational properties of effects into a simple set of axioms for the unit and extension functions, we can characterize the minimal requirements for a relation-extension as follows:

Definition 3.20 A computation-extension of relations assigns to any pair of types α and α′, and admissible relation R ∈ ARel(α, α′), a computation-admissible relation˚ R ∈ CARel(˚ α,˚ α′), such that for all admissible R, R_{1} and R_{2}, the following holds:

1. ∀a R a′.˚ a (˚ R)˚ a′.

2. If ∀a R_{1} a′. f a (˚ R_{2}) f′ a′ then ∀m (˚ R_{1}) m′. let˚ x ⇐ m in f x (˚ R_{2}) let˚ x′ ⇐ m′ in f′ x′.

That is, if two terms are related as values, their inclusions into computations must also be related. And if two parameterized computations are related for every pair of related parameters, they must remain related when prefixed by related computations computing values for those parameters. A simple instance is given by the following:

Proposition 3.21 In the standard partiality semantics (i.e., with T A = T′A = A_{⊥}), taking for any R,˚ R to be the lifting of R, i.e.,

m (˚ R) m′ ⟺ (∃v R v′. m =˚ v ∧ m′ =˚ v′) ∨ (m = ⊥_{˚ α} ∧ m′ = ⊥_{˚ α′})

determines a computation-extension.

Proof. Lemma A.10(4) shows that ˚ R is computation-admissible. Condition (1) (v R v′ ⇒ ˚ v (˚ R) ˚ v′) is also immediate. For (2), assume m (˚ R_{1}) m′ and ∀a R_{1} a′. f a (˚ R_{2}) f′ a′. There are two cases, one for each disjunct in the definition of R̊_{1}:

• m =˚ v and m′ =˚ v′ for some v R_{1} v′. Then we get the result directly by assumption on f and f′:

let˚ x ⇐ m in f x = f v (˚ R_{2}) f′ v′ = let˚ x′ ⇐ m′ in f′ x′

• m = ⊥, m′ = ⊥. Then, by the second disjunct in the definition of R̊_{2},

let˚ x ⇐ m in f x = ⊥ (˚ R_{2}) ⊥ = let˚ x′ ⇐ m′ in f′ x′

(using Proposition 2.13 to obtain the equalities)

We will see later (Proposition 3.40) how to systematically construct computationextensions for effects defined by an explicit monadic translation.

3.3 The simulation proof

To avoid repetition in the following, we first define:

Definition 3.22 (Persistent assumption) Throughout this section, we will assume that there is given a relational correspondence between interpretations L_{s} of L_{0} (the specification language) and L_{i} of a signature L_{0}^{+} ⊇ L_{0} (the implementation language), with a fixed computation-extension of relations. In particular, all unqualified occurrences of ARel(α, α′), CARel(β, β′), and˚ R will refer to this correspondence.

For concreteness, it may help to think of L_{0}^{+} as L_{0} extended with recursive types and embedding types (which is what we will use in the monad-continuation case in Section 3.3.4), L_{s} as the partiality semantics and L_{i} as a continuation semantics (for base computations, not to be confused with a continuation monad defined in (L_{0}^{+}, L_{i}); we will use such a continuation-based interpretation of ambient effects in Chapter 4).

3.3.1 Overview

A monad morphism i gives us a simple way of converting a T -computation t to the U-computation representing it, by taking u = i_{α} t. However, this simple relationship does not extend directly to functions on computations. For example, given a function f : T α → T α′, how would we obtain the g : Uα → Uα′, representing f?

If i has a left inverse j, we could try taking g = i_{α′} ∘ f ∘ j_{α}. This is not really satisfactory, however: for example, when α = α′ and f is the identify function, we get g : Uα → Uα = i_{α} ∘ j_{α}, meaning that the identity on Uα would in general not be the correct representation of the identity on T α.

A better approach, therefore, is to characterize instead what a correct U-based representation g of f would be, for example by requiring it to satisfy the equation i_{α′} ∘f = g∘i_{α}. In general, then, instead of a function from higher-order values involving T -computations to the corresponding ones with U-computations, we get a (binary) relation.

The general outline of the proof is then as follows. First, for any type family α in L_{0}, we define a family of logical relations ⟨⟨α⟩⟩, and show that for any relational interpretation of the type parameters, the two interpretations of a term family M of L_{0} are related by ⟨⟨α⟩⟩. In particular, this means that the term components of any monad-triple T in L_{0} are related in the two interpretations. This gives us a way of talking about related T -effects in the two languages, even when T is not a monad in L_{i}.

We then define the general notion of a monad relation between a monad T in the specification language and a monad U in the implementation language. This is a more general notion than existence of a monad morphism from T to U: instead of assigning to every α a function from T α to Uα, we only assign a binary relation. More precisely, to every relation R ∈ ARel(α, α′), we assign a relation R ∈ ARel(T α, Uα′).

However, if U is also a monad in the specification language, any monad morphism i from T to U induces in a canonical way a monad relation between T and U, by taking t ( R) u ⟺ it (UR) u, where UR is the standard, syntactically-derived action of U on relations. This way of constructing monad relations covers most of our sample monad simulations – all except the general monad-continuation case.

Given a monad relation between T and U, we can now exhibit a family of relations indexed by L_{0}^{T} -types α, ⊴_{α} ∈ ARel([[α]]_{T} , [[α]]_{U}), defined in the usual inductive way for the standard type constructors, and taking ⊴_{α}= ⊴_{α} ∈ ARel(T [[α]]_{T} , U[[α]]_{U}).

Further, we show that the two translations, [[ ]]_{T} : L_{0}^{T} → L_{0} and [[ ]]′_{T} : L_{0}^{T} → L_{0}^{+} of an L_{0}^{T} -term of type α are related by ⊴_{α}. Since in particular ⊴_{˚ ι}=˚ (ι^{r}), this says that the two translations coincide for complete programs.

Returning to the monad-continuation case, we show how to construct the appropriate monad relation between T and K_{o} explicitly. (Intuitively, we need the generalization from an equational to a relational characterization of the relationship between the monads for the same reason that forced us to adapt a relational approach for higher-order values: when we embed non-simple types in the answer type o of the continuation monad, the K_{o}-based representation of T -effects will in general not be unique.)

For this simulation, we also rely on the fact that our implementation language may contain additional types and terms beyond those in L_{0}; in particular, depending on how general a notion of T -reification we want to simulate, we will need recursive types and/or embedding-types to construct K_{o}. Once we have established the monad relation, it is a simple consequence of the properties of ⊴ that the definitional T -translation and the variant, or continuation-passing, translation agree on complete programs.

Finally, we show how to lift the simulation result from a relationship between translations to a relationship between source terms. Specifically, the basic motivation for adding reflection and reification to our source language was precisely to permit programs to be written in direct style instead of in effect-passing style. And in fact, it is possible to express the simulation result at the source level, by defining the reflection and reification operators for T in terms of those for K_{o}. Thus, we do not need a monad-specific variant translation for implementing T-effects with continuations, but can use a fixed continuation-passing translation for all such effects.

3.3.2 Relating standard terms

We first show that a large collection of terms are related by the relations determined systematically from their types:

Definition 3.23 Let Δ be a finite set of type variables, θ a substitution of closed L_{0}- types for variables in Δ, and θ′ of closed L_{0}^{+}-types. Further, let ϱ assign to each type variable a ∈ Δ a relation ϱ a ∈ ARel(θ a, θ′ a). To every type α over Δ in L_{0}, we then assign a relation ⟨⟨α⟩⟩^{ϱ} ∈ ARel(α{θ}, α{θ′}) (such that ⟨⟨β⟩⟩^{ϱ}∈ CARel(β{θ}, β{θ′})) as follows:

⟨⟨a⟩⟩^{ϱ} = ϱ a
⟨⟨ι⟩⟩^{ϱ} = ι^{r}
⟨⟨1⟩⟩^{ϱ} = 1^{r}
⟨⟨α_{1} × α_{2}⟩⟩^{ϱ} = ⟨⟨α_{1}⟩⟩^{ϱ} ×^{r} ⟨⟨α_{2}⟩⟩^{ϱ}
⟨⟨α_{1} + α_{2}⟩⟩^{ϱ} = ⟨⟨α_{1}⟩⟩^{ϱ} +^{r} ⟨⟨α_{2}⟩⟩^{ϱ}
⟨⟨α → β⟩⟩^{ϱ} = ⟨⟨α⟩⟩^{ϱ} →^{r} ⟨⟨β⟩⟩^{ϱ}
⟨⟨˚ α⟩⟩^{ϱ} = ˚ ⟨⟨α⟩⟩^{ϱ}

We extend this definition pointwise to relate value-substitutions σ and σ′, i.e., if for each (x_{i}: α_{i}) ∈ Γ, (σx_{i}) ⟨⟨α_{i}⟩⟩^{ϱ} (σ′x_{i}), we write σ ⟨⟨Γ⟩⟩^{ϱ} σ′.

The (computation-)admissibility of these relation follows directly from Lemma 3.18. It is also easy to see that we have the usual weakening and substitution principles,

⟨⟨α⟩⟩^{ϱ} = ⟨⟨α⟩⟩^{ϱ[a ↦ R]} (a ∉ F T V (α)) and ⟨⟨α{α′/a}⟩⟩^{ϱ} = ⟨⟨α⟩⟩^{ϱ[a ↦ ⟨⟨α′⟩⟩^{ϱ}]}

Lemma 3.24 (logical relations lemma) Let θ and θ′ be substitutions of closed types for Δ-variables, and ϱ a relation assignment for θ and θ′ (as in Definition 3.23). Further, let Γ and α be a type assignment and a type over Δ, both in L_{0}, and let M be a term of L_{0} with Γ ⊢_{Δ} M : α. Finally, let σ and σ′ be substitutions of values from L_{s} and L_{i} for variables in Γ, such that σ ⟨⟨Γ⟩⟩^{ϱ} σ′. Then M^{θσ} ⟨⟨α⟩⟩^{ϱ} M^{θ′σ′}.

Proof. By induction on the structure of M:

• Case x_{i}, where (x_{i}: α_{i}) ∈ Γ. To show: x_{i}^{θσ} ⟨⟨α_{i}⟩⟩^{ϱ} x_{i}^{θ′σ′}, i.e., that σx_{i} ⟨⟨α_{i}⟩⟩^{ϱ} σ′x_{i}, which follows directly from the assumption on σ and σ′.

• Case z. To show:

∃n ∈ N. z^{θσ} = \underline{n} ∧ z^{θ′σ′} = \underline{n}

Since z^{θσ} = z, we can simply take n = 0.

• Case s M. To show:

∃n ∈ N. s (M^{θσ}) = \underline{n} ∧ s (M^{θ′σ′}) = \underline{n}

By IH on M, we already have

∃m ∈ N. M^{θσ} = \underline{m} ∧ M^{θ′σ′} = \underline{m}

so we get the result by taking n = m + 1.

• Case ifz(M, M_{z}, x. M_{s}). To show:

ifz(M^{θσ}, M_{z}^{θσ}, x. M_{s}^{θσ}) ⟨⟨α⟩⟩^{ϱ} ifz(M^{θ′σ′}, M_{z}^{θ′σ′}, x. M_{s}^{θ′σ′})

By IH on M, we know that (in L_{s}) M^{θσ} = \underline{n} and (in L_{i}) M^{θ′σ′} = \underline{n} for some natural number n. There are then two cases:

– Case n = 0. By IH on M_{z}, we get ifz(z, M_{z}^{θσ}, x_{s}. M_{s}^{θσ}) = M_{z}^{θσ} ⟨⟨α⟩⟩^{ϱ} M_{z}^{θ′σ′} = ifz(z, M_{z}^{θ′σ′}, x_{s}. M_{s}^{θ′σ′})

– Case n = m + 1. Then ifz(s \underline{m}, M_{z}^{θσ}, x. M_{s}^{θσ}) = M_{s}^{θσ}\{\underline{m}/x\} = M_{s}^{θ(σ,(\underline{m}/x))}

and analogously on the RHS, so the result follows by IH on M_{s}, in the type assignment (Γ, x: ι) and the extended substitutions (σ, \underline{m}/x) and (σ′, \underline{m}/x).

• Case ⟨⟩. To show:

⟨⟩^{θσ} ⟨⟨1⟩⟩^{ϱ} ⟨⟩^{θ′σ′}

which is trivially true by the definition of ⟨⟨1⟩⟩^{ϱ}.

• Case ⟨M_{1}, M_{2}⟩. To show:

⟨M_{1}, M_{2}⟩^{θσ} ⟨⟨α_{1} × α_{2}⟩⟩^{ϱ} ⟨M_{1}, M_{2}⟩^{θ′σ′}

That is,

fst (⟨M_{1}, M_{2}⟩^{θσ}) ⟨⟨α_{1}⟩⟩^{ϱ} fst (⟨M_{1}, M_{2}⟩^{θ′σ′})
∧ snd (⟨M_{1}, M_{2}⟩^{θσ}) ⟨⟨α_{2}⟩⟩^{ϱ} snd (⟨M_{1}, M_{2}⟩^{θ′σ′})

Since we have

fst (⟨M_{1}, M_{2}⟩^{θσ}) = fst ⟨M_{1}^{θσ}, M_{2}^{θσ}⟩ = M_{1}^{θσ}

and analogously on the RHS and for snd, we get the result by IH on M_{1} and M_{2}.

• Case fst M (snd M is analogous). To show:

fst (M^{θσ}) ⟨⟨α_{1}⟩⟩^{ϱ} fst (M^{θ′σ′})

which we get from IH on M and the first conjunct of the definition of ⟨⟨α_{1} × α_{2}⟩⟩^{ϱ}.

• Case inl M (inr M is analogous). To show:

inl M^{θσ} ⟨⟨α_{1} + α_{2}⟩⟩^{ϱ} inl M^{θ′σ′}

By the first disjunct in the definition of ⟨⟨α_{1} + α_{2}⟩⟩^{ϱ}, it suffices to show that

M^{θσ} ⟨⟨α_{1}⟩⟩^{ϱ} M^{θ′σ′}

which we get from IH on M.

• Case case(M, x_{1}.M_{1}, x_{2}.M_{2}). To show:

case(M^{θσ}, x_{1}.M_{1}^{θσ}, x_{2}.M_{2}^{θσ}) ⟨⟨α⟩⟩^{ϱ} case(M^{θ′σ′}, x_{1}.M_{1}^{θ′σ′}, x_{2}.M_{2}^{θ′σ′}) By IH on M, we have M^{θσ} ⟨⟨α_{1} + α_{2}⟩⟩^{ϱ} M^{θ′σ′}. Without loss of generality, assume that we are in the first case of the definition of ⟨⟨α_{1} + α_{2}⟩⟩^{ϱ}. That is, M^{θσ} = inl a_{1} and M^{θ′σ′} = inl a′_{1} for some a_{1} ⟨⟨α_{1}⟩⟩^{ϱ} a′_{1}. Then

case(M^{θσ}, x_{1}.M_{1}^{θσ}, x_{2}.M_{2}^{θσ}) = case(inl a_{1}, x_{1}.M_{1}^{θσ}, x_{2}.M_{2}^{θσ}) = M_{1}^{θσ}{a_{1}/x_{1}}

= M_{1}^{θ(σ,(a_{1}/x_{1}))}

and analogously on the RHS, so we get the result by IH on M_{1} using the extended substitutions σ_{1} = (σ, a_{1}/x_{1}) and σ′_{1} = (σ′, a′_{1}/x_{1}).

• Case λx^{α}.M. To show:

(λx^{α}.M)^{θσ} ⟨⟨α → β⟩⟩^{ϱ} (λx^{α}.M)^{θ′σ′}

I.e., that

λx^{α{θ}}.M^{θσ} ⟨⟨α → β⟩⟩^{ϱ} λx^{α{θ′}}.M^{θ′σ′}

Accordingly, let a ⟨⟨α⟩⟩^{ϱ} a′; we must show that

(λx^{α{θ}}.M^{θσ})a ⟨⟨β⟩⟩^{ϱ} (λx^{α{θ′}}.M^{θ′σ′})a′

And since

(λx^{α{θ}}.M^{θσ})a = M^{θσ}{a/x} = M^{θ(σ,(a/x))}

we get the result by IH on Γ, x: α ⊢_{Δ} M : β using the extended substitutions σ_{1} = (σ, a/x) and σ′_{1} = (σ′, a′/x).

• Case M_{1} M_{2}. To show:

(M_{1}^{θσ})(M_{2}^{θσ}) ⟨⟨β⟩⟩^{ϱ} (M_{1}^{θ′σ′})(M_{2}^{θ′σ′})

This follows directly from IH on M_{1} and M_{2}, and the definition of ⟨⟨α → β⟩⟩^{ϱ}.

• Case˚ M. To show:

˚ (M^{θσ}) ⟨⟨˚ α⟩⟩^{ϱ}˚ (M^{θ′σ′})

By IH on M, M^{θσ} ⟨⟨α⟩⟩^{ϱ} M^{θ′σ′}, so the result follows from the definition of ⟨⟨˚ α⟩⟩^{ϱ} and the properties of a computation-extension (Definition 3.20(1)).

• Case let˚ x ⇐ M_{1} in M_{2}. To show:

let˚ x ⇐ M_{1}^{θσ} in M_{2}^{θσ} ⟨⟨˚ α_{2}⟩⟩^{ϱ} let˚ x ⇐ M_{1}^{θ′σ′} in M_{2}^{θ′σ′}

By IH on M_{1}, we have M_{1}^{θσ} ⟨⟨˚ α_{1}⟩⟩^{ϱ} M_{1}^{θ′σ′}, i.e.,

M_{1}^{θσ} (˚ ⟨⟨α_{1}⟩⟩^{ϱ}) M_{1}^{θ′σ′}

Similarly, by IH on M_{2} with appropriately extended substitutions, we get

∀a_{1} ⟨⟨α_{1}⟩⟩^{ϱ} a′_{1}. M_{2}^{θ(σ,(a_{1}/x))} (˚ ⟨⟨α_{2}⟩⟩^{ϱ}) M_{2}^{θ′(σ′,(a′_{1}/x))}

And from those two facts and Definition 3.20(2), we get the required result.

• Case fix_{β} M. To show:

fix_{β{θ}} M^{θσ} ⟨⟨β⟩⟩^{ϱ} fix_{β{θ′}} M^{θ′σ′}

By IH on M, we have M^{θσ} ⟨⟨β → β⟩⟩^{ϱ} M^{θ′σ′}, i.e.,

∀b ⟨⟨β⟩⟩^{ϱ} b′. M^{θσ} b ⟨⟨β⟩⟩^{ϱ} M^{θ′σ′} b′

The result then follows directly from fixed-point induction (Lemma 3.19), because ⟨⟨β⟩⟩^{ϱ} is computation-admissible.

As a simple corollary of Lemma 3.24, we obtain that the two interpretations of a monad-triple are related:

Lemma 3.25 Let T be a monad-triple in L_{0}. Then the standard relational action of T, given by

t (T R) t′ ⟺ t ⟨⟨T a⟩⟩^{a↦R} t′

respects the monad operations in the sense that for any R, R_{1}, and R_{2}, the following conditions are satisfied:

0. If ∀a R_{1} a′. f a (T R_{2}) f′ a′ then ∀m (˚ R_{1}) m′. let_{˚ T α_{2}} x ⇐ m in f x (T R_{2}) let_{˚ T α′_{2}} x′ ⇐ m′ in f′ x′.

1. ∀a R a′. η_{α} a (T R) η_{α′} a′.

2. If ∀a R_{1} a′. f a (T R_{2}) f′ a′ then ∀t (T R_{1}) t′. f^{∗} t (T R_{2}) f^{′∗} t′

Proof. All cases are simple:

0. By assumption on f and f′, we have

f ⟨⟨a_{1} → T a_{2}⟩⟩^{a_{1}↦R_{1},a_{2}↦R_{2}} f′

and by assumption on m and m′,

m ⟨⟨˚ a_{1}⟩⟩^{a_{1}↦R_{1},a_{2}↦R_{2}} m′

Then use Lemma 3.24 on the term

x_{f}: a_{1} → T a_{2}, x_{m}:˚ a_{1} ⊢_{{a_{1},a_{2}}} let_{˚ T a_{2}} x ⇐ x_{m} in x_{f} x : T a_{2}

(which, recall, abbreviates a term of the core syntax, given by expanding the generalized let according to the shape of T) with the substitutions σ = (f/x_{f}, m/x_{m}) and σ′ = (f′/x_{f}, m′/x_{m}).

1. Analogous to above, using the term x_{a}: a ⊢_{{a}} η_{a} x_{a} : T a.

2. Analogous to above, with x_{f}: a_{1} → T a_{2}, x_{t}: T a_{1} ⊢_{{a_{1},a_{2}}} x_{f}^{∗} x_{t} : T a_{2}.

3.3.3 Relating computational structure

We are now ready to characterize what it means for two monads to be related:

Definition 3.26 A monad relation between monads T = (T, η, ^{∗}) in (L_{0}, L_{s}) and U = (U, ε, ^{+}) in (L_{0}^{+}, L_{i}) assigns to every admissible relation R ∈ ARel(α, α′) a computationadmissible relation R ∈ CARel(T α, Uα′) such that for all admissible relations R, R_{1}, and R_{2},

0. If ∀a R_{1} a. f a ( R_{2}) g a′ then ∀m (˚ R_{1}) m′. let_{˚ T α_{2}} x ⇐ m in f x ( R_{2}) let_{˚ Uα′_{2}} x′ ⇐ m′ in g x′.

1. ∀a R a′. η_{α} a ( R) ε_{α′} a′.

2. If ∀a R_{1} a′. f a ( R_{2}) g a′ then ∀t ( R_{1}) u. f^{∗} t ( R_{2}) g^{+} u.

Further, we say that an L_{0}^{+}-term i_{α′} : T α′ → Uα′ is a reflection function (with respect to the monad relation) if for any L_{0}-type α and relation R ∈ ARel(α, α′),

3. ∀t (T R) t′. t ( R) i_{α′}t′

Analogously, a reification function is an L_{0}^{+}-term j_{α′} : Uα′ →T α′ such that for any α and R ∈ ARel(α, α′),

4. ∀t ( R) u. t (T R) j_{α′}u (Note that since T need not satisfy the monad laws in L_{i}, i cannot in general be a monad morphism. Nor do we explicitly require that j_{α′} ∘i_{α′} = id_{T α′}, although it often does hold.)

An important special case is when a monad-triple T = U is a monad in both L_{s} and L_{i}. Then Lemma 3.25 shows that the standard relational action of T induces a monad relation between the two copies of T; moreover, for any α′, we can simply take i_{α′} = j_{α′} = id_{T α′}. (There is still some non-trivial content to the definition in this case, because L_{s} and L_{i} could be different models, with their ambient effects related only by relation-extension.)

More generally, we have the following convenient way of obtaining monad relations directly from monad morphisms:

Proposition 3.27 Let T and U be monad-triples in L_{0} such that T is a monad in L_{s} and U is a monad in both L_{s} and L_{i}. Further, let i in (L_{0}, L_{s}) be a monad morphism from T to U. Then the assignment to any R ∈ ARel(α, α′) of the R ∈ CARel(T α, Uα′) given by

t ( R) u ⟺ i_{α} t (UR) u

establishes a monad relation between T and U.

Moreover, for any α′ of L_{0}^{+}, i_{α′} is a reflection function. And if j is a schematic retraction of i in (L_{0}, L_{s}) then for any α′ in L_{0}^{+}, j_{α′} is a reification function.

Proof. First, R is computation-admissible, because it is defined as an inverse image of the computation-admissible UR by the rigid functions i_{α} and id_{Uα′}. Further, we have:

0. Let m (˚ R_{1}) m′ and f (R_{1} → R_{2}) g be given. To show:

i_{α_{2}} (let_{˚ T α_{2}} a ⇐ m in f a) (UR_{2}) let_{˚ Uα′_{2}} a′ ⇐ m′ in g a′

By rigidity of i_{α_{2}} (Definition 3.1(0)) on the LHS, this amounts to showing

let_{˚ Uα_{2}} a ⇐ m in i_{α_{2}} (f a) (UR_{2}) let_{˚ Uα′_{2}} a′ ⇐ m′ in g a′

We get that from assumption on m and m′, and the properties of U’s relational action (Lemma 3.25(0)). if we can establish that

∀a R_{1} a′. i_{α_{2}} (f a) (UR_{2}) g a′

And that was precisely the assumption on f and g.

1. Let a R a′. To show: i_{α} (η_{α} a) (UR) ε_{α′} a′. By Definition 3.1(1) on the LHS, this is

equivalent to showing

ε_{α} a (UR) ε_{α′} a′

which we get from Lemma 3.25(1) for U.

2. Let t ( R_{1}) u and f (R_{1} → R_{2}) g be given as in the hypothesis. We must show that

i_{α_{2}} (f^{∗} t) (UR_{2}) g^{+} u

Again, using the property of a monad morphism 3.1(2) on the LHS, this amounts to showing

(i_{α_{2}} ∘ f)^{+} (i_{α_{1}} t) (UR_{2}) g^{+} u

Now, by assumption on t and u, we have

i_{α_{1}} t (UR_{1}) u

and by assumption on f and g,

∀a R_{1} a′. (i_{α_{2}} ∘ f)a (UR_{2}) g a′

from which we get the desired result by 3.25(2).

3. Let t (T R) t′; we must show that t ( R) i_{α′} t′, i.e., that i_{α} t (UR) i_{α′} t′. And since i_{a} was a term of L_{0}, this follows from Lemma 3.24 by an argument analogous to those in Lemma 3.25.

4. Let t ( R) u; to show: t (T R) j_{α′} u. From the assumption on t and u, we have i_{α} t (UR) u, and hence again by Lemma 3.24 with M = j_{a}, we get j_{α} (i_{α} t) (T R) j_{α′} u. Then cancelling the j_{α} and i_{α} on the LHS gives us the result.

A monad relation with reflection and reification functions is exactly what we need to relate the definitional and the variant translation: the monad relation itself relates the computational structure, and the reflection and reification functions, where they exist, convert between effect representations:

Definition 3.28 Let T be a monad in (L_{0}, L_{s}) and U a monad in (L_{0}^{+}, L_{i}), with a monad relation between T and U. Then for any type α of L_{0}^{T}, the relation

⊴_{α} ∈ ARel([[α]]_{T} , [[α]]_{U})

is given in the usual way by induction on the structure of α (as in Definition 3.23, but without the type variables), and with ⊴_{α}= ⊴_{α} from the monad relation.

Note in particular that since the standard relational action of T in Lemma 3.25 is also given by Definition 3.23, we have ⊴_{T α}= T ⊴_{α} for any L_{0}^{T} -type α. We can now state:

Proposition 3.29 Let there be given a monad relation between T and U, with a reflection function i at every type [[α]]_{U} where α is a type of L_{0}^{T}. Further, let ℵ be a family of L_{0}^{T} -types, with j a reification function at every [[ℵ(i)]]_{U}. Let Γ ⊢ M : α be a term of L_{0}^{T [ℵ]} (i.e., with reifications only at types in ℵ). Then for any pair of substitutions of ⊴-related values for the variables in Γ, σ ⊴_{Γ} σ′, we have [[M]]σ_{T} ⊴_{α} [[M]]′_{T}^{σ′} (where [[ ]]′_{T} is the variant translation from Definition 3.9).

Proof. Given the definitions, this is a simple induction on the structure of M. The cases for variables, numbers, products, sums, functions, and base computations are exactly as before (Lemma 3.24). For the remaining constructs, we have:

• Case M. To show:

η [[M]]σ_{T} ⊴_{α} ε[[M]]′_{T}^{σ′}

Follows from IH on M and Definition 3.26(1).

• Case let˚ x ⇐ M_{1} in M_{2} (of type α_{2}). To show:

let_{˚ T [[α_{2}]]_{T}} x ⇐ [[M_{1}]]σ_{T} in [[M_{2}]]σ_{T} ⊴_{α_{2}} let_{˚ U[[α_{2}]]_{U}} x ⇐ [[M_{1}]]′_{T}^{σ′} in [[M_{2}]]′_{T}^{σ′}

By IH on M_{1} and M_{2}, and 3.26(0).

• Case let x ⇐ M_{1} in M_{2}. To show:

(λx.[[M_{2}]]σ_{T})^{∗} [[M_{1}]]σ_{T} ⊴_{α_{2}} (λx.[[M_{2}]]′_{T}^{σ′})^{+} [[M_{1}]]′_{T}^{σ′}

Follows from IH on M_{1} and M_{2} using 3.26(2).

• Case μ(M). To show:

[[M]]σ_{T} ⊴_{α} i[[M]]′_{T}^{σ′}

Since ⊴_{T α} = T ⊴_{α}, this follows from IH on M and Definition 3.26(3).

• Case [M]. To show:

[[M]]σ_{T} ⊴_{T α} j[[M]]′_{T}^{σ′}

As above, ⊴_{T α} = T ⊴_{α}, so we get the result by IH on M and Definition 3.26(4).

• Case fix. As before, we need to check that ⊴_{β} is computation-admissible, where β may now also contain the type α. And in that case, computation-admissibility of ⊴_{α} is ensured by the requirement of a monad relation (Definition 3.26).

Using the monad relation induced by a monad morphism (Proposition 3.27), Proposition 3.29 immediately gives us correctness of a number of effect-simulations. For example, the complexity-state monad morphism from Example 3.3 validates the statebased maintenance of complexities.

For our monad-continuation simulation in full generality, however, we have to work a little harder. To obtain reification at arbitrary types, we cannot use purely equational properties of the monad morphism and the standard relational action of U alone – we need to construct the appropriate monad relation explicitly.

3.3.4 Relating monads to continuation-passing

Recall from Example 3.10 that the main limitation of our monad-morphism formulation of the continuation-based variant translation was its incomplete treatment of T -reification. Specifically, it did not directly allow us to define a reification operation (1) at more than one type and (2) at types containing . We will now see how to overcome these problems by using a more elaborate monad relation.

At the same time, we will take care of an independent technical complication (3) with simulating a monad T with a continuation monad K_{o}. Following Lemma 3.5, we would expect to take the answer type o to be T γ for some γ. However, the constraints imposed by our eventual application in Section 3.3.5 will not always allow us to do this. Specifically, we will need the answer type to be expressible as˚ ω for some ω, but T γ is not necessarily of this form.

Fortunately, the relational approach allows us considerable latitude in picking the actual answer type o, as long as it is “larger” than the T γ that we originally needed. (Because the answer type occurs both positively and negatively in the continuation monad, we cannot express this condition purely equationally.) For conciseness, we formulate the requirement in general terms:

Definition 3.30 An answer-embedding of L_{0}^{+}-computation-types o_{1} into o_{2} consists of a pair of functions ϕ^{•} : o_{1} → o_{2} and ψ^{•} : o_{2} → o_{1}, such that in L_{i}, (1) ψ^{•} is rigid and (2) ψ^{•} ∘ ϕ^{•} = id_{o_{1}}.

(Taking o_{1} = o_{2} = T γ and ϕ^{•} = ψ^{•} = id_{T γ} certainly satisfies these requirements, and still gives us a result strong enough to solve problems (1) and (2) mentioned above. Thus, on a first reading, it may be helpful to simply ignore all occurrences of ϕ^{•} and ψ^{•} throughout this section. However, for the purpose of the next section, it is important that the we only rely on the weaker properties guaranteed by Definition 3.30.)

We are now ready to state a central result about relating monads and continuations. The essential trick is that, although we commit to a fixed answer type for the continuation monad, we are still free to consider all possible relational interpretations of that type:

Lemma 3.31 (continuation-simulation of monads) Let T be a monad in (L_{0}, L_{s}). Further, let γ be a type and o a computation-type of L_{0}^{+}, with an answer-embedding ϕ^{•} : T γ →o and ψ^{•} : o→T γ. Then the mapping of R ∈ ARel(α, α′) to R ∈ CARel(T α, K_{o}α′) given by

t ( R) u
⟺ ∀α_{0} type_{L_{0}}, O ∈ ARel(α_{0}, γ), k ∈ Val_{L_{s}}(α → T α_{0}), k′ ∈ Val_{L_{i}}(α′ → o). (∀a R a′. k a (T O) ψ^{•} (k′ a′)) ⇒ k^{∗} t (T O) ψ^{•} (uk′)

is a monad relation between T and K_{o}. Moreover,

i_{α′} = λt^{T α′}.λk^{α′→o}.ϕ^{•} ((ψ^{•} ∘ k)^{∗} t)  and  j_{γ} = λu^{(γ→o)→o}.ψ^{•} (u(ϕ^{•} ∘ η_{γ}))

form a reflection function for all α′ and a reification function for γ.

(Intuitively, the outer quantification over α_{0} allows us to overcome limitation (1) from above; if we only needed reification at a single L_{0}^{T} -type α, we could simply fix α_{0} = [[α]]_{T}. Further, the inner quantification over O takes care of (2), by replacing a fixed relation on answer types (where in particular γ may be recursive) with a stronger parametricity condition. And finally, as already mentioned, we need ϕ^{•} and ψ^{•} for (3).

It is instructive to compare the cases of the following proof with the corresponding ones in Proposition 3.27. Although some common structure could clearly be abstracted out, it is probably easier to follow how the continuations are being passed around in a concrete formulation.)

Proof. First, we check that R is computation-admissible when R is admissible: R is defined as an intersection over inverse images of the computation-admissible relations T O by the rigid functions k^{∗} and ψ^{•} ∘ λu.uk′. For the specific requirements, we have:

0. Assume ∀a R_{1} a′. f a ( R_{2}) g a′ and m (˚ R_{1}) m′. To show:

let_{˚ T α_{2}} x ⇐ m in f x ( R_{2}) let_{˚ (α′_{2}→o)→o} x′ ⇐ m′ in g x′

i.e., that

let_{˚ T α_{2}} x ⇐ m in f x ( R_{2}) λk′.let_{˚ o} x′ ⇐ m′ in g x′ k′ .

Let O, k, and k′ be given, with

∀a R_{2} a′. k a (T O) ψ^{•} (k′ a′) .

We must then show that

k^{∗} (let_{˚ T α_{2}} x ⇐ m in f x) (T O) ψ^{•} (let_{˚ o} x′ ⇐ m′ in g x′ k′) .

Using rigidity of k^{∗} (Definition 2.15(0)) on the LHS and rigidity of ψ^{•} (Definition 3.30(1)) on the RHS, this is equivalent to showing

let_{˚ T α_{0}} x ⇐ m in k^{∗} (f x) (T O) let_{˚ T γ} x′ ⇐ m′ in ψ^{•} (g x′ k′) .

Now, by Lemma 3.25(0) and the assumption that m (˚ R_{1}) m′, it suffices to show that

∀a R_{1} a′. k^{∗} (f a) (T O) ψ^{•} (g a′ k′)

and that follows from the assumption that f a ( R_{2}) g a′.

1. Assume a R a′. Then for O, k, and k′ as above, we must show that

k^{∗} (η a) (T O) ψ^{•} (εa′ k′)

i.e., using law 2.15(1) and the definition of ε, that

k a (T O) ψ^{•} (k′ a′)

which was precisely the assumption on k and k′.

2. Assume ∀a R_{1} a′. f a ( R_{2}) g a′ and t ( R_{1}) u.

Let O, k and k′ be given as before; to show:

k^{∗} (f^{∗} t) (T O) ψ^{•} (g^{+} uk′)

using monad law 2.15(3) on the LHS and expanding the RHS, this amounts to showing

(λx.k^{∗} (f x))^{∗} t (T O) ψ^{•} (u(λx.g xk′))

This follows from the definition of t ( R_{2}) u if we can show that

∀a R_{1} a′. (λx.k^{∗} (f x))a (T O) ψ^{•} ((λx.g xk′)a′)

i.e., that

∀a R_{1} a′. k^{∗} (f a) (T O) ψ^{•} (g a′ k′)

And that follows from the assumption that f a ( R_{2}) g a′.

3. Again, let O, k, and k′ be given with ∀a R a′. k a (T O) ψ^{•} (k′ a′), and t (T R) t′; we

must show that

k^{∗} t (T O) ψ^{•} (ϕ^{•} ((ψ^{•} ∘ k′)^{∗} t′))

i.e., cancelling the ψ^{•} and ϕ^{•} (Definition 3.30(2)), that

k^{∗} t (T O) (ψ^{•} ∘ k′)^{∗} t′ .

By Lemma 3.25(2), it suffices to show that

∀a R a′. k a (T O) (ψ^{•} ∘ k′)a′ .

And that was precisely the assumption on k and k′.

4. Let R ∈ ARel(α, γ) be given, with t ( R) u. To show:

t (T R) ψ^{•} (u(ϕ^{•} ∘ η_{γ}))

Here we finally need to instantiate the O in the definition of R. Take

α_{0} = α,  O = R,  k = η_{α},  k′ = ϕ^{•} ∘ η_{γ} .

Clearly this O is admissible, because R was assumed to be. Further, let a R a′. Then, because η respects the relational action of T (Lemma 3.25(1)), we have:

k a = η a (T O) η a′ = ψ^{•} (ϕ^{•} (η a′)) = ψ^{•} (k′ a′)

From the assumption on t and u, and monad law 2.15(2), we therefore obtain

t = η^{∗} t = k^{∗} t (T O) ψ^{•} (uk′) = ψ^{•} (u(ϕ^{•} ∘ η))

as required.

Although the construction only gives reification at γ directly, by choosing γ appropriately, we can define reification functions at other types:

Lemma 3.32 Let there be a monad relation between T and U, and let j_{γ} : Uγ → T γ be a reification function at (L_{0}^{+}-type) γ. Let α′ be any type of L_{0}^{+} with term constructors ϵ : α′ → γ and δ : γ →˚ α′ such that (in L_{i}) a: α′ ⊢ δ (ϵa) =˚ a. Then the term

j_{α′}^{γ}(ϵ, δ) : Uα′ → T α′ =^{def} λu^{Uα′}.(λs^{γ}.let_{˚ T α′} a ⇐ δ s in η_{α′} a)^{∗} (j_{γ} ((λa^{α′}.ε_{γ} (ϵa))^{+} u))

is a reification function at α′.

Proof. Let α in L_{0} and R ∈ ARel(α, α′) be given, with t ( R) u; we must show that t (T R) j_{α′}^{γ} (ϵ, δ)u. Accordingly, define R_{γ} ∈ ARel(α, γ) by

a R_{γ} s ⟺ ˚ a (˚ R) δ s .

This is clearly admissible, being given as an inverse image of the admissible˚ R. Moreover, from the assumption on ϵ and δ, and the properties of the computation-extension ˚ R (Definition 3.20(1)), we immediately get

∀a R a′. a R_{γ} ϵa′ ,

which, together with the assumption on t and u and the properties of the monad relation (Definition 3.26(1,2)), gives us

t = (λa.η a)^{∗} t ( R_{γ}) (λa′.ε(ϵa′))^{+} u .

From this, we get by the assumption on j_{γ} that

t (T R_{γ}) j_{γ} ((λa′.ε(ϵa′))^{+} u) .

And finally, using all three parts of Lemma 3.25 and the definition of R_{γ},

t = (λa.let_{˚ T α} a ⇐˚ a in η a)^{∗} t
(T R) (λs.let_{˚ T α′} a′ ⇐ δ s in η a′)^{∗} (j_{γ} ((λa′.ε(ϵa′))u^{+} )) = j_{α′}^{γ}(ϵ, δ)u

as required.

For the j from Lemma 3.31 specifically, this works out to:

j_{α′}^{γ}(ϵ, δ) = λu^{K_{o}α′}.(λs^{γ}.let_{˚ T α′} a ⇐ δ s in η a)^{∗} (ψ^{•} (u(λa^{α′}.(ϕ^{•} ∘ η)(ϵa))))

Consider now an L_{0}^{T} -program. Because our type system is monomorphic, every [ ]- operator in that program can be uniquely labeled with a specific type. There is thus only ever a finite set ℵ of L_{0}^{T} -types α such that [[α]]′_{T} needs to be embedded in the γ from Lemma 3.32. (Note that this is a static property of the program, with the set of reification-types bounded linearly by the program size. This in contrast to, say, finite unrollings of fixed points, where we cannot a priori determine how deeply to unroll.)

Thus, we can simulate reification with a finite sum, if we are willing to construct the relevant type γ = Σ_{i}[[ℵ(i)]]′_{T} for each program. In fact, for any finite ℵ covering all reifications in an L_{0}^{T} -program, we get the same overall result when using any larger ℵ for defining γ. We can thus formally define the evaluation semantics of programs resulting from the variant translation to be the unique meaning determined by any “sufficiently large” finite collection ℵ.

Or, we can use a single, infinite embedding type that works for all programs. In particular, we can simply take I to be the countable set of names of closed L_{0}^{T} -types, with ℵ(‘α’) = α. Giving such an enumeration is unproblematic: the set of L_{0}^{T} -types does not itself contain any embedding-types. Also, the tags themselves are inherently unstructured; in particular, for a monadic translation, we have [[in_{‘α’}M]]_{T} = in_{‘α’}[[M]]_{T}, not in_{‘[[α]]_{T} ’}[[M]]_{T}. (In the actual ML implementation, we use an extensible data type for Σℵ, with tags dynamically generated and assigned at each instance of reification.)

Embedding-types alone do not suffice to express a continuation-based simulation of reification, however: there is an independent problem with reifying at L_{0}^{T} -types containing the type constructor . Suppose for simplicity that we only needed reification at a single L_{0}^{T} -type α_{0}, and moreover that we could choose the answer type of the continuation monad freely. Then it would seem natural to simply take γ = [[α_{0}]]′_{T} and o = T γ.

But since the continuation-passing [[ ]]′_{T}-translation is itself defined in terms of the answer type o, this would require us to solve the recursive type equation o = T [[α_{0}]]′_{T} exactly, which is too strong a requirement in general. Instead, we still take γ = [[α_{0}]]′_{T}, but only o ≅ T γ. In fact, the latter need not even be a full isomorphism; an answerembedding suffices.

(Alternatively, we could have broken up the recursion by taking o = T γ and γ ≅ [[α_{0}]]′_{T}. This approach gives a slightly simpler abstract correspondence between monads and continuation-passing, but does not allow us to express the construction in the next section in full generality.)

3.3.5 Factorizing the variant translation

Although it translates from L^{T} to L, the [[ ]]′_{T}-translation using U-effects is actually much more like the standard U-monadic translation [[ ]]_{U} from L^{U} to L, sharing the type translation and most of the term translation clauses with the latter. The only nonstandard clauses are for reflection and reification of T -effects. And in fact, we can express the [[ ]]′_{T}-translation entirely in terms of the [[ ]]_{U} -translation by expanding T -reflection and reification into L^{U}-definable terms.

In practice, this means that if we have a good (efficient, convenient, etc.) way of implementing evaluators for L_{U} (whether using the definitional translation for U or some other technique, as long as it gives correct results for complete programs), we can obtain an evaluator for L_{T} by simply viewing T -reflection and -reification as definitional extensions of L^{U}.

When i and j are definable in L_{0} (and hence invariant under the translations), this is immediate: we can simply take μ^{T}(M) = μ^{U}(iM) and [M]^{T} = j[M]^{U}. When U is a continuation monad with a recursively-defined answer type, however, it will be more convenient to work with a formulation of U-effects that integrates the recursion isomorphisms in the continuation-passing translation.

First, since for any monad-triples T and U, the sets of types of L^{T} and L^{U} are actually the same (given by the type constructors of L together with ), we use the name L^{⋆}-type for a type from the extended signature, independent of the actual monad (which only affects the types of μ( ) and [ ]). We can then define a suitable notion of “native” effects for an L^{⋆}-continuation monad:

Definition 3.33 Let L be a cll signature. Then for any closed L^{⋆}-type ω, the signature L^{K_{˚ ω}} extends L with a new computation-type constructor α, the associated value-inclusion and two lets (with types as in Definition 2.21), and the following two term constructors:

\frac{Γ ⊢ M : (α →˚ ω) →˚ ω}{Γ ⊢ μ^{K}(M) : α}  \frac{Γ ⊢ M : α}{Γ ⊢ [M]^{K} : (α →˚ ω) →˚ ω}

Note that this strictly generalizes our previous definition of a monad-extended signature, because in the case where ω is actually a type of L (i.e., does not contain any ), the above is exactly what we get by taking the monad T in Definition 2.21 to be K_{˚ ω}.

Unlike the case for a standard monad-extension of a signature, however, we will not always be able to translate L^{K_{˚ ω}} back into L, because the corresponding monad now involves a recursive type definition. But when the target syntax includes μ-types, we can give such a translation:

Definition 3.34 The translation [[ ]]_{K} : L^{K_{˚ ω}} → L^{μ} is defined as follows: first take

ω̂ = μa. [[ω]]_{K_{˚ a}}  and  o = ˚ ω̂

(where the type translation [[ ]]_{K_{˚ a}} expands α into ([[α]]_{K_{˚ a}} →˚ a)→˚ a and preserves all type constructors of L). We abbreviate the associated isomorphisms as:

ϕ =^{def} roll_{a.[[ω]]_{K_{˚ a}}} : [[ω]]_{Ko} →^{∼} ω̂  and  ψ =^{def} unroll_{a.[[ω]]_{K_{˚ a}}} : ω̂ →^{∼} [[ω]]_{Ko}

(where we write φ : α →^{∼} α′ to summarize the typing rule of a term constructor φ building α′-terms from α-terms).

Then the type and term translation is the standard monadic translation for the monad K_{o}, except with the clauses for reflection and reification reading:

[[μ^{K}(M)]]_{K} = λk.let˚ r ⇐ [[M]]_{K} (λa.let˚ o ⇐ k a in˚ (ψ o)) in˚ (ϕr)
[[[M]^{K}]]_{K} = λk.let˚ o ⇐ [[M]]_{K} (λa.let˚ r ⇐ k a in˚ (ϕr)) in˚ (ψ o)

Note that when the isomorphisms are identities, as we can always trivially ensure when ω is only an L-type, this reduces to the original definition of a monadic translation (Definition 2.23) because of the law let˚ x ⇐ M in˚ x = M.

The usual direct-style reasoning principles for reflection and reification from Definition 2.27 still hold for the more general notion of monadic translation. Specifically:

Lemma 3.35 In addition to the equations for let and inclusion from Definition 2.27, the following equations are sound for the [[ ]]_{K}-translation from Definition 3.34:

μ^{K}([M]^{K}) = M
[μ^{K}(M)]^{K} = λk.M k = M
[ M]^{K} = λk.k M
[let x ⇐ M_{1} in M_{2}]^{K} = λk.[M_{1}]^{K} (λx.[M_{2}]^{K} k)
[let˚ x ⇐ M_{1} in M_{2}]^{K} = λk.let˚ x ⇐ M_{1} in [M_{2}]^{K} k

(where k does not occur free in any of the Ms).

Proof. Simple calculation; we mainly have to verify that the isomorphisms cancel out. For example, for the third equation:

[[[ M]^{K}]]_{K} = λk.let˚ o ⇐ [[ M]]_{K} (λa.let˚ r ⇐ k a in˚ (ϕr)) in˚ (ψ o) = λk.let˚ o ⇐ (λk′.k′ [[M]]_{K})(λa.let˚ r ⇐ k a in˚ (ϕr)) in˚ (ψ o) = λk.let˚ o ⇐ (let˚ r ⇐ k [[M]]_{K} in˚ (ϕr)) in˚ (ψ o) = λk.let˚ r ⇐ k [[M]]_{K} in let˚ o ⇐˚ (ϕr) in˚ (ψ o) = λk.let˚ r ⇐ k [[M]]_{K} in˚ (ψ (ϕr)) = λk.let˚ r ⇐ k [[M]]_{K} in˚ r = λk.k [[M]]_{K} = [[λk.k M]]_{K}

The others are similar.

Note again the similarity of the direct-style equations characterizing [ ] to those of an explicit continuation-passing translation. The operational intuition is that μ^{K}(M) passes to M a functional representation of the current evaluation context, i.e., the continuation waiting for the result of μ^{K}(M). Conversely, [M]^{K} evaluates M with a given continuation and returns the answer. For example, taking ω = ι, we have

[let x ⇐ μ^{K}(λk.let˚ r ⇐ k \underline{3} in k r) in (s x)]^{K} (λa.˚ a)

= [μ^{K}(λk.let˚ r ⇐ k \underline{3} in k r)]^{K} (λx.[ (s x)]^{K} (λa.˚ a))
= (λk.let˚ r ⇐ k \underline{3} in k r)(λx.(λa.˚ a)(s x)) = (λk.let˚ r ⇐ k \underline{3} in k r)(λx.˚ (s x))
= let˚ r ⇐ (λx.˚ (s x))\underline{3} in (λx.˚ (s x))r = let˚ r ⇐˚ (s \underline{3}) in (λx.˚ (s x))r
= (λx.˚ (s x))\underline{4} =˚ \underline{5}

That is, k gets bound to the function λx.˚ (s x) and applied twice to \underline{3}.

Remark 3.36 The circularity inherent in allowing ω to be an L^{⋆}-type is genuine: even without fix in the language, it is possible to write non-terminating programs in L^{K_{˚ ω}}. For perhaps the simplest example, take ω = 1. Then reflection and reification (as shown above, they are still two-sided inverses) give us an isomorphism

ω = 1 ≅ (1 →˚ ω) →˚ ω ≅˚ ω →˚ ω

And indeed, we can define a diverging term Ω_{˚ ω} by the usual double self-application made type-correct by the isomorphisms:

d : ω →˚ ω = λx^{ω}.[x](λ⟨⟩.˚ x)
d′ : ω = μ(λk^{1→˚ ω}.let˚ x ⇐ k ⟨⟩ in dx)
Ω :˚ ω = dd′

It turns out that picking a fixed shape for the answer type (i.e., requiring it to be of the form˚ ω) necessitates a slight twist when simulating monads whose type constructors do not contain an outermost˚ ; this is why we allowed the answer-embedding in Definition 3.30 to be a retraction, rather than a full isomorphism. First, we slightly transform the monad to be simulated:

Definition 3.37 Let T = (T, η, ^{∗}) be a monad-triple. We then define a new monadtriple T̂ = (T̂,η̂, ˆ∗) as follows:

T̂α = ˚ (T α)
η̂ = λa.˚ (η a)
f ˆ∗ = λm.let˚ t ⇐ m in˚ ((λa.let_{˚ T α_{2}} r ⇐ f a in r)^{∗} t)

For example, for state (Example 2.18), this gives

T̂α = ˚ (σ →˚ (α × σ))
η̂ = λa.˚ (λs.˚ ⟨a, s⟩)
f^{∗} = λm.let˚ t ⇐ m in˚ (λs.let˚ ⟨a, s′⟩ ⇐ ts in let˚ r ⇐ f a in r s)

Now T̂α does have an outermost˚ . On the other hand, T̂ is not in general a monad, even if T was. In particular, for law 2.15(1) we only get

f ˆ∗ (η̂a) = let˚ t ⇐˚ (η a) in˚ ((λa.let_{˚ T α_{2}} r ⇐ f a in r)^{∗} t)

=˚ ((λa.let_{˚ T α_{2}} r ⇐ f a in r)^{∗} (η a)) =˚ (let_{˚ T α_{2}} r ⇐ f a in r) =^{?} f a

T̂ is, however, a monad “up to extensionality”: when evaluated and applied to a value, f a and f ˆ∗ (η̂a) do behave identically:

let˚ r ⇐ f ˆ∗ (η̂a) in r s = let˚ r ⇐˚ (let_{˚ T α_{2}} r ⇐ f a in r) in r s
= (let_{˚ T α_{2}} r ⇐ f a in r)s = let˚ r ⇐ f a in r s

And in fact, our construction will ensure that functions like f are always “fully applied”, so that we can use η̂ and ˆ∗ instead of η and ^{∗}.

We can now define reflection and reification for T in terms of the corresponding operators for continuations as follows:

Theorem 3.38 Let T be a monad in (L_{0}, L_{s}), and let M be an L_{0}^{T} -program without top-level focus effects, (i.e., · ⊢ M :˚ ι). Further, let ℵ be a family of types containing at least all L_{0}^{T} -types for which M contains a reification-operator. Take ω = T (Σℵ), a well-formed (L_{0}^{Σ})^{⋆}-type, and in (L_{0}^{Σ})^{K_{˚ ω}} define the term constructors μ^{T}( ) and [ ]^{T} by:

μ^{T}(M) = μ^{K}(λk.kˆ∗ (˚ M))
[M]^{T} = let_{˚ T α} t ⇐ (λr.let˚ a ⇐ out_{i} r in η̂a)ˆ∗ ([M]^{K} (λa.η̂(in_{i} a))) in t (ℵ(i)=α)

Now take L_{0}^{+} = (L_{0}^{Σ})^{μ}, with L_{i} a model of (E_{0}^{Σ})^{μ} from Section 3.2.1. Then replacing all T -reflection and -reification operators in M with the definitions above (picking i for each reification arbitrarily, subject to the constraint), yields an (L_{0}^{Σ})^{K_{˚ ω}}-program M′ such that [[M]]_{T} (˚ ι^{r}) [[M′]]_{K}.

Proof. Let ω̂ ≅ [[T (Σℵ)]]_{K} with associated isomorphisms be as in Definition 3.34, and take γ = Σ_{i}[[ℵ(i)]]_{K}; then [[ω]]_{K} = [[T (Σℵ)]]_{K} = T [[Σℵ]]_{K} = T (Σ[[ℵ]]_{K}) = T γ. It is also easy to see that the functions defined by

ϕ^{•} : T γ → ˚ ω̂ = λr.˚ (ϕr)
ψ^{•} : ˚ ω̂ → T γ = λm.let_{˚ T γ} o ⇐ m in ψ o

form an answer-embedding in the sense of Definition 3.30.

Now, using the i from Lemma 3.31 directly, we get (omitting a few tedious letsimplification steps):

[[μ^{T}(M)]]_{K} = [[μ^{K}(λk.kˆ∗ (˚ M))]]_{K} = [[μ^{K}(λk.˚ ((λa.let_{˚ ω} r ⇐ k a in r)^{∗} M))]]_{K} = λk.let˚ r ⇐ [[λk.˚ ((λa.let_{˚ ω} r ⇐ k a in r)^{∗} M)]]_{K} (λa.let˚ o ⇐ k a in˚ (ψ o)) in˚ (ϕr) = λk.let˚ r ⇐˚ ((λa.let_{˚ T γ} o ⇐ k a in ψ o)^{∗} [[M]]_{K}) in˚ (ϕr) = λk.˚ (ϕ((λa.let_{˚ T γ} o ⇐ k a in ψ o)^{∗} [[M]]_{K})) = λk.ϕ^{•} ((λa.ψ^{•} (k a))^{∗} [[M]]_{K})

= i_{[[α]]_{K}} [[M]]_{K} = [[μ(M)]]′_{T}

Similarly, using j from 3.31 as extended by Lemma 3.32 (with ϵ = in_{i} and δ = out_{i}, satisfying the retraction condition by definition), we get:

[[[M]^{T}]]_{K} = [[let_{˚ T α} t ⇐ (λr.let˚ a ⇐ out_{i} r in η̂a)ˆ∗ ([M]^{K} (λa.η̂(in_{i} a))) in t]]_{K} = let_{˚ T [[α]]_{K}} t ⇐ [[[M]^{K}]]_{K} (λa.˚ (η (in_{i} a))) in (λr.let_{˚ T [[α]]_{K}} a ⇐ out_{i} r in η a)^{∗} t = let_{˚ T [[α]]_{K}} o ⇐ [[M]]_{K} (λa.˚ (ϕ(η (in_{i} a)))) in (λr.let_{˚ T [[α]]_{K}} a ⇐ out_{i} r in η a)^{∗} (ψ o) = (λr.let_{˚ T [[α]]_{K}} a ⇐ out_{i} r in η a)^{∗} (let_{˚ T γ} o ⇐ [[M]]_{K} (λa.ϕ^{•} (η (in_{i} a))) in ψ o) = (λr.let_{˚ T [[α]]_{K}} a ⇐ out_{i} r in η a)^{∗} (ψ^{•} ([[M]]_{K} (λa.(ϕ^{•} ∘ η)(in_{i} a))))

= j_{[[α]]_{K}}^{γ} (in_{i}, out_{i})[[M]]_{K} = [[[M]]]′_{T}

We can thus apply Proposition 3.29 (with empty σ and σ′) to get the result.

Remark 3.39 When the monad T is already of the form T α = ˚ (T′α) for some type constructor T′ (e.g., for exceptions, T′α = α + χ), a slightly simpler construction is possible. Take ω = T′(Σℵ) so that o = ˚ ω̂ ≅˚ (T′γ) = T γ, ϕ^{•} t = let˚ s ⇐ t in˚ (ϕs), and ψ^{•} m = let˚ r ⇐ m in˚ (ψ r). (Here ϕ^{•} and ψ^{•} are actually two-sided inverses.) Then we get the analog of Theorem 3.38 by defining reflection and reification as follows:

μ^{T}(M) = μ^{K}(λk.k^{∗} M)
[M]^{T} = (λr.let˚ a ⇐ out_{i} r in η a)^{∗} ([M]^{K} (λa.η (in_{i} a))) (ℵ(i)=α)

The actual ML code in Section 4.5 takes advantage of this optimization by not including an explicit suspension in the definition of monads like T, but instead having it implicitly inserted by the CBV elaboration from Section 2.1.6. In other words, the type constructor T in the ML signature of such a monad actually corresponds to the T′ above, so that, e.g., ((σ_{1} → Tσ_{2}))^{v} = ((σ_{1}))^{v} →˚ ((Tσ_{2}))^{v} = ((σ_{1}))^{v} → T ((σ_{2}))^{v}. We could, however, simply use Theorem 3.38 directly in all cases.

We have thus reduced the problem of implementing a language with monadic effects for an arbitrary definable monad T to that of implementing a language with reflection and reification operators for a continuation monad with an answer type˚ ω for some valuetype ω. In the next chapter, we will show how this can itself be achieved by embedding the continuation-effect language into a Scheme-like one.

3.3.6 Induced relational correspondence

We finally show how the relational correspondence between L_{s} and L_{i} can be generalized to the case where the base language is itself given by a monadic translation. That is, we consider the language where we take as the distinguished computation-type constructor, while˚ and its related operations become simply additional type and term constructors. Corollary 2.29 showed that this new language is also a model of E_{0}; the following shows that this equational characterization extends to a relational one:

Proposition 3.40 Let there be given a monad relation between T in L_{s} and U in L_{i} (Definition 3.26). Then the relation assignment determined by the monad relation is a computation-extension in the sense of Definition 3.20 for -computations in the languages (L_{0}^{T} , L_{s}^{T}) and (L_{0}^{+}^{U}, L_{i}^{U}), as given by Definition 2.24.

Proof. For the first condition, let R ∈ ARel_{L_{s}^{T} ,L_{i}^{U}} (α, α′) be a relation, and let a R a′; we must show that a ( R) a′, i.e., that in the original correspondence,

[[ a]]_{T} = η a ( R) εa = [[ a]]_{U}

and that follows immediately from Definition 3.26(1).

Similarly, assume that ∀a R_{1} a′. f a ( R_{2}) f′ a′ and m ( R_{1}) m′. We must show that

let x ⇐ m in f x ( R_{2}) let x′ ⇐ m′ in f′ x′

in the new correspondence, i.e., that

[[let x ⇐ m in f x]]_{T} = (λx.f x)^{∗} m = f^{∗} m
( R_{2}) f^{′^{+}} m′ = (λx′.f′ x′)^{+} m′ = [[let x′ ⇐ m′ in f′ x′]]_{U}

in the original one, which is precisely the statement of 3.26(2).

3.4 Related work

The study of relationships between direct and continuation semantics has a long history. Early investigations [Rey74a, ST80, Sto81] were set in a domain-theoretic framework where the main difficulties concerned reflexive domains; as a result, these methods and results were closely tied to specific semantic models. On the other hand, Meyer and Wand’s more abstract approach [MW85] applied to all models of simply-typed λ-calculi, but did not encompass computational effects – not even nontermination.

The present work, while formulated in a simply-typed setting, and using mostly axiomatic reasoning, is nevertheless closer conceptually to the domain-theoretic results. In particular, it explicitly handles general recursion in computations by fixed-point induction, and should extend to recursively-defined types without too many obstacles. (The initial version in [Fil94] was based on the Meyer-Wand approach, but it is not clear how well that would scale to ambient effects and especially recursion.)

A possible correspondence between monads and continuation-passing style (CPS) was conjectured by Danvy and Filinski [DF90], and more concretely presented by Wadler [Wad92b]. (The general idea of using a monad morphism to simulate one monadic effect with another is also due to Wadler [Wad90].) However, this work was largely informal. Most notably, the problems with reification (needed, e.g., to express handle for continuation-based exceptions) in a typed setting were not addressed at all.

Peyton Jones and Wadler [PW93] probe the relationship between monads and CPS further, and Wadler [Wad94] analyzes composable continuations from a monadic perspective, but in both cases the restriction to Hindley-Milner typable translations obscures the general correspondence; properly expressing the answer-type parametricity in a simulation of general monads by continuation-passing requires a more flexible type system.

Finally, another glimmer of the connection between monads and continuations can be seen in Sabry and Felleisen’s result that βη-equivalence of CPS terms coincides with direct-style equivalence in Moggi’s computational λ-calculus [SF93, Mog89]; the latter captures exactly the equivalences holding in the presence of arbitrary monadic effects. While this does not by itself imply that any monadic effect can be simulated by a continuation monad, it does indicate that continuations form a maximally (but not necessarily most) general notion of effect.

Implementing Continuation-Effects

In this chapter, we continue the simulation of effects by showing that a language with reflection and reification operators for a continuation monad can itself be embedded in a language with a more traditional set of effects: Scheme-style first-class continuations and typed state.

That is, in the previous chapter we showed that continuations are in a precise sense a universal effect: any definable monad can be simulated by a continuation monad with a suitable answer type. Now we show that this universal effect can itself be expressed in terms of two specific, low-level effects. Thus, we can program directly with monadic effects in a language such as Scheme, or ML with continuations.

The development consists of three major steps. First, we re-express reflection and reification for continuations in terms of an alternative, more operationally motivated pair of control operators. These implement a control abstraction known as composable continuations. We further decompose the composable-continuations operators into a standard escape-operator, an abort-operator, and a control delimiter.

Then we show that the level-tags (˚ and on value-inclusions and lets) introduced by the monadic translation are actually unnecessary for evaluation-purposes: the levelerasure of a program evaluates to the same result as the original one. The proof involves another set of logical relations, indexed by types of the original two-level language, and relating original and level-erased terms at each type.

Finally, in the level-erased language, we define the control operators in terms of Scheme-like primitives. The key step here is to re-express the sequencing of already continuation-passing terms in meta-continuation-passing style, then observe that the metacontinuation is used in a single-threaded way throughout the translation and can hence be maintained in a fixed cell of the store. Again, a simple logical-relations argument shows the equivalence of the original definitions of the control operators to their escape-state simulation.

We conclude the chapter with an actual implementation of the construction in Standard ML of New Jersey, which includes language support for first-class continuations. In addition to the complete code implementing monadic reflection and reification in terms of escapes and state, we show a few simple programming examples. In particular, we illustrate how definable monadic effects, such as nondeterministic or probabilistic computations, fit very naturally into a traditional call-by-value setting.

4.1. CONTINUATION-REFLECTION AND COMPOSABLE CONTINUATIONS 81

4.1 Continuation-reflection and composable continuations

For convenience, we will assume that all signatures in the following contain an empty type 0 with no value constructors. Our implementation signature L_{0}^{+} = L_{0}^{Σ,μ} (i.e., L_{0} with recursive types and embedding-types) certainly does, for example defining 0 as μa. a or as an embedding-type with empty index set. We need not require that 0 has a counterpart in the specification language L_{0} as well, although adding it there would be unproblematic.

Further, for any computation-type β there is a (unique) function from 0 to β, expressible as, e.g., λz. ⊥_{β}, which we write as V_{β}. In fact, 0 and V are simply the zero-ary analogs of sums and case (except that we do not require a V_{α} for arbitrary α); in particular, for any monadic translation we have [[0]]_{T} = 0 and [[V_{β} M]]_{T} = V_{[[β]]_{T}} [[M]]_{T}.

Unlike Chapter 3, where the simulation results were parameterized by the fairly complex notion of a definable monad (which included a type constructor and type-indexed families of term constructors), all the constructions in this chapter are parameterized by a fixed, closed value-type ω. Accordingly, except within definitions of recursive types with μ, we only need to consider type-closed types and terms.

The continuation-passing translation allows us to define a wide range of control operators in the source language. We have already seen reflection and reification, but many others are possible. In particular, we have:

Definition 4.1 Let L be a cll signature with a 0-type, and let ω be a type of L^{⋆}. Then in L^{K_{˚ ω}} (Definition 3.33), we define the operations

\frac{Γ ⊢ M : (α → 0) → 0}{Γ ⊢ C M : α}  \frac{Γ ⊢ M : ω}{Γ ⊢ AM : 0}  \frac{Γ ⊢ M : ω}{Γ ⊢ #M :˚ ω}  \frac{Γ ⊢ M : (α →˚ ω) → ω}{Γ ⊢ S M : α}

by the expansions:

C M = μ(λk^{α→˚ ω}.[M (λa^{α}.μ(λq^{′0→˚ ω}.k a))](λz^{0}.V_{˚ ω}z))
AM = μ(λq^{0→˚ ω}.˚ M)
#M = [M](λr^{ω}.˚ r)
S M = μ(λk^{α→˚ ω}.[M k](λr^{ω}.˚ r))

The operational intuition is as follows: C M (escape) invokes M with a representation of the current evaluation context as a procedure q : α → 0 that, when applied to a value a : α, will abandon the then current context of evaluation and return a as the result of C M, e.g.,

C (λq.let z ⇐ q \underline{3} in ⋯) = \underline{3}

(regardless of what happens in ⋯). The 0-“returning” q can invoked in a context expecting an α′-typed result by writing let z ⇐ q a in V_{α′} z. C thus acts very much like Scheme’s call/cc, except that the M must explicitly invoke q in order to return a value from C M. Each variant can be used to define the other, however.

AM (abort) immediately terminates the current computation, returning M as the answer. Like a q supplied by C, it can be used in combination with V to break out of any computation-typed context.

#M (reset or prompt) evaluates M in an empty evaluation context and returns the final answer of that evaluation, thus delimiting any control effects M might have. For example, when ω = ι, we get

#(let z ⇐ A\underline{5} in \underline{7}) =˚ \underline{5}

Finally, S M (shift) captures and erases the current evaluation context up to (but not including) the innermost enclosing #, passing this context to M as a composable function. For example, still with ω = ι, we have

#(let x ⇐ S (λk.let˚ r_{1} ⇐ k \underline{4} in let˚ r_{2} ⇐ k r_{1} in r_{2}) in (s x)) =˚ \underline{6}

Note also that we could define AM = S (λk^{0→˚ ω}. M)

Although probably not as well known as call/cc, control operators like S, A, and # have already seen a fair amount of study, e.g., [Fel88, SF90, DF92, Wad94, Fil94, GRR95]; we will briefly compare the various approaches in Section 4.6.

For reference, and since we will need it later (in Definition 4.5), let us note:

Lemma 4.2 The translations of the derived control operators using Definition 3.34 work out to:

[[C M]]_{K} = λk.[[M]]_{K} (λa.λq.k a)(λz.V z)
[[AM]]_{K} = λq.˚ (ϕ[[M]]_{K})
[[#M]]_{K} = let˚ o ⇐ [[M]]_{K} (λr.˚ (ϕr)) in˚ (ψ o)
[[S M]]_{K} = λk.[[M]]_{K} (λa.let˚ o ⇐ k a in˚ (ψ o))(λr.˚ (ϕr))

Proof. Straightforward. For example,

[[AM]]_{K} = [[μ^{K}(λq.˚ M)]]_{K} = λk.let˚ r ⇐ [[λq.˚ M]]_{K} (λa.let˚ o ⇐ k a in˚ (ψ o)) in˚ (ϕr)

= λk.let˚ r ⇐ (λq.˚ [[M]]_{K})(λa.let˚ o ⇐ k a in˚ (ψ o)) in˚ (ϕr)
= λk.let˚ r ⇐˚ [[M]]_{K} in˚ (ϕr) = λk.˚ (ϕ[[M]]_{K})

The others are similar.

The reason why we can concentrate on the composable-continuations operators instead of the seemingly more general μ^{K}( ) and [ ]^{K} is the following property:

Lemma 4.3 Shift and reset form a complete set of control operators, in the sense that we can use them to express reflection and reification as follows:

μ^{K}(M) = S (λk^{α→˚ ω}.let˚ r ⇐ M k in r)
[M]^{K} = λk^{α→˚ ω}.#(let a ⇐ M in let˚ r ⇐ k a in r)

and get equivalent translations under [[ ]]_{K}.

Proof. The actual translations of the terms contain explicit isomorphisms, which clutter up the equational proofs. It is thus more convenient to use the standard directstyle reasoning principles for reflection and reification, whose soundness with respect to the [[ ]]_{K}-translation was established in Lemma 3.35:

S (λk.let˚ r ⇐ M k in r) = μ(λk.[(λk.let˚ r ⇐ M k in r)k](λr.˚ r))

= μ(λk.[let˚ r ⇐ M k in r](λr.˚ r)) = μ(λk.let˚ r ⇐ M k in [ r](λr.˚ r))

= μ(λk.let˚ r ⇐ M k in (λk.k r)(λr.˚ r)) = μ(λk.let˚ r ⇐ M k in˚ r) = μ(λk.M k) = μ(M)

λk.#(let a ⇐ M in let˚ r ⇐ k a in r) = λk.[let a ⇐ M in let˚ r ⇐ k a in r](λr.˚ r) = λk.[M](λa.[let˚ r ⇐ k a in r](λr.˚ r)) = λk.[M](λa.let˚ r ⇐ k a in [ r](λr.˚ r)) = λk.[M](λa.let˚ r ⇐ k a in (λr.˚ r)r) = λk.[M](λa.let˚ r ⇐ k a in˚ r) = λk.[M](λa.k a) = λk.[M]k = [M]

That is, reflection essentially captures the current continuation and passes it to M, while reification evaluates M in a delimited control context containing only k. This simple reading is somewhat obscured by the explicit lets and value-inclusions used to coerce between the two kinds of computation. However, when we eliminate the operational distinction between˚ and in the next section, the two mixed-level lets (binding r) can actually be replaced with just M k and k a.

Somewhat surprisingly, the reflect-like S can itself be decomposed into a standard escape-operator and two simpler constructs:

Lemma 4.4 S is definable in terms of C, #, and A by

S M = C (λc^{α→ 0}.let r ⇐ M (λa^{α}.#(let z ⇐ ca in V_{ω}z)) in Ar)

Proof. As in Lemma 4.3, we can use direct-style reasoning for the actual verification:

C (λc.let r ⇐ M (λa.#(let z ⇐ ca in V z)) in Ar)

= μ(λk.[(λc.let r ⇐ M (λa.#(let z ⇐ ca in V z)) in Ar)(λa.μ(λq.k a))](λz.V z))

= μ(λk.[let r ⇐ M (λa.#(let z ⇐ (λa.μ(λq.k a))a in V z)) in Ar](λz.V z))
= μ(λk.[M (λa.#(let z ⇐ μ(λq.k a) in V z))](λr.[Ar](λz.V z)))
= μ(λk.[M (λa.[let z ⇐ μ(λq.k a) in V z](λx.˚ x))](λr.[μ(λq.˚ r)](λz.V z)))
= μ(λk.[M (λa.[μ(λq.k a)](λz.[V z](λx.˚ x)))](λr.(λq.˚ r)(λz.V z)))
= μ(λk.[M (λa.(λq.k a)(λz.[V z](λx.˚ x)))](λr.˚ r)) = μ(λk.[M (λa.k a)](λr.˚ r))
= μ(λk.[M k](λr.˚ r)) = S M

Here, we wrap the escaping continuation c provided by C in a control delimiter, making it into a composable function that can be passed to M. Since S also needs to erase the continuation after capturing it, we explicitly abort with the result r returned by M.

Because C, A and # were themselves defined in terms of μ^{K}( ) and [ ]^{K}, in principle it does not matter which set we use in the following. Pragmatically, however, (C, A, #) have the advantage that their types contain no negative occurrences of˚ , which slightly simplifies the arguments in Section 4.2. We therefore now switch attention to the new set:

Definition 4.5 Let L be a cll signature with a 0-type, and let ω be a type of L^{⋆}. The signature L^{K_{˚ ω}^{cc}} extends L with a computation-type constructor , associated value-inclusion and lets, and the term constructors C, A, and #, typed as in Definition 4.1. There is an evident translation from L^{K_{˚ ω}} to L^{K_{˚ ω}^{cc}}, given by the statements of Lemmas 4.3 and 4.4.

The definitional translation [[ ]]_{K} from L^{K_{˚ ω}^{cc}} to L^{μ} is identical to the one for L^{K_{˚ ω}} from Definition 3.34, except that the clauses for μ^{K}( ) and [ ]^{K} are replaced with the clauses for C, A, and # from Lemma 4.2.

Note that, since we consider complete programs to be terms of type˚ ι, the type system ensures that all control effects in an L^{K_{˚ ω}^{cc}}-program occur within the dynamic scope of some #.

4.2 Level-erasure

Between Chapter 3 and Section 4.1, we have now reduced monadic effects to shift/reset and further to escapes, abort, and reset; all dependencies on the original monadic translation are gone from the translation equations, with the monad simulation being performed entirely by expanding T -reflection and -reification into simple control operators and components of the monad-triple.

However, our effect-enriched language L_{0}^{T} still has a significant practical limitation: we need to explicitly indicate the levels on all value-inclusions and lets. From a specification perspective, this is reasonable; with general reflection and reification available, we must distinguish properly between˚ - and -computations in order to even define the monadic translation.

Moreover, most programs can actually be written in terms of -computations alone, with uses of˚ restricted to the definitions of monad-specific effects from reflection and reification, such as the raise and handle in Example 2.26. Thus, for particular computational effects, we may not need to explicitly expose˚ -computations to the language as a whole.

On the other hand, if we are to provide reflection and reification for arbitrary, programmer-defined monads, we do need general˚ -computations to be directly expressible in the language. In languages such as Effect-PCF (i.e., our L_{0}), where computationsequencing is already explicit, adding level-annotations to all inclusions and lets may not be too problematic. But in an ML-like language, implicitly elaborated into Effect-PCF as in Section 2.1.6, there is no room for significant effect-annotations of source terms. And fortunately, as far as program evaluation is concerned, the levels can actually be safely elided.

The idea is to view ˚ α as a subtype of α, rather than as an entirely separate type. Membership in ˚ α then becomes a semantic property on values, with the type system guaranteeing absence of effects in certain terms, but not playing an active role in the actual evaluation process. Accordingly, we now define a new language with a unified notion of control-effects:

Definition 4.6 Take L_{1} to be L_{0}^{Σ} extended with an empty type 0 and associated V. We further define the set of L¯⋆ ; 1-types to be the same as that of L_{1}, but with the type constructor ˚ replaced by a new constructor˚ .

When ω is an L¯⋆ ; 1-type, the signature L_{1}^{CC_{ω}} of the composable-continuations language then consists of L_{1} with all instances of˚ replaced by˚ (so in particular, for computations we now have:

\frac{Γ ⊢ M : α}{Γ ⊢˚ M :˚ α}  \frac{Γ ⊢ M_{1} :˚ α_{1}  Γ, x: α_{1} ⊢ M_{2} :˚ α_{2}}{Γ ⊢ let˚ x ⇐ M_{1} in M_{2} :˚ α_{2}}

but no˚ -computations), together with the following additional term constructors:

\frac{Γ ⊢ M : (α →˚ 0) →˚ 0}{Γ ⊢ C M :˚ α}  \frac{Γ ⊢ M : ω}{Γ ⊢ AM :˚ 0}  \frac{Γ ⊢ M :˚ ω}{Γ ⊢ #M :˚ ω}

We can define a translation from our two-level control-effect language into the unified one by simply dropping the distinctions between the levels:

Definition 4.7 The level-erasure translation | | is defined as follows. First, for any L_{1}^{⋆}-type α, |α| is the L¯⋆ ; 1-type obtained by replacing all occurrences of˚ and in α with˚ :

|˚ α| = | α| =˚ |α|

with the other type constructors unaffected. For terms, level-erasure likewise conflates all uses of˚ and into˚ (e.g., |˚ M| = | M| =˚ |M|), and maps the constructors C, A, and # to their counterparts from Definition 4.6.

It is easy to see that if Γ ⊢ M : α in L_{1}^{K_{˚ ω}^{cc}} then |Γ| ⊢ |M| : |α| in L_{1}^{CC_{|ω|}}.

We can also give a definitional translation of the one-level language:

Definition 4.8 Let ω be an L¯⋆ ; 1-type. We then define the continuation-passing translation [[ ]]_{\overline{K}} from L_{1}^{CC_{ω}} to L_{1}^{μ} as follows. First, let the auxiliary [[ ]]_{\overline{K} ; ˚ a} on types be the syntactic expansion of˚ α into ([[α]]_{\overline{K} ; ˚ a} →˚ a) →˚ a, and take

ω̄ = μa. [[ω]]_{\overline{K} ; ˚ a} .

We also write

ϕ̄ : [[ω]]_{\overline{K} ; ˚ω̄} →^{∼} ω̄ = roll_{a.[[ω]]_{\overline{K} ; ˚ a}}  and  ψ̄ : ω̄ →^{∼} [[ω]]_{\overline{K} ; ˚ω̄} = unroll_{a.[[ω]]_{\overline{K} ; ˚ a}}

for the associated isomorphisms. The translation of types is then given by:

[[˚ α]]_{\overline{K}} = ([[α]]_{\overline{K}} → ˚ ω̄) → ˚ ω̄

(with the other type constructors not affected). Correspondingly, the non-identity clauses of the term translation are:

[[˚ M]]_{\overline{K}} = λk.k [[M]]_{\overline{K}}
[[let˚ x ⇐ M_{1} in M_{2}]]_{\overline{K}} = λk.[[M_{1}]]_{\overline{K}} (λx.[[M_{2}]]_{\overline{K}} k)
[[C M]]_{\overline{K}} = λk.[[M]]_{\overline{K}} (λa.λq.k a)(λz.V z)
[[AM]]_{\overline{K}} = λq.˚ (ϕ̄[[M]]_{\overline{K}})
[[#M]]_{\overline{K}} = λk.let˚ o ⇐ [[M]]_{\overline{K}} (λr.˚ (ϕ̄r)) in k (ψ̄o)

As usual, this translation is easily seen to be type-preserving: if Γ ⊢ M : α in L_{1}^{CC_{ω}} then [[Γ]]_{\overline{K}} ⊢ [[M]]_{\overline{K}} : [[α]]_{\overline{K}} in L_{1}^{μ}.

We can now state our goal concretely: we want to show that the original [[ ]]_{K}- translation from Definition 4.5 is equivalent for evaluation purposes to a level-erasure followed by the [[ ]]_{\overline{K}}-translation. To establish this equivalence, we will define a collection of relations between the types arising from the two translations. The key ingredient here is a suitable relational action of the type constructor˚ in the two translations:

Definition 4.9 Let there be given a relational correspondence between two interpretations of L_{1}^{μ}, with a computation-extension of relations R ↦˚ R (Definition 3.20). Then for any relation R ∈ ARel(α, α′), we define the relation ¯˚ R ∈ CARel(˚ α, (α′ → ˚ ω̄) → ˚ ω̄) by:

m (¯˚ R) u
⟺ ∀α_{0} type_{L_{1}^{μ}}, O ∈ ARel(α_{0}, ω̄). λk.let˚ x ⇐ m in k x ((R →˚ O) →˚ O) u
⟺ ∀α_{0} type_{L_{1}^{μ}}, O ∈ ARel(α_{0}, ω̄), k : α →˚ α_{0}, k′ : α′ → ˚ ω̄. (∀a R a′. k a (˚ O) k′ a′) ⇒ let˚ a ⇐ m in k a (˚ O) uk′

(Note that this is essentially the monad relation from Lemma 3.31, with T taken as the identity monad, γ = ω̄, o = ˚ ω̄, and ϕ^{•} = ψ^{•} = id. That is, we are using a continuation monad to simulate a trivial notion of focus effects.)

We can now define our system of relations:

Definition 4.10 Let ω be an L_{1}^{⋆}-type, take ω̂ ≅ [[ω]]_{K} (Definition 3.34) and ω̄ ≅ [[|ω|]]_{\overline{K}} (Definition 4.8), and let

⪰ ∈ ARel(ω̂, ω̄)

be an admissible relation on final answers; for the moment we leave its definition unspecified. For any type α of L_{1}^{⋆}, the relation

≻_{α} ∈ ARel([[α]]_{K}, [[|α|]]_{\overline{K}})

is then given in the usual way for base types, sums, products, and functions. For the remaining L_{1}^{⋆}-type constructors, we take:

z ≻_{0} z′ ⟺ z 0^{r} z′ ⟺ false
s ≻_{Σℵ} s′ ⟺ s (Σ_{i}^{r}≻_{ℵ(i)}) s′ ⟺ ∃i ∈ dom ℵ, a ≻_{ℵ(i)} a′. s = in_{i} a ∧ s′ = in_{i} a′
m ≻_{˚ α} u ⟺ m (¯˚ ≻_{α}) u
u ≻_{α} u′ ⟺ u ((≻_{α} →˚ ⪰) →˚ ⪰) u′
⟺ ∀k, k′. (∀a ≻_{α} a′. k a (˚ ⪰) k′ a′) ⇒ uk (˚ ⪰) u′ k′

It is easy to see that all ≻_{α} are admissible (we can view ≻_{0} as being defined by an inverse image of constant functions: z ≻_{0} z′ ⟺ \underline{0} ι^{r} \underline{1} ; for ≻_{Σℵ}, see Lemma A.10(3)), and that ≻_{˚ α} and ≻_{α} are also computation-admissible.

The representation of a˚ α-value in the [[| |]]_{\overline{K}}-translation will always be of the form λk.let˚ x ⇐ m′ in k x for some m′, and thus in particular must be parametric in the answer type. Hence, we could define ≻_{˚ α} without committing to any particular relational interpretation of answers: ⪰ does not occur in the definition of ≻_{˚ α} from ≻_{α}.

On the other hand, for ≻_{α} the identity of the answer type is explicitly exposed to the source language, because A takes an arbitrary value of type ω to be an answer, while # allows answers to be inspected as (control-effect-free) computations of type ω. Thus, if we want to relate terms containing A and #, we cannot choose ⪰ arbitrarily: it must match up with ≻_{ω}. Fortunately, this circular dependency can be resolved, because of the following important result:

Theorem 4.11 Let F and F′ be type constructors, and let ^{•} be a formal relation constructor, built out of (1) the standard relational actions of L_{1}-type constructors, (2) constant admissible relations (computation-admissible for computation-types), and (3) the relation constructor ¯˚ ; so that^{•} maps any relation R ∈ ARel(α, α′) to^{•}R ∈ ARel(F α, F′α′).

Then ^{•} has an invariant relation μR.^{•}R ∈ ARel(μa. F a, μa. F′a), such that

a (μR.^{•}R) a′ ⟺ unroll_{a.F a} a ^{•}(μR.^{•}R) unroll_{a.F′a} a′ .

Proof. See Corollary A.20 in Section A.4.

Form this we immediately obtain:

Lemma 4.12 There exists an admissible relation ⪰ ∈ ARel(ω̂, ω̄) such that

o ⪰ o′ ⟺ ψ o ≻_{ω} ψ̄o′

(where ≻_{ω} is defined in terms of ⪰ by Definition 4.10).

Proof. The existence of ⪰ hinges on ≻_{ω} being defined from it using only the operations enumerated in Theorem 4.11. Thus, we can directly take ⪰ to be the invariant relation for the action ^{•} mapping ⪰ to ≻_{ω}.

Note that even though ω̄ is genuinely recursive when ω contains any computationtype constructors, the circularity in the definition of ⪰ still only occurs when ω contains a . Otherwise, ≻_{α} becomes just an unparameterized definition by induction on α, and in particular does not depend on ⪰. We can then simply take Lemma 4.12 as the definition of ⪰; there is nothing to prove in that case.

We can now state the correctness result for level-erasure:

Lemma 4.13 If Γ ⊢ M : α is a term of L_{1}^{K_{˚ ω}^{cc}} and σ ≻_{Γ} σ′ then [[M]]σ_{K} ≻_{α} [[|M|]]σ′_{\overline{K}}.

Proof. The proof is by induction on M. The interesting cases are:

• Case˚ M. To show:

˚ [[M]]σ_{K} ≻_{˚ α} λk′.k′ [[|M|]]σ′_{\overline{K}}

I.e., that for any O and k (≻_{α} →˚ O) k′,

let˚ a ⇐˚ [[M]]σ_{K} in k a (˚ O) k′ [[|M|]]σ′_{\overline{K}}

With a simplification of the LHS, this reduces to showing

k [[M]]σ_{K} (˚ O) k′ [[|M|]]σ′_{\overline{K}}

which we get immediately from IH on M and the assumption on k and k′.

• Case let˚ x ⇐ M_{1} in M_{2} :˚ α_{2}. To show:

let˚ x ⇐ [[M_{1}]]σ_{K} in [[M_{2}]]σ_{K} ≻_{˚ α_{2}} λk′.[[|M_{1}|]]σ′_{\overline{K}} (λx.[[|M_{2}|]]σ′_{\overline{K}} k′)

That is, for any O and k (≻_{α_{2}} →˚ O) k′, we must show

let˚ a_{2} ⇐ (let˚ x ⇐ [[M_{1}]]σ_{K} in [[M_{2}]]σ_{K}) in k a_{2} (˚ O) [[|M_{1}|]]σ′_{\overline{K}} (λx.[[|M_{2}|]]σ′_{\overline{K}} k′)

Again, by a simple rewriting of the LHS, this is equivalent to

let˚ x ⇐ [[M_{1}]]σ_{K} in (λx.let˚ a_{2} ⇐ [[M_{2}]]σ_{K} in k a_{2})x

(˚ O) [[|M_{1}|]]σ′_{\overline{K}} (λx.[[|M_{2}|]]σ′_{\overline{K}} k′)

By IH on M_{1} and the definition of ≻_{˚ α_{1}}, it suffices to show that

∀a_{1} ≻_{α_{1}} a′_{1}. (λx.let˚ a_{2} ⇐ [[M_{2}]]σ_{K} in k a_{2})a_{1} (˚ O) (λx.[[|M_{2}|]]σ′_{\overline{K}} k′)a′_{1}

i.e., that for all a_{1} ≻_{α_{1}} a′_{1},

let˚ a_{2} ⇐ [[M_{2}]]σ_{K}\{a_{1}/x\} in k a_{2} (˚ O) [[|M_{2}|]]σ′_{\overline{K}}\{a′_{1}/x\}k′

And that follows immediately from the IH on M_{2}, with extended substitutions (σ, a_{1}/x) and (σ′, a′_{1}/x).

• Case let˚ x ⇐ M_{1} in M_{2} : α_{2}. To show:

λk.let˚ x ⇐ [[M_{1}]]σ_{K} in [[M_{2}]]σ_{K} k ≻_{α_{2}} λk′.[[|M_{1}|]]σ′_{\overline{K}} (λx.[[|M_{2}|]]σ′_{\overline{K}} k′)

That is, for k (≻_{α_{2}} →˚ ⪰) k′,

let˚ x ⇐ [[M_{1}]]σ_{K} in [[M_{2}]]σ_{K} k (˚ ⪰) [[|M_{1}|]]σ′_{\overline{K}} (λx.[[|M_{2}|]]σ′_{\overline{K}} k′)

Again, by a simple rewriting of the LHS, this is equivalent to

let˚ x ⇐ [[M_{1}]]σ_{K} in (λx.[[M_{2}]]σ_{K} k)x (˚ ⪰) [[|M_{1}|]]σ′_{\overline{K}} (λx.[[|M_{2}|]]σ′_{\overline{K}} k′)

As above, by IH on M_{1} and the definition of ≻_{˚ α_{1}}, taking α_{0} = ω̂ and O = ⪰, it suffices to show that

∀a_{1} ≻_{α_{1}} a′_{1}. (λx.[[M_{2}]]σ_{K} k)a_{1} (˚ ⪰) (λx.[[|M_{2}|]]σ′_{\overline{K}} k′)a′_{1}

which follows immediately from the IH on M_{2}, with extended substitutions (σ, a_{1}/x) and (σ′, a′_{1}/x).

• Case AM. To show:

λq.˚ (ϕ[[M]]σ_{K}) ≻_{0} λq.˚ (ϕ̄[[|M|]]σ′_{\overline{K}})

I.e., that when q (≻_{0} →˚ ⪰) q′ (vacuously true for any q and q′) then

˚ (ϕ[[M]]σ_{K}) (˚ ⪰)˚ (ϕ̄[[|M|]]σ′_{\overline{K}})

4.2. LEVEL-ERASURE 89

By property 3.20(1) of˚ ⪰, it suffices to show that

ϕ[[M]]σ_{K} ⪰ ϕ̄[[|M|]]σ′_{\overline{K}}

which by Lemma 4.12 is equivalent to

ψ (ϕ[[M]]σ_{K}) ≻_{ω} ψ̄(ϕ̄[[|M|]]σ′_{\overline{K}})

and that we get by IH on M after cancelling out the isomorphisms.

• Case #M. To show:

let˚ o ⇐ [[M]]σ_{K} (λr.˚ (ϕr)) in˚ (ψ o)

≻_{˚ ω} λk′.let˚ o′ ⇐ [[|M|]]σ′_{\overline{K}} (λr′.˚ (ϕ̄r′)) in k′ (ψ̄o′)

I.e., that for any O and k (≻_{ω} →˚ O) k′,

let˚ x ⇐ (let˚ o ⇐ [[M]]σ_{K} (λr.˚ (ϕr)) in˚ (ψ o)) in k x

(˚ O) let˚ o′ ⇐ [[|M|]]σ′_{\overline{K}} (λr′.˚ (ϕ̄r′)) in k′ (ψ̄o′)

which simplifies to

let˚ o ⇐ [[M]]σ_{K} (λr.˚ (ϕr)) in k (ψ o)

(˚ O) let˚ o′ ⇐ [[|M|]]σ′_{\overline{K}} (λr′.˚ (ϕ̄r′)) in k′ (ψ̄o′)

By definition of ⪰ and the assumption on k and k′, we have

∀o ⪰ o′. k (ψ o) (˚ O) k (ψ̄o′)

so by 3.20(2), it suffices to show that

[[M]]σ_{K} (λr.˚ (ϕr)) (˚ ⪰) [[|M|]]σ′_{\overline{K}} (λr′.˚ (ϕ̄r′))

By IH on M, we have [[M]]σ_{K} ≻_{ω} [[|M|]]σ′_{\overline{K}}, so we only need to show that

∀r ≻_{ω} r′.˚ (ϕr) (˚ ⪰)˚ (ϕ̄r′)

which follows from 3.20(1) if we have

∀r ≻_{ω} r′. ϕr ⪰ ϕ̄r′

and that is again an immediate consequence of the definition of ⪰, as in the case for AM above.

• Case C M. Simple – same translation on both sides.

• Case V_{β} M. To show:

V_{[[β]]_{K}} [[M]]σ_{K} ≻_{β} V_{[[|β|]]_{\overline{K}}} [[|M|]]σ′_{\overline{K}}

But by IH on M, [[M]]σ_{K} ≻_{0} [[|M|]]σ′_{\overline{K}}, so this case can never actually occur (indeed, there are no closed values of type 0).

• Case in_{i} M. To show:

in_{i} [[M]]σ_{K} ≻_{Σℵ} in_{i} [[|M|]]σ′_{\overline{K}}

By IH on M, we have [[M]]σ_{K} ≻_{ℵ(i)} [[|M|]]σ′_{\overline{K}}, so we get the result directly from the definition of ≻_{Σℵ}.

• Case outd_{i}M. To show:

outd_{i} [[M]]σ_{K} ≻_{ℵ(i)+1} outd_{i} [[|M|]]σ′_{\overline{K}}

I.e., that

outd_{i} [[M]]σ_{K} (≻_{ℵ(i)} + 1) outd_{i} [[|M|]]σ′_{\overline{K}}

By IH on M and the definition of ≻_{Σℵ}, [[M]]σ_{K} = in_{i′} a and [[|M|]]σ′_{\overline{K}} = in_{i′} a′ for some i′ ∈ dom ℵ and a ≻_{ℵ(i′)} a′. There are two possibilities:

– i′ = i. Then by definition of +^{r} we have

outd_{i} (in_{i} a) = inl a (≻_{ℵ(i)} + 1) inl a′ = outd_{i} (in_{i} a′)

– i′ ≠ i. Then, again by definition of the relational actions of + and 1,

outd_{i} (in_{i′} a) = inr ⟨⟩ (≻_{ℵ(i)} +1) inr ⟨⟩ = outd_{i} (in_{i′} a′)

4.3 Composable continuations from escapes and state

We now only have to implement a one-level language with escapes, prompts, and abort, specified by a simple continuation-passing transform. Since we may want to perform the continuation-passing translation anyway, e.g., for cps-based code generation [App92], we seem to be on the right track. On closer inspection, however, the translation does not quite produce “proper” continuation-passing terms: there is still a little bit of explicit sequencing left in the output.

Recall the equations for [[ ]]_{\overline{K}} from Definition 4.8. The problem is with A, and especially with #, which introduce an explicit notion of sequencing of already continuationpassing terms. By a stroke of good luck, however, we can express this sequencing in terms of another standard effect, namely state.

The key idea is to eliminate the remaining traces of explicit sequencing by performing another continuation-passing transformation, using a new metacontinuation γ to keep track of the nested˚ -computations. That is, we take the implementation interpretation of ambient effects to also be given by a continuation monad. (We do not constrain the answer type of this monad, so we retain the full range of possible ambient computational effects.) While this may at first seem to move us farther away from a direct implementation, we will see that the “properly continuation-passing” terms are effectively unaffected by this second translation, while the translations of A and # change in a useful way.

Definition 4.14 In L_{1}^{μ}, let there be given a computation-type θ of ultimate answers; we will often abbreviate α → θ as ¬α. We then define [[ ]]_{C} : L_{1}^{μ} → L_{1}^{μ} to be the translation expanding˚ -computations into continuation-passing with answer type θ, i.e.,

[[˚ α]]_{C} = ([[α]]_{C} → θ) → θ
[[˚ M]]_{C} = λγ.γ [[M]]_{C}
[[let˚ x ⇐ M_{1} in M_{2}]]_{C} = λγ.[[M_{1}]]_{C} (λx.[[M_{2}]]_{C} γ)

(with other type and term constructors unaffected as usual).

We also define a new continuation-passing translation of L_{1}^{CC_{ω}}, where the answer type is itself explicitly a type of continuation-passing computations (as opposed to the unspecified notion of ambient effects in ˚ ω̄):

Definition 4.15 Let ω be an L¯⋆ ; 1-type, and take ω̌ = μa. [[ω]]_{\overline{K}_{¬¬a}} with isomorphisms

ϕ^{u} : [[ω]]_{\overline{K}_{¬¬ω̌}} →^{∼} ω̌ = roll_{a.[[ω]]_{\overline{K}_{¬¬a}}}  and  ψ^{u} : ω̌ →^{∼} [[ω]]_{\overline{K}_{¬¬ω̌}} = unroll_{a.[[ω]]_{\overline{K}_{¬¬a}}}

We then define [[ ]]_{K^{u}} : L_{1}^{CC_{ω}} → L_{1}^{μ} as follows:

[[˚ α]]_{K^{u}} = K_{¬¬ω̌}[[α]]_{K^{u}} = ([[α]]_{K^{u}} → ¬¬ω̌) → ¬¬ω̌
[[˚ M]]_{K^{u}} = λk.λγ.k [[M]]_{K^{u}} γ
[[let˚ x ⇐ M_{1} in M_{2}]]_{K^{u}} = λk.λγ.[[M_{1}]]_{K^{u}} (λx.λγ′.[[M]]_{K^{u}} k γ′)γ
[[C M]]_{K^{u}} = λk.λγ.[[M]]_{K^{u}} (λa.λq.λγ′.k aγ′)(λz.λγ′′.V z γ′′)γ
[[AM]]_{K^{u}} = λq.λ\underline{γ}.\underline{γ} (ϕ^{u} [[M]]_{K^{u}})
[[#M]]_{K^{u}} = λk.λ\underline{γ}.[[M]]_{K^{u}} (λr.λ\underline{γ′}.\underline{γ′} (ϕ^{u} r))(λo.k (ψ^{u} o)\underline{γ})

Note that all but the underlined occurrences of γ can be η-reduced away, so the translations for value-inclusions, lets, and escapes form a completely standard continuationpassing transformation.

It is also easy to see that this translation consolidates the two nested continuationpassing translations into one:

Lemma 4.16 For any type α of L_{1}^{CC_{ω}}, [[[[α]]_{\overline{K}}]]_{C} = [[α]]_{K^{u}} and for any term M, [[[[M]]_{\overline{K}}]]_{C} = [[M]]_{K^{u}} (in the predomain interpretation of L_{1}^{μ}).

Proof. The only complication is the type-recursion in the definition of the translations. Recall the key cases:

• [[˚ α]]_{\overline{K}} = K ˚ ω̄[[α]]_{\overline{K}} where ω̄ = μa. [[ω]]_{\overline{K} ; ˚ a}.

• [[˚ α]]_{K^{u}} = K_{¬¬ω̌}[[α]]_{K^{u}} where ω̌ = μa. [[ω]]_{\overline{K}_{¬¬a}}.

• [[˚ α]]_{C} = K_{θ}[[α]]_{C} = ¬¬[[α]]_{C}.

We first strengthen the relationship for the type translation to, for any o of L_{1}^{μ},

[[[[α]]_{\overline{K}_{o}}]]_{C} = [[α]]_{\overline{K}_{[[o]]_{C}}}  (*)

The proof of (*) is a simple induction on α; the only interesting case is

[[[[˚ α]]_{\overline{K}_{o}}]]_{C} = [[([[α]]_{\overline{K}_{o}} → o) → o]]_{C} = ([[[[α]]_{\overline{K}_{o}}]]_{C} → [[o]]_{C}) → [[o]]_{C}

=^{ih} ([[α]]_{\overline{K}_{[[o]]_{C}}} → [[o]]_{C}) → [[o]]_{C} = K_{[[o]]_{C}} [[α]]_{\overline{K}_{[[o]]_{C}}} = [[˚ α]]_{\overline{K}_{[[o]]_{C}}}

Now, first take o =˚ a in (*) to get

[[ω̄]]_{C} = [[μa. [[ω]]_{\overline{K} ; ˚ a} ]]_{C} = μa. [[[[ω]]_{\overline{K} ; ˚ a} ]]_{C} = μa. [[ω]]_{\overline{K}_{[[˚ a]]_{C}}} = μa. [[ω]]_{\overline{K}_{¬¬a}} = ω̌

and then, with o = ˚ ω̄,

[[[[α]]_{\overline{K}}]]_{C} = [[[[α]]_{\overline{K} ; ˚ω̄}]]_{C} = [[α]]_{\overline{K}_{[[ ˚ ω̄]]_{C}}} = [[α]]_{\overline{K}_{¬¬[[ω̄]]_{C}}} = [[α]]_{\overline{K}_{¬¬ω̌}}

Given the equalities on types, the equality on terms is completely straightforward. The cases for value-inclusion, let, and escape are immediate since their [[ ]]_{K}-translations do not contain any sequencing; we obtain the result by simple η-conversion. For A and #, we use that

[[ϕ̄M]]_{C} = [[roll_{a.[[ω]]_{\overline{K} ; ˚ a}} M]]_{C} = roll_{a.[[[[ω]]_{\overline{K} ; ˚ a} ]]_{C}} [[M]]_{C} = roll_{a.[[ω]]_{\overline{K}_{¬¬a}}} [[M]]_{C} = ϕ^{u} [[M]]_{C}

and analogously for ψ̄. Then, for example,

[[[[AM]]_{\overline{K}}]]_{C} = [[λq.˚ (ϕ̄[[M]]_{\overline{K}})]]_{C} = λq.λγ.γ (ϕ^{u} [[[[M]]_{\overline{K}}]]_{C}) =^{ih} λq.λγ.γ (ϕ^{u} [[M]]_{K^{u}} )

= [[AM]]_{K^{u}}

4.3.1 Re-tying the recursive knot

Our metacontinuation translation [[ ]]_{K^{u}} was derived directly from the original [[ ]]_{\overline{K}}. However, to match it up with he state-passing translation later, we first need to relate [[ ]]_{K^{u}} to an equivalent formulation, using an isomorphic answer type:

Definition 4.17 Let ς = μa′. ¬[[ω]]_{\overline{K}_{¬a′}} with isomorphisms

ϕ^{n} : ¬[[ω]]_{\overline{K}_{¬ς}} →^{∼} ς = roll_{a′.¬[[ω]]\overline{K}_{¬a′}}  and  ψ^{n} : ς →^{∼} ¬[[ω]]_{\overline{K}_{¬ς}} = unroll_{a′.¬[[ω]]\overline{K}_{¬a′}}

Then define [[ ]]_{K^{n}} to be the continuation-passing translation with answer type ¬ς, and with translation equations for escape, abort and reset now reading:

[[C M]]_{K^{n}} = λk.λg.[[M]]_{K^{n}} (λa.λq.λg′.k ag′)(λz.λg′′.V z g′′)g
[[AM]]_{K^{n}} = λk.λg.ψ^{n}g [[M]]_{K^{n}}
[[#M]]_{K^{n}} = λk.λg.[[M]]_{K^{n}} (λr.λg′.ψ^{n} g′ r)(ϕ^{n} (λa.k ag))

It should be intuitively plausible that this definition is equivalent to the one in Definition 4.15 above; we state this precisely in Corollary 4.22 below, to which one may proceed without loss of continuity.

From Section A.3, we include:

Definition 4.18 The functorial action of L_{1}-type constructors on isomorphisms is given as follows, so that for any φ : α_{1} →^{∼} α_{2}, Φ^{i}_{a.α}(φ) : α{α_{1}/a} →^{∼} α{α_{2}/a} and Ψ^{i}_{a.β}(φ) :

β{α_{1}/a} →^{∼} β{α_{2}/a}:

Φ^{i}_{a.a}(φ)a = φa
Φ^{i}_{a.ι}(φ)n = n
Φ^{i}_{a.1}(φ)u = ⟨⟩
Φ^{i}_{a.α_{1}×α_{2}}(φ)p = ⟨Φ^{i}_{a.α_{1}}(φ)(fst p), Φ^{i}_{a.α_{2}}(φ)(snd p)⟩
Φ^{i}_{a.0}(φ)z = z
Φ^{i}_{a.α_{1}+α_{2}}(φ)s = case(s, a_{1}.inl (Φ^{i}_{a.α_{1}}(φ)a_{1}), a_{2}.inl (Φ^{i}_{a.α_{2}}(φ)a_{2}))
Φ^{i}_{a.Σℵ}(φ)s = case(s, i. a_{i}. in_{i} (Φ^{i}_{a.ℵ(i)}(φ)a_{i}))
Φ^{i}_{a.β}(φ)b = Ψ^{i}_{a.β}(φ)b
Ψ^{i}_{a.˚ α}(φ)m = let˚ x ⇐ m in˚ (Φ^{i}_{a.α}(φ)x)
Ψ^{i}_{a.1}(φ)o = ⟨⟩
Ψ^{i}_{a.β_{1}×β_{2}}(φ)p = ⟨Ψ^{i}_{a.β_{1}}(φ)(fst p), Ψ^{i}_{a.β_{2}}(φ)(snd p)⟩
Ψ^{i}_{a.α→β}(φ)g = λx.Ψ^{i}_{a.β}(φ)(g (Φ^{i}_{a.α}(φ^{−1})x))

We then take advantage of the fact that our chosen solutions to recursive type equations are unique up to isomorphism, so that in particular it does not matter where we break up the recursion when defining a pair of mutually recursive types:

Lemma 4.19 Let F and G be type constructors of L_{1} (not necessarily covariant), and let α = μa. F (Ga) and α′ = μa′. G(F a′) be the solutions to the corresponding recursive type equations. Then in the predomain model, there exists an isomorphism χ : Gα →^{∼} α′, which further satisfies the following two (equivalent) coherence equations:

x: Gα ⊢ roll_{a′.G(F a′)} (Φ^{i}_{a′.G(F a′)}(χ)(Φ^{i}_{a.Ga}(unroll_{a.F (Ga)})x)) = χx : α′
y: α′ ⊢ Φ^{i}_{a.Ga}(roll_{a.F (Ga)})(Φ^{i}_{a′.G(F a′)}(χ^{−1})(unroll_{a′.G(F a′)} y)) = χ^{−1} y : Gα

Proof. See Lemma A.14 in the appendix.

In our case, we obtain from this the following instance:

Lemma 4.20 There exists an isomorphism χ : ¬ω̌ →^{∼} ς. This induces for any type α of L_{1}^{CC_{ω}} an isomorphism Υ_{α} : [[α]]_{K^{u}} →^{∼} [[α]]_{K^{n}} = Φ^{i}_{a′.[[α]]\overline{K}_{¬a′}} (χ), and moreover

g: ς, r: [[ω]]_{K^{u}} ⊢ χ^{−1} g (ϕ^{u} r) = ψ^{n} g (Υ_{ω} r) : θ

Proof. Define the type constructors F a′ = [[ω]]_{\overline{K}_{¬a′}} and Ga = ¬a. Then we have

ω̌ = μa. [[ω]]_{\overline{K}_{¬¬a}} = μa. F (Ga) and ς = μa′. ¬[[ω]]_{\overline{K}_{¬a′}} = μa′. G(F a′)

We thus get the isomorphism χ : ¬ω̌ → ς directly from Lemma 4.19. Moreover, we can write Υ_{ω} = Φ^{i}_{a′.F a′}(χ). Now, first note that

Φ^{i}_{a.¬α}(φ)hx = Ψ^{i}_{a.α→θ}(φ)hx = Ψ^{i}_{a.θ}(φ)(h(Φ^{i}_{a.α}(φ^{−1})x)) = id (h(Φ^{i}_{a.α}(φ^{−1})x))
= h(Φ^{i}_{a.α}(φ^{−1})x)

And then, using the second form of the coherence equation from 4.19, we get

χ^{−1} g (ϕ^{u} r) = Φ^{i}_{a.¬a}(ϕ^{u})[Φ^{i}_{a′.¬[[ω]]\overline{K}_{¬a′}} (χ^{−1})(ψ^{n} g)](ϕ^{u} r)

= [Φ^{i}_{a′.¬[[ω]]\overline{K}_{¬a′}} (χ^{−1})(ψ^{n} g)](Φ^{i}_{a.a}(ψ^{u})(ϕ^{u} r)) = [Φ^{i}_{a′.¬[[ω]]\overline{K}_{¬a′}} (χ^{−1})(ψ^{n} g)](ψ^{u} (ϕ^{u} r))
= Φ^{i}_{a′.¬[[ω]]\overline{K}_{¬a′}} (χ^{−1})(ψ^{n} g)r = ψ^{n} g (Φ^{i}_{a′.[[ω]]\overline{K}_{¬a′}} (χ)r) = ψ^{n} g (Υ_{ω} r)

Because the translations from Definitions 4.15 and 4.17 are both continuation-passing translations with isomorphic answer types, they are very closely related: instead of the usual logical relation, we get a simple equational correspondence:

Lemma 4.21 Let Γ = (x_{1}: α_{1}, …, x_{n}: α_{n}), and let us write Υ_{Γ} for the substitution (Υ_{α_{1}} x_{1}/x_{1}, …, Υ_{α_{n}} x_{n}/x_{n}). Then for any L_{1}^{CC_{ω}}-term Γ ⊢ M : α,

Υ_{α} [[M]]_{K^{u}} = [[M]]Υ_{Γ}_{K^{n}}

Proof. The cases for the standard terms (products, sums, and functions) are straightforward. For example, for abstractions and applications, we have

Υ_{α→β} [[λx.M]]_{K^{u}} = λa.Υ_{β} ((λx.[[M]]_{K^{u}} )(Υ_{α}^{−1} a)) = λa.(λx.Υ_{β} [[M]]_{K^{u}} )(Υ_{α}^{−1} a)

=^{ih} λa.(λx.[[M]]Υ_{Γ},Υ_{α} x/x_{K^{n}})(Υ_{α}^{−1} a) = λa.[[M]]Υ_{Γ},Υ_{α} x/x_{K^{n}}{Υ_{α}^{−1} a/x} = λa.[[M]]Υ_{Γ},Υ_{α} (Υ_{α}^{−1} a)/x_{K^{n}}

= λa.[[M]]Υ_{Γ},a/x_{K^{n}} = (λx.[[M]]_{K^{n}} )^{Υ_{Γ}} = [[λx.M]]Υ_{Γ}_{K^{n}}

Υ_{β} [[M_{1} M_{2}]]_{K^{u}} = Υ_{β} ([[M_{1}]]_{K^{u}} [[M_{2}]]_{K^{u}} ) = (λa.Υ_{β} ([[M_{1}]]_{K^{u}} a))[[M_{2}]]_{K^{u}} = (λa′.Υ_{β} ([[M_{1}]]_{K^{u}} (Υ_{α}^{−1} a)))(Υ_{α} [[M_{2}]]_{K^{u}} ) = (Υ_{α→β} [[M_{1}]]_{K^{u}} )(Υ_{α} [[M_{2}]]_{K^{u}} ) =^{ih} [[M_{1}]]Υ_{Γ}_{K^{n}} [[M_{2}]]Υ_{Γ}_{K^{n}} = ([[M_{1}]]_{K^{n}} [[M_{2}]]_{K^{n}} )^{Υ_{Γ}} = [[M_{1} M_{2}]]Υ_{Γ}_{K^{n}}

For computations, let us first name the induced isomorphisms on meta-computations

π : ¬¬ω̌ →^{∼} ¬ς = Φ^{i}_{a.¬a}(χ) and π^{−1} : ¬ς →^{∼} ¬¬ω̌ = Φ^{i}_{a.¬a}(χ^{−1})

The value isomorphism for computations then becomes:

Υ_{˚ α} u = Φ^{i}_{a.[[˚ α]]_{\overline{K}_{¬a}}} (χ)u = Φ^{i}_{a.([[α]]_{\overline{K}_{¬a}} →¬a)→¬a}(χ)u

= λk.Ψ^{i}_{a.¬a}(χ)(u(Φ^{i}_{a.[[α]]_{\overline{K}_{¬a}} →¬a}(χ^{−1})k))
= λk.π (u(λa.Ψ^{i}_{a.¬a}(χ^{−1})(k (Φ^{i}_{a.[[α]]_{\overline{K}_{¬a}}} (χ)a)))) = λk.π (u(λa.π^{−1} (k (Υ_{α} a))))

With this, the cases for inclusion and let are also simple, e.g.,

Υ_{˚ α} [[˚ M]]_{K^{u}} = Υ_{˚ α} [[λk.k [[M]]_{\overline{K}}]]_{C} = Υ_{˚ α} (λk.k [[M]]_{K^{u}} )

= λk.π ((λk.k [[M]]_{K^{u}} )(λa.π^{−1} (k (Υ_{α} a)))) = λk.π (π^{−1} (k (Υ_{α} [[M]]_{K^{u}} )))
= λk.k (Υ_{α} [[M]]_{K^{u}} ) =^{ih} λk.k [[M]]Υ_{Γ}_{K^{n}} = [[˚ M]]Υ_{Γ}_{K^{n}}

The interesting cases are for A and #, which actually depend on the answer type. Here we need to expand the π and π^{−1},

π f = Φ^{i}_{a.¬a}(χ)f = λg.f (χ^{−1} g) and π^{−1} f = Φ^{i}_{a.¬a}(χ^{−1})f = λγ.f (χγ)

in Υ_{˚ α} to get

Υ_{˚ α} u = λk.λg.u(λa.λγ.k (Υ_{α} a)(χγ))(χ^{−1} g) .

We then check:

4.3. COMPOSABLE CONTINUATIONS FROM ESCAPES AND STATE 95

Υ_{˚ 0} [[AM]]_{K^{u}} = Υ_{˚ 0} [[λq.˚ (ϕ^{u} [[M]]_{K})]]_{C} = Υ_{˚ 0} (λq.λγ.γ (ϕ^{u} [[M]]_{K^{u}} ))

= λq.λg.χ^{−1} g (ϕ^{u} [[M]]_{K^{u}} ) =^{†} λq.λg.ψ^{n} g (Υ_{ω} [[M]]_{K^{u}} ) =^{ih} λq.λg.ψ^{n} g [[M]]Υ_{Γ}_{K^{n}}

= [[AM]]Υ_{Γ}_{K^{n}}

where the step marked with † uses the coherence equation from Lemma 4.20.

For reset, the calculation is a little more involved, since here we do not simply discard the continuation:

Υ_{˚ ω} [[#M]]_{K^{u}} = Υ_{˚ ω} [[λk.let˚ o ⇐ M (λr.˚ (ϕ^{u} r)) in k (ψ^{u} o)]]_{C} = Υ_{˚ ω} (λk.λγ.[[M]]_{K^{u}} (λr.λγ′.γ′ (ϕ^{u} r))(λo.k (ψ^{u} o)γ)) = λk.λg.[[M]]_{K^{u}} (λr.λγ′.γ′ (ϕ^{u} r))(λo.k (Υ_{ω} (ψ^{u} o))(χ(χ^{−1} g))) = λk.λg.[[M]]_{K^{u}} (λr.λγ′.γ′ (ϕ^{u} r))(λo.k (Υ_{ω} (ψ^{u} o))g) = λk.λg.[[M]]_{K^{u}} (λr.λγ′.γ′ (ϕ^{u} r))(λo.(λr.k r g)(Υ_{ω} (ψ^{u} o))) = λk.λg.[[M]]_{K^{u}} (λr.λγ′.χ^{−1} (χγ′)(ϕ^{u} r))(λo.ψ^{n} (ϕ^{n} (λr.k r g))(Υ_{ω} (ψ^{u} o))) =^{†} λk.λg.[[M]]_{K^{u}} (λr.λγ′.ψ^{n} (χγ′)(Υ_{ω} r))(λo.χ^{−1} (ϕ^{n} (λr.k r g))(ϕ^{u} (ψ^{u} o))) = λk.λg.[[M]]_{K^{u}} (λr.λγ′.ψ^{n} (χγ′)(Υ_{ω} r))(χ^{−1} (ϕ^{n} (λr.k r g))) = λk.λg.(Υ_{˚ ω} [[M]]_{K^{u}} )(λr.λg′.ψ^{n} g′ r)(ϕ^{n} (λr.k r g))

=^{ih} λk.λg.[[M]]Υ_{Γ}_{K^{n}} (λr.λg′.ψ^{n} g′ r)(ϕ^{n} (λr.k r g)) = [[#M]]Υ_{Γ}_{K^{n}}

where again the † marks two applications of the coherence equation.

We can now state the observable consequence of the above result, expressed using only constructs of L_{1}^{μ}, i.e., without the “helper” isomorphisms χ and Υ_{α}:

Corollary 4.22 Let M be a closed L_{1}^{CC_{ω}}-term of type˚ ι. Then for any a_{0} : θ and p : ι→θ,

[[M]]_{K^{u}} (λn.λγ′.pn)(λo.a_{0}) = [[M]]_{K^{n}} (λn.λg′.pn)(ϕ^{n} (λr.a_{0}))

Proof. Simple equational verification, using Lemma 4.21 (with empty Γ), Υ_{ι} = Φ^{i}_{a′.ι}(χ) = id, and the coherence equation:

[[M]]_{K^{n}} (λn.λg′.pn)(ϕ^{n} (λr.a_{0})) = (Υ_{˚ ι} [[M]]_{K^{u}} )(λn.λg′.pn)(ϕ^{n} (λr.a_{0})) = [[M]]_{K^{u}} (λn.λγ′.[λn.λγ.pn](Υ_{ι} n)(χγ′))(χ^{−1} (ϕ^{n} (λr.a_{0}))) = [[M]]_{K^{u}} (λn.λγ′.p(Υ_{ι} n))(λo.χ^{−1} (ϕ^{n} (λr.a_{0}))(ϕ^{u} (ψ^{u} o))) =^{†} [[M]]_{K^{u}} (λn.λγ′.pn)(λo.ψ^{n} (ϕ^{n} (λr.a_{0}))(Υ_{ω} (ψ^{u} o))) = [[M]]_{K^{u}} (λn.λγ′.pn)(λo.(λr.a_{0})(Υ_{ω} (ψ^{u} o))) = [[M]]_{K^{u}} (λn.λγ′.pn)(λo.a_{0})

Although this corollary may at first appear too specialized, it actually covers exactly what we need. In particular, if M is a term without escaping effects, it must be equivalent to an included numeral,˚ \underline{n} in the [[ ]]_{K^{u}}-translation, and we get:

[[M]]_{K^{u}} (λn.λγ′.pn)(λo.a_{0}) = [[˚ \underline{n}]]_{K^{u}} (λn.λγ′.pn)(λo.a_{0})

= [[˚ \underline{n}]]_{K^{n}} (λn.λg′.pn)(ϕ^{n} (λr.a_{0})) = [λk.λg.k \underline{n}g](λn.λg′.pn)(ϕ^{n} (λr.a_{0})) = p\underline{n}

On the other hand, if M actually invokes the metacontinuation (through an A not protected by an enclosing #), both translations return the “error answer” a_{0}.

4.3.2 The continuation-state language

Let us now assume that we have available a language with Scheme-like escapes and state as the effects. For simplicity, we consider the state to consist of only a single, typed cell (additional state could still be accommodated by choosing θ appropriately):

Definition 4.23 Let σ be an L¯⋆ ; 1-type. Then the signature L_{1}^{CS_{σ}} of the continuation-state language consists of L_{1} with all occurrences of˚ replaced by˚ (as in Definition 4.6), and extended with the following term constructors:

\frac{Γ ⊢ M : (α →˚ 0) →˚ 0}{Γ ⊢ C M :˚ α}  \overline{Γ ⊢ !st :˚ σ}  \frac{Γ ⊢ M : σ}{Γ ⊢ st := M :˚ 1}

Note that the type σ can be a complex type, such as ι→˚ ι, so that values stored in the cell can be procedures that themselves read or modify the state. This again introduces a reflexivity in the types, and it is well known that one can define a fixed-point operator using higher-order state (as actually done for letrec in Scheme [CR91]). As usual, we give a definitional translation of the new language:

Definition 4.24 Let θ be a computation-type of L_{1}^{μ}, and let σ be a L¯⋆ ; 1-type. Then the translation [[ ]]_{S} from L_{1}^{CS_{σ}} to L_{1}^{μ} is given by, on types:

[[˚ α]]_{S} = K_{¬σ̂}[[α]]_{S} where σ̂ = μa. [[σ]]_{\overline{K}_{¬a}}

We have ϕ^{s} : [[σ]]_{S} →^{∼} σ̂ and ψ^{s} : σ̂ →^{∼} [[σ]]_{S} in the two directions. (As usual, these can be taken as identities if σ does not contain˚ .) Then we can give the term translations of the new constructs:

[[˚ M]]_{S} = λk.λs.k [[M]]_{S} s
[[let˚ x ⇐ M_{1} in M_{2}]]_{S} = λk.λs.[[M_{1}]]_{S} (λx.λs′.[[M_{2}]]_{S} k s′)s
[[C M]]_{S} = λk.λs.[[M]]_{S} (λa.λq.λs′.k as′)(λz.λs′′.V z s′′)s
[[!st]]_{S} = λk.λ\underline{s}.k (ψ^{s} \underline{s})\underline{s}
[[st := M]]_{S} = λk.λ\underline{s}.k ⟨⟩(ϕ^{s} [[M]]_{S})

Again, all but the underlined instances of state-passing in the above can be etareduced away. In other words, for the core computational structure, this is a standard continuation-translation with answer type σ̂ → θ.

Now pick σ = ω →˚ 0. We then have

σ̂ ≅ [[ω →˚ 0]]_{S} = [[ω]]_{S} → (0 → σ̂ → θ) → σ̂ → θ

We will use σ̂ to represent our metacontinuation γ. Although this state-based encoding of γ now also gets passed a continuation 0 → ¬σ̂ and a state σ̂, it will use neither of these. That is, we informally have “σ̂ ≅ [[ω]]_{S} → θ”.

Having chosen a suitable state type, we also need to express the relevant operations on the metacontinuation in terms of the constructs of our continuation-state language. For conciseness, we introduce the abbreviation:

\frac{Γ ⊢ M_{1} :˚ 1 Γ ⊢ M_{2} :˚ α}{Γ ⊢ M_{1}; M_{2} :˚ α}

with expansion

M_{1}; M_{2} =^{def} let˚ ⟨⟩ ⇐ M_{1} in M_{2}

We already have value-inclusion, computation-sequencing, and escapes directly available in L_{1}^{CS_{σ}}. For the remaining two constructs of L_{1}^{CC_{ω}}, we take:

Definition 4.25 Let ω be an L¯⋆ ; 1-type. Then in L_{1}^{CS_{ω→˚ 0}}, we define operators A and #, typed as in Definition 4.6, as follows:

AM =^{def} let˚ g ⇐!st in g M
#M =^{def} C (λc^{ω→˚ 0}.let˚ g ⇐!st in (st := (λv^{ω}.(st := g; cv)); let˚ x ⇐ M in Ax))

Note in particular that the procedure stored into st in # does not use the previous value of st, nor does it return to its point of call (not that it could, since its return type is empty).

We now set up a system of logical relations suitable for showing that the above statebased definitions of the control operators capture the behavior of the metacontinuationbased translation:

Lemma 4.26 Let there be given a relational correspondence between two interpretations L and L′ of L_{1}^{μ}, with a computation-extension of relations R ↦˚ R. Let ≃ ∈ CARel(θ, θ) be an arbitrary computation-admissible relation on ultimate answers. Then there exists a collection of relations with the following properties:

• On “wrapped” ultimate answers: ≃^{∗} ∈ CARel(θ, (0 → ¬σ̂) → σ̂ → θ)

m ≃^{∗} m′ ⟺ ∀q ∈ Val_{L′}(0 → ¬σ̂), s ∈ Val_{L′}(σ̂). m ≃ m′ q s

• On metacontinuations/state: ≍ ∈ ARel(ς, σ̂),

g ≍ s ⟺ ψ^{n} g (∼_{ω}→ ≃^{∗}) ψ^{s} s ⟺ ∀r ∼_{ω} r′. ψ^{n} g r ≃^{∗} ψ^{s} sr′

• On meta-computations: ≈ ∈ CARel(ς → θ, σ̂ → θ) = (≍ → ≃), i.e.,

x ≈ x′ ⟺ ∀g ≍ s. xg ≃ x′ s

• On values: for any L¯⋆ ; 1-type α, ∼_{α} ∈ ARel([[α]]_{K^{n}}, [[α]]_{S}), defined in the usual way for the standard type constructors, and in particular,

u ∼_{˚ α} u′ ⟺ u ((∼_{α} → ≈) → ≈) u′

Proof. First assume that the relation ≍ is given independently, and define the others in terms of it (thus satisfying all of the equivalences in the lemma except the one characterizing ≍). Further, define ^{•}≍ ∈ CARel([[ω]]_{K^{n}} → θ, [[ω]]_{S} → (0 → ¬σ̂) → σ̂ → θ) by

γ (^{•}≍) γ′ ⟺ γ (∼_{ω} → ≃^{∗}) γ′ ⟺ ∀r ∼_{ω} r′. γ r ≃^{∗} γ′ r′

≃^{∗} is a computation-admissible relation, being given as an intersection over inverse images of ≃ by the (rigid) functions id on the LHS and λm.mq s on the RHS. (≃^{∗} does not depend on ≍, so there is no concern about admissibility of the action defining it.) All of the relational actions defining ^{•}≍ from ≍ are thus standard, so by Theorem 4.11 we can take ≍ as the invariant relation for the overall action, i.e.,

g ≍ s ⟺ ψ^{n} g (^{•}≍) ψ^{s} s

giving us the remaining equivalence of the lemma.

Having established existence of the appropriate relations, we can now easily show correctness of the state-based representation of the metacontinuation:

Lemma 4.27 Let Γ ⊢ M : α be a term of L_{1}^{CC_{ω}} and σ ∼_{Γ} σ′. Then [[M]]σ_{K^{n}} ∼_{α} [[M]]σ′_{S} (where on the RHS we use the expansions of A and # from Definition 4.25.

Proof. By induction on M. Most cases are immediate, with the term constructors having the same expansions in the two translations. The only exceptions are:

• Case AM. We first compute

[[AM]]_{S} = [[let˚ g ⇐!st in g M]]_{S} = λq.λs.[[!st]]_{S} (λg.λs′.[[g M]]_{S} q s′)s

= λq.λs.(λg.λs′.g [[M]]_{S} q s′)(ψ^{s} s)s = λq.λs.(ψ^{s} s)[[M]]_{S} q s

We must then show that [[AM]]σ_{K^{n}} ∼_{˚ 0} [[AM]]σ′_{S}, i.e., that

λq.λg.(ψ^{n} g)[[M]]_{K^{n}} ∼_{˚ 0} λq′.λs.(ψ^{s} s)[[M]]σ′_{S} q′ s

So let q (0 → ≈) q′. Then we must show

λg.(ψ^{n} g)[[M]]σ_{K^{n}} ≈ λs.(ψ^{s} s)[[M]]σ′_{S} q′ s

Accordingly, let g ≍ s; it then suffices to show that

(ψ^{n} g)[[M]]σ_{K^{n}} ≃^{∗} (ψ^{s} s)[[M]]σ′_{S}

which follows from the definition of g ≍ s and the IH that [[M]]σ_{K^{n}} ∼_{ω} [[M]]σ′_{S}.

• Case #M. Again, we first expand the RHS:

[[#M]]_{S} = [[C (λc.let˚ g ⇐!st in (st := (λv.(st := g; cv)); let˚ x ⇐ M in Ax))]]_{S} = ⋯ = λk.λs.[[M]]_{S} (λx.λs′.(ψ^{s} s′)x(λz.V z)s′)(ϕ^{s} (λv.λq.λs′′.k v (ϕ^{s} (ψ^{s} s))))

= λk.λs.[[M]]_{S} (λx.λs′.(ψ^{s} s′)x(λz.V z)s′)(ϕ^{s} (λv.λq.λs′′.k v s))

We must now show that [[#M]]σ_{K^{n}} ∼_{˚ ω} [[#M]]σ′_{S}, i.e., that

λk.λg.[[M]]σ_{K^{n}} (λr.λg′.ψ^{n} g′ r)(ϕ^{n} (λa.k ag))

∼_{˚ ω} λk′.λs.[[M]]σ′_{S} (λx.λs′.(ψ^{s} s′)x(λz.V z)s′)(ϕ^{s} (λv.λq.λs′′.k′ v s))

As usual, assume k (∼_{ω} → ≈) k′ and g ≍ s; we must then show

[[M]]σ_{K^{n}} (λr.λg′.ψ^{n} g′ r)(ϕ^{n} (λa.k ag))

≃ [[M]]σ′_{S} (λx.λs′.(ψ^{s} s′)x(λz.V z)s′)(ϕ^{s} (λv.λq.λs′′.k′ v s))

By IH on M, it suffices to show that the continuations and metacontinuations passed to the two translations are related. For the continuations, we must show that if r ∼_{ω} x and g′ ≍ s′ then

ψ^{n} g′ r ≃ (ψ^{s} s′)x(λz.V z)s′

which follows from the definition of g′ ≍ s′. Similarly, for the metacontinuations, we must show that

ϕ^{n} (λa.k ag) ≍ ϕ^{s} (λv.λq.λs′′.k′ v s)

Again, by definition of ≍, this requires showing that for r ∼_{ω} r′.

ψ^{n} (ϕ^{n} (λa.k ag))r ≃^{∗} ψ^{s} (ϕ^{s} (λv.λq.λs′′.k′ v s))r′

i.e., cancelling the isomorphisms, that

k r g ≃^{∗} λq.λs′′.k′ r′ s

which follows immediately from the definition of ≃^{∗} and the assumption on k and k′.

4.4 Putting it all together

Summarizing the results of this chapter, we can state:

Theorem 4.28 Let there be given a relational correspondence between a language (L_{1}^{μ}, L) and itself, with a computation-extension of relations R ↦˚ R such that ≃ =˚ (ι^{r}) is an equivalence relation.

Further, let [[ ]]_{C} : L_{1}^{μ} →L_{1}^{μ} be the translation of ambient effects using the continuation monad with answer type θ =˚ ι from Definition 4.14, p ∈ Val_{L}(ι → θ) a printing function, and a_{0} ∈ Val_{L}(θ) an error answer.

Finally, let ω be a type of L_{1}^{⋆}, [[ ]]_{K} the continuation-passing transform with answer type ω from Definition 3.34, and [[ ]]_{S} the continuation-state transform from Definition 4.24, with state type σ = |ω| →˚ 0 and ultimate-answer type θ.

Then for any complete program · ⊢ M :˚ ι in L_{1}^{K_{˚ ω}},

[[[[M]]_{K}]]_{C} p ≃ [[M′_{1}]]_{S} (λn.λs′.pn)(ϕ^{s} (λx.λq.λs.a_{0}))

where · ⊢ M′_{1} : ˚ ι is a term of L_{1}^{CS_{σ}} obtained syntactically from M by (1) erasing all level-annotations on value-inclusions and lets, and (2) defining μ^{K}( ) and [ ]^{K} in terms of escapes and state as detailed in Lemmas 4.3 and 4.4, and Definition 4.25.

(For a correctly effect-stratified program M, the initial error-metacontinuation on the right-hand side will never be invoked. When the implementation is hosted in an ML-like language, however, the system cannot statically verify that M is typable in our stricter system, only that the level-erasure of M is ML-typable. Pragmatically, to give a more useful behavior for effect-typing errors (notably if M has escaping control-effects, i.e., if it effectively has type ι rather than˚ ι), we therefore take the initial metacontinuation to produce a distinct answer a_{0} when invoked; we want to show that the simulation is still correct with this error-catching extension.)

Proof. First, let L_{C} be the predomain semantics for the ambient-effect monad induced by K_{˚ ι} (which is easily checked to be a uniform monad in the predomain semantics) as in Proposition 2.20. Proposition 2.25 (straightforwardly extended to the additional term constructors of L_{1}^{μ}) then gives us that

L_{C}[[ ]] = L[[[[ ]]_{C}]]

for types and terms. Moreover, the standard relational action of K_{˚ ι} in L, i.e.,

m (CR) m′ ⟺ m ((R →˚ ι) →˚ ι) m′
⟺ ∀γ, γ′. (∀a R a′. γ a ≃ γ′ a′) ⇒ mγ ≃ m′ γ′

is easily seen to be a computation-extension for the notion of ambient effects determined by the continuation monad: for any a and a′ such that a R a′,

[[˚ a]]_{C} = λk.k a ((R → ≃)→ ≃) λk.k a′ = [[˚ a′]]_{C} ,

and similarly for let˚ .

Let M′ now be the L_{1}^{K_{˚ ω}^{cc}}-program obtained from M by defining μ^{K}( ) and [ ]^{K} in terms of C, A, and # (still with their two-level types). Then from Definition 4.5 (with associated lemmas) we get that in L_{C}, [[M]]_{K} = [[M′]]_{K}, and hence in L that

[[[[M]]_{K}]]_{C} p = [[[[M′]]_{K}]]_{C} p  (*)

We can now use the level-erasure Lemma 4.13 to get, in the relational correspondence between the two copies of L_{C}:

[[M′]]_{K} ≻_{˚ ι} [[|M′|]]_{\overline{K}}

Since ≻_{ι} is simply equality of numerals, this expands to

∀α_{0} type_{L_{1}^{μ}}, O ∈ ARel_{L_{C},L_{C}}(α_{0}, ω̄), k ∈ Val_{L_{C}}(ι →˚ α_{0}), k′ ∈ Val_{L_{C}}(ι → ˚ ω̄).
(∀n ∈ N. k \underline{n} (˚ O) k′ \underline{n}) ⇒ let˚ x ⇐ [[M′]]_{K} in k x (˚ O) [[|M′|]]_{\overline{K}} k′

Or, in the original correspondence:

∀α_{0} type_{L_{1}^{μ}}, O ∈ ARel_{L,L}([[α_{0}]]_{C}, [[ω̄]]_{C}),
k ∈ Val_{L}(ι → ([[α_{0}]]_{C} → θ) → θ), k′ ∈ Val_{L}(ι → ([[ω̄]]_{C} → θ) → θ).
(∀n ∈ N. k \underline{n} (CO) k′ \underline{n}) ⇒ λγ.[[[[M′]]_{K}]]_{C} (λx.k xγ) (CO) [[[[|M′|]]_{\overline{K}}]]_{C} k′

Somewhat surprisingly, the actual choice of the relation O does not matter much; it is the use of C to computation-extend O that is important. In fact, we can simply take α_{0} = 0, and O to vacuously relate every element of 0 to every element of [[ω̄]]_{C}. Then consider the two continuations

k = λn^{ι}.λγ^{0→θ}.pn and k′ = λn^{′ι}.λγ^{′[[ω̄]]_{C} →θ}.pn′ .

Let n be a natural number; we must show that k \underline{n} (CO) k′ \underline{n}, i.e., that

∀γ, γ′. (∀o O o′. γ o ≃ γ′ o′) ⇒ k \underline{n}γ ≃ k′ \underline{n}γ′ .

Since both k and k′ ignore their metacontinuation arguments, this reduces to p\underline{n} ≃ p\underline{n}, which we get from reflexivity of ≃. We thus have:

λγ.[[[[M′]]_{K}]]_{C} p = λγ.[[[[M′]]_{K}]]_{C} (λx.k xγ)

(CO) [[[[|M′|]]_{\overline{K}}]]_{C} k′ = λγ′.[[[[|M′|]]_{\overline{K}}]]_{C} (λn.λγ′′.pn)γ′

Take γ = λz.V_{θ} z and γ′ = λo.a_{0}; they vacuously map all O-related values to ≃- related results. Expanding the definition of CO, we therefore get:

[[[[M′]]_{K}]]_{C} p ≃ [[[[|M′|]]_{\overline{K}}]]_{C} (λn.λγ′′.pn)(λo.a_{0})  (*)

We can now take the step to escapes and state. Let M_{1} = |M′|. First, Lemma 4.16 gives us

[[[[M_{1}]]_{\overline{K}}]]_{C} (λn.λγ′′.pn)(λx.a_{0}) = [[M_{1}]]_{K^{u}} (λn.λγ′′.pn)(λx.a_{0})  (*)

and then Corollary 4.22,

[[M_{1}]]_{K^{u}} (λn.λγ′′.pn)(λx.a_{0}) = [[M_{1}]]_{K^{n}} (λn.λg.pn)(ϕ^{n} (λx.a_{0}))  (*)

From Lemma 4.27, we get, in the L-correspondence:

[[M_{1}]]_{K^{n}} ∼_{˚ ι} [[M′_{1}]]_{S}

where M′_{1} is obtained from M_{1} by defining A and # in terms of C, !st and st := as in Definition 4.25. We want to get from this that

[[M_{1}]]_{K^{n}} (λx.λg′.px)(ϕ^{n} (λx.a_{0})) ≃ [[M′_{1}]]_{S} (λx.λs′.px)(ϕ^{s} (λx.λq.λs.a_{0}))  (*)

Expanding the definition of ∼_{˚ ι}, we need to verify that the continuations are related, i.e., that for every n ∼_{ι} n′ (i.e., n = n′), λg′.pn ≈ λs′.pn′, which again reduces to just pn ≃ pn′. We must also check that the initial metacontinuation and state are related by ≍, i.e., that for r ∼_{ω} r′, and q, s arbitrary,

ψ^{n} (ϕ^{n} (λx.a_{0}))r ≃ (ψ^{s} (ϕ^{s} (λx.λq.λs.a_{0})))r′ q s

And that is true since both sides simplify to a_{0}.

Finally, taking the lines marked with (∗) above together in sequence, using the transitivity of ≃, gives us the desired result.

And finally, taking this theorem together with Chapter 3, with nontermination as the notion of ambient effects, and simply diverging for effect-typing errors, we get:

Corollary 4.29 Let L_{⊥} be the partiality interpretation of L_{1}^{μ}, and let T be a monad in (L_{0}, L_{⊥}). Then we can pick a state type σ in L¯⋆ ; 1 such that for any complete L_{0}^{T} -program · ⊢ M :˚ ι,

L_{⊥}[[[[M]]_{T}]](•) = L_{⊥}[[[[M′]]_{S} (λn.λs′.˚ n)(ϕ^{s} (λx.⊥))]](•)

where M′ is a term of L_{1}^{CS_{σ}} obtained syntactically from M by (1) erasing all the levels on value-inclusions and lets, and (2) defining μ^{T}( ) and [ ]^{T} in terms of escapes, state, embeddings, and the term constructors of T.

Proof. In the partiality semantics, with relation lifting as the computation-extension, two closed terms of type˚ ι are related by ≃ =˚ (ι^{r}) iff their denotations are equal in the model (so in particular, ≃ is an equivalence relation).

First, let T_{0} = I be the identity monad (Example 2.16) and U_{0} = K_{˚ ι} the continuation monad with answer type ˚ ι (Definition 3.4). Then by Lemma 3.5 there is a monad morphism h from T_{0} to U_{0} defined as follows:

h_{α} = λm^{˚ α}.λγ^{α→˚ ι}.let˚ a ⇐ m in γ a

From this, Proposition 3.27 gives us a monad relation between I and K_{˚ ι}, mapping a relation R ∈ ARel(α, α′) to R ∈ CARel(˚ α, (α →˚ ι) →˚ ι) by:

m ( R) m′ ⟺ h m = λγ.let˚ x ⇐ m in γ x ((R → ≃)→ ≃) m′
⟺ ∀γ, γ′. (∀a R a′. γ a ≃ γ′ a′) ⇒ let˚ x ⇐ m in γ x ≃ m′ γ′

Hence, by Proposition 3.40, we get a relational correspondence between the interpretations given by L_{s}[[ ]] = L_{⊥}[[ ]] and L_{i}[[ ]] = L_{⊥}[[[[ ]]_{C}]], with computation-extension˚ R taken as the R defined above.

Theorem 3.38, with ℵ taken as an enumeration of all closed L_{0}^{T} -types, now gives us that, in the correspondence between L_{s} and L_{i},

[[M]]_{T} (˚ ι^{r}) [[M_{1}]]_{K}

where M_{1} in L_{1}^{K_{˚ T (Σℵ)}} is obtained from M by defining μ^{T}( ) and [ ]^{T} in terms of μ^{K}( ) and [ ]^{K}, the components of T, and the operations for embedding-types. Using ι^{r} as our R above, with γ = γ′ = λx.˚ x, we thus get in the original correspondence:

[[M]]_{T} = let˚ x ⇐ [[M]]_{T} in (λx.˚ x)x ≃ [[[[M_{1}]]_{K}]]_{C} (λx.˚ x)

And from this, we get the desired result directly by Theorem 4.28 with p = λx.˚ x and a_{0} = ⊥_{˚ ι}.

4.5 ML implementation and examples

In this section we illustrate how the abstract construction presented so far can be transcribed into runnable code. To emphasize the typing issues involved, we use the New Jersey dialect of Standard ML [AM91] as our concrete language, but the operational content should translate straightforwardly into Scheme as well (though instantiation to different monads may be less convenient without a “parameterized module” facility). We also give several examples; the reader may want to compare these with Wadler’s presentation [Wad92b].

4.5.1 Composable continuations

In SML/NJ, first-class continuations have a type distinct from the type of general procedures. Let us therefore first set up a Scheme-style representation of such continuations as non-returning procedures (this is not essential but makes for a more direct correspondence with the development in Section 4.3):

   signature signature signature ESCAPE =
      sig sig sig
         type type type void
         val val val coerce : void -> 'a
         val val val escape : (('1a -> void) -> void) -> '1a
      end end end;
   structure structure structure Escape : ESCAPE =
      struct struct struct
         datatype datatype datatype void = VOID of of of void
         fun fun fun coerce (VOID v) = coerce v
         fun fun fun escape f = callcc (fn fn fn k => coerce (f (fn fn fn x => throw k x)))
      end end end;

For example, we can write

   let let let open open open Escape
   in in in 3 + escape (fn fn fn k => k (6 + coerce (k 1))) end end end;
   (* val it = 4 : int *)

(The use of void and coerce instead of an unconstrained type variable in Escape permits continuations to be stored in ref-cells while staying within the ML type system [HDM93].)

Now we can define a composable-continuations facility, parameterized by the type of final answers (using Definition 4.25 and Lemma 4.4):

   signature signature signature CONTROL =
      sig sig sig
         type type type ans
         val val val reset : (unit -> ans) -> ans
         val val val shift : (('1a -> ans) -> ans) -> '1a
      end end end;
   functor functor functor Control (type type type ans) : CONTROL =
      struct struct struct

104 CHAPTER 4. IMPLEMENTING CONTINUATION-EFFECTS

open open open Escape
exception exception exception MissingReset
val val val mk : (ans -> void) ref = ref (fn fn fn _=> raise raise raise MissingReset)
fun fun fun abort x = (!mk x)
type type type ans = ans
fun fun fun reset t =
   escape (fn fn fn k => let let let val val val m = !mk
                   in in in mk := (fn fn fn r => (mk := m; k r));
                      abort (t()) end end end)
fun fun fun shift h =
   escape (fn fn fn k => abort (h (fn fn fn v => reset (fn fn fn ()=>coerce (k v)))))
end end end;

For example,

structure structure structure IntCtrl = Control (type type type ans = int);
let let let open open open IntCtrl
in in in 1 + reset (fn fn fn () => 2 * shift (fn fn fn k => k (k 10))) end end end;
(* val it = 41 : int *)

4.5.2 Monadic reflection

Building on the composable-continuations package, we implement the construction of Section 3.3.5. The signature of a monad is simple:

signature signature signature MONAD =
sig sig sig
type type type 'a t
val val val unit : 'a -> 'a t
val val val ext : ('a -> 'b t) -> 'a t -> 'b t
val val val show : string t -> string
end end end;

(The monad laws have to be verified manually, though.) The component show is included in the signature for convenience only. We require it to satisfy show ∘ unit = id; on terms that do not factor through unit, it provides an informal string-based representation of the effect if possible. It might at first seem more general to parameterize over types, i.e., have a show': ('a -> string) -> 'a t -> string, but we can recover that as fn ms=>fn t=>show (ext (unit o ms) t). Our goal is to define reflection and reification operations for an arbitrary monad M to get

signature signature signature RMONAD =
sig sig sig
structure structure structure M : MONAD
val val val reflect : '1a M.t -> '1a
val val val reify : (unit -> '1a) -> '1a M.t
val val val run : (unit -> string) -> string
end end end;

Here, run is again mostly for illustration purposes: it takes a suspended string-returning computation and returns the result of executing it, annotated by an external representation of its computational effects, if any.

Using Control we can now define a representation of the continuation monad for an arbitrary answer type (Lemma 4.3, but simplified because of level-erasure):

   functor functor functor ContMonad (type type type answer) : MONAD =
      struct struct struct
         type type type 'a t = ('a -> answer) -> answer
         fun fun fun unit a = fn fn fn k => k a
         fun fun fun ext f t = fn fn fn k => t (fn fn fn a => f a k)
         fun fun fun show t = raise raise raise Fail "show not defined"
      end end end;
   functor functor functor ContRep (type type type answer) : RMONAD =
      struct struct struct
         structure structure structure C = Control (type type type ans = answer)
         structure structure structure M = ContMonad (type type type answer = answer)
         val val val reflect = C.shift
         fun fun fun reify t = fn fn fn k => C.reset (fn fn fn () => k (t ()))
         fun fun fun run t = raise raise raise Fail "run not defined"
       end end end;

(where show and run cannot be defined when the answer type is unknown).

To implement the general construction, we also need to somehow represent the infinitary embedding type from Section 3.3.4. This might at first seem fundamentally incompatible with SML’s type system, especially if we want a “parametric” solution, independent of the collection of available base types and type constructors. But the construction only requires us to exhibit an embedding for those types at which we actually perform a reification. Thus, all we need is what could be called a “generative type dynamic”: a structure matching

   signature signature signature DYNAMIC =
      sig sig sig
         type type type dyn
         val val val newdyn : unit -> ('1a -> dyn) * (dyn -> '1a)
      end end end;

such that for any monotype '1a, an invocation of newdyn () returns a pair of functions (to_d, from_d) with from_d ∘ to_d equal to the identity on '1a. This signature can actually be implemented type-safely in SML, by exploiting the fact that the standard datatype exn (nominally of exception names, but useful for other purposes as well) can be dynamically extended with new summands:

   structure structure structure Dynamic : DYNAMIC =
      struct struct struct
         exception exception exception Dynamic
         abstype abstype abstype dyn = DYN of of of exn
         with with with fun fun fun newdyn () =
            let let let exception exception exception E of of of '1a
            in in in (fn fn fn a => DYN (E a), fn fn fn DYN (E a) => a | _=> raise raise raise Dynamic) end end end

end end end
end end end;

Note that we never actually raise or handle the exception E anywhere; we only use it as a dynamically-allocated tag.

Remark 4.30 Encoding dynamic types in terms of exception names is probably the most efficient approach in SML/NJ (short of bypassing the type system entirely via System.Unsafe.cast), but we do not actually depend on existence of an “extensible datatype” for the construction. In fact, we can get the same effect by representing a value of type dyn as a procedure unit -> unit, setting a specific cell to the desired value:

structure structure structure Dynamic' : DYNAMIC =
struct struct struct
exception exception exception Dynamic
abstype abstype abstype dyn = DYN of of of unit -> unit
with with with fun fun fun newdyn () =
   let let let val val val r = ref NONE
   in in in (fn fn fn a => DYN (fn fn fn () => r := SOME a),
       fn fn fn (DYN d) =>
         (r := NONE; d ();
          case case case !r of of of SOME a => a | NONE => raise raise raise Dynamic)) end end end
end end end
end end end;

However, this needlessly builds a closure for the dynamic value, and is perhaps a bit more obscure than the exn-based definition above.

We can now complete the construction (Theorem 3.38):

functor functor functor Represent (structure structure structure M : MONAD) : RMONAD =
struct struct struct
structure structure structure CR = ContRep (type type type answer = Dynamic.dyn M.t)
structure structure structure M = M
fun fun fun reflect m = CR.reflect (fn fn fn k => M.ext k m)
fun fun fun reify t =
   let let let val val val (to_d, from_d) = Dynamic.newdyn ()
   in in in M.ext (M.unit o from_d) (CR.reify t (M.unit o to_d)) end end end
fun fun fun run t = M.show (reify t)
end end end;

4.5.3 Example: exceptions

Example 1.5 from the Introduction becomes, in the concrete setting of our ML-based implementation:

structure structure structure ErrorMonad =
struct struct struct
datatype datatype datatype 'a t = SUC of of of 'a | ERR of of of string
val val val unit = SUC

4.5. ML IMPLEMENTATION AND EXAMPLES 107

fun fun fun ext f (SUC a) = f a
  | ext f (ERR s) = (ERR s)
fun fun fun show (SUC a) = a
  | show (ERR s) = "<Error: " ^s ^">"
end end end;
functor functor functor ErrorOps (structure structure structure R : RMONAD sharing sharing sharing R.M = ErrorMonad) :
sig sig sig
val val val myraise : string -> '1a
val val val myhandle : (unit -> '2a) -> (string -> '2a) -> '2a
end end end =
struct struct struct
open open open ErrorMonad
fun fun fun myraise e = R.reflect (ERR e)
fun fun fun myhandle t h = case case case R.reify t of of of SUC a => a | ERR s => h s
end end end;

Note that the operations myhandle and myraise are defined generically in terms of any valid implementation of reflection and reification for the exception monad. For example, since SML already has exceptions we could simply take

structure structure structure ErrorRep' : RMONAD =
struct struct struct
exception exception exception Exc of of of string;
structure structure structure M = ErrorMonad open open open M
fun fun fun reflect (SUC a) = a
  | reflect (ERR e) = raise raise raise Exc e
fun fun fun reify t = SUC (t ()) handle handle handle Exc e => ERR e
fun fun fun run t = show (reify t)
end end end;

We can, however, also plug in the “canonical” definitions obtained from Represent:

structure structure structure ErrorRep = Represent (structure structure structure M = ErrorMonad)
structure structure structure FX = ErrorOps (structure structure structure R = ErrorRep) open open open FX;
fun fun fun mydiv (x,y) = if if if y = 0 then then then myraise "Div0" else else else x div y;
(* val mydiv : int * int -> int *)
ErrorRep.run (fn fn fn () => makestring (1 + mydiv (100, 3)));
(* val it = "34" : string *)
ErrorRep.run (fn fn fn () => makestring (1 + mydiv (100, 0)));
(* val it = "<Error: Div0>" *)
ErrorRep.run (fn fn fn () => myhandle (fn fn fn () => makestring (1 + mydiv (100, 0)))
                          (fn fn fn s => "Oops: " ^s));
(* val it = "Oops: Div0" *)

The type inferred for myraise above is actually overly conservative wrt. weakness: since an exception-raising operation never returns normally in the first place, it is safe to give it a fully polymorphic type. We can achieve this by simply changing the defin-

ition of myraise to Escape.coerce (reflect (ERR e)). Unfortunately, myhandle is

also only weakly polymorphic, which can be traced back to the fact that reify in the functor Represent has a weakly polymorphic type (and that itself is a consequence of its definition in terms of Dynamic.newdyn).

It is instructive to inspect the expansion of myraise and myhandle into the underlying state and continuation manipulations: the cell allocated for the metacontinuation in Control effectively contains the “current handler continuation”, which is invoked by a raise and temporarily rebound in the scope of each new handle. This is very much like the way exceptions are actually implemented in SML/NJ, although the details are not quite the same: an exception-specific implementation can take advantage of particular operational properties of the monad (notably that handler continuations are invoked at most once) to optimize the generic construction a bit.

4.5.4 Example: state

The state monad is straightforward:

functor functor functor StateMonad (type type type state) : MONAD =
struct struct struct
type type type 'a t = state -> 'a * state
fun fun fun unit a = fn fn fn s => (a,s)
fun fun fun ext f t = fn fn fn s => let let let val val val (a,s') = t s in in in f a s' end end end
fun fun fun show t = raise raise raise Fail "not defined"
end end end;
structure structure structure IntStateMonad : MONAD =
struct struct struct
structure structure structure S = StateMonad (type type type state = int) open open open S
fun fun fun show t =
   let let let val val val (a,s') = t 42
   in in in if if if s' = 42 then then then a else else else "<s: " ^makestring s' ^"> " ^a end end end
end end end
functor functor functor IntStateOps (structure structure structure R : RMONAD sharing sharing sharing R.M = IntStateMonad) :
sig sig sig
val val val store : int -> unit
val val val fetch : unit -> int
val val val tick : unit -> unit
end end end =
struct struct struct
fun fun fun store n = R.reflect (fn fn fn s => ((),n))
fun fun fun fetch () = R.reflect (fn fn fn s => (s,s))
fun fun fun tick () = R.reflect (fn fn fn s => ((),s+1))
end end end
structure structure structure IntStateRep = Represent (structure structure structure M = IntStateMonad)
structure structure structure FX = IntStateOps (structure structure structure R = IntStateRep) open open open FX;
IntStateRep.run (fn fn fn () => (store 5; tick ();
                     let let let val val val x = fetch ()
                     in in in tick (); makestring (2 * x) end end end));
(* val it = "<s: 7> 12" *)

Here the general construction is clearly wasteful, however: we could easily have represented the state monad without using callcc at all. This is also true for many other “state-like” monads, such as I/O or complexity. Thus, the real value of the general construction is when the decomposition into escapes and state is not immediately apparent, as in the following examples.

4.5.5 Example: nondeterminism

A nondeterministic computation can be represented as a list of answers. (Formally, this goes beyond the monads considered in Chapter 3, but extending the proof to a language with inductive datatypes such as lists is straightforward.)

   structure structure structure ListMonad : MONAD =
      struct struct struct
         type type type 'a t = 'a list
         fun fun fun unit a = [a]
         fun fun fun ext f [] = []
           | ext f (h::t) = f h @ ext f t
         fun fun fun show [] = "<fail>"
           | show [x] = x
           | show (h::t) = h ^" <or> " ^show t
      end end end;
   functor functor functor ListOps (structure structure structure R : RMONAD sharing sharing sharing R.M = ListMonad) :
      sig sig sig
         val val val pick : '1a list -> '1a
         val val val fail : unit -> '1a
         val val val results : (unit -> '1a) -> '1a list
      end end end =
      struct struct struct
         fun fun fun pick l = R.reflect l
         fun fun fun fail () = R.reflect []
         fun fun fun results t = R.reify t
      end end end;
   structure structure structure ListRep = Represent (structure structure structure M = ListMonad)
   structure structure structure FX = ListOps (structure structure structure R = ListRep) open open open FX;
   ListRep.run (fn fn fn () => let let let val val val x = pick [3,4] * pick [5,7]
                         in in in if if if x >= 20 then then then makestring x else else else fail () end end end);
   (* val it = "21 <or> 20 <or> 28" : string *)

More generally, we get Haskell-style list comprehensions “for free”, in that the schema

[E | x_{1} ← E_{1}; …; x_{n} ← E_{n}]

(where each x_{i} may be used in E_{i+1}, …, E_{n} and in E) can be expressed directly as

[let x_{1} = μ(E_{1}) in … let x_{n} = μ(E_{n}) in E]

Of course, this is probably not the most efficient way of implementing list comprehensions in ML. As observed by Wadler, however, list comprehensions can be generalized to arbitrary monads [Wad92a]; similarly we get general monad comprehensions in ML simply by supplying the appropriate [ ] and μ( ) operations.

4.5.6 Example: probability

A slight refinement of the nondeterminism monad permits us to keep track not only of the possible outcomes of a nondeterministic evaluation, but also their relative probabilities, given a distribution on the individual choice operations. That is, a probabilistic computation of type α is represented by a finite set of pairs (a_{i}, p_{i}), where a_{i} is a value of type α, p_{i} ∈ (0, 1], all the a_{i} are distinct, and the p_{i} sum to 1.

However, this example also illustrates a technical problem with monads as a structuring tool for functional programs, as opposed to describing programming language semantics: the definition of a monad requires that the operations η and ^{∗} be defined uniformly at all types, but in general we cannot properly implement sets of higher-order values because elements of such type cannot be tested for equality.

For example, we cannot algorithmically identify two probabilistic computations like {(λx. x, 1)} and {(λx. x, 0. 5), (λx. x, 0. 5)}, even though both represent the same “definite” identity function. Note that the latter variant can easily arise even if we do not allow explicit non-deterministic choice at higher types – consider a source term like let y = amb (3, 4) in λx. x + y − y.

While this non-uniqueness is not in itself a problem – after all, we cannot observe functions directly – we need to ensure that any ground-type result we may obtain by a series of applications of potentially higher-order probabilistic functions is still uniquely represented. An easy way of achieving this is to always represent “active” probabilistic computations non-uniquely using list-nondeterminism, but then only expose reification at types for which we can eliminate duplicates:

   abstraction abstraction abstraction ProbMonad :
      sig sig sig
         include include include MONAD
         val val val to_t : ('a * real) list -> 'a t
         val val val from_t : ''a t -> (''a * real) list
      end end end =
      struct struct struct
         type type type 'a t = ('a * real) list  (* 0.0<p<=1.0; sum(p) = 1.0 *)
         fun fun fun unit a = [(a,1.0)]
         fun fun fun ext f ([]:'a t) = []
           | ext f ((a,p) :: t) = map (fn fn fn (b,q) => (b,p*q)) (f a) @ ext f t
         fun fun fun show' [(a, 1.0)] = a
           | show' [] = ""
           | show' ((a,p) :: t) = "<p: " ^makestring p ^">" ^a ^show' t
         fun fun fun to_t l = l  (* could do some sanity checking here *)
         fun fun fun tally (a,p) ([]:''a t) = [(a,p)]
           | tally (a,p) ((a',p') :: t) =
                if if if a = a' then then then (a,p+p') :: t else else else (a',p') :: tally (a,p) t
         fun fun fun from_t t = fold (fn fn fn (h,l) => tally h l) t []
         fun fun fun show t = show' (from_t t)
      end end end;
   functor functor functor ProbOps (structure structure structure R : RMONAD sharing sharing sharing R.M = ProbMonad) :
      sig sig sig
         val val val choose : ('1a * real) list -> '1a

         val val val flip : real -> bool
         val val val distribution : (unit -> ''1a) -> (''1a * real) list
      end end end =
      struct struct struct
         fun fun fun choose l = R.reflect (ProbMonad.to_t l)
         fun fun fun flip p = if if if p <= 0.0 then then then false
                      else else else if if if p >= 1.0 then then then true
                      else else else choose [(true,p), (false,1.0-p)]
         fun fun fun distribution t = ProbMonad.from_t (R.reify t)
      end end end;
   structure structure structure ProbRep = Represent (structure structure structure M = ProbMonad)
   structure structure structure FX = ProbOps (structure structure structure R = ProbRep) open open open FX;
   ProbRep.run (fn fn fn () => if if if flip 0.3 = flip 0.3 then then then "same" else else else "diff");
   (* val it = "<p: 0.58>same<p: 0.42>diff" : string *)

Here we have used the SML/NJ abstraction extension to hide the implementation of the type t; an analogous effect could be achieved, slightly more verbosely, using the standard abstype construct. Also, strictly speaking, the above only gives us uniqueness up to permutation; to get a truly unique representation we actually need the type ''a to be linearly orderable, not only supporting an equality predicate.

We can use probabilistic effects to solve “textbook problems” such as finding the distribution of the total number of heads in n tosses of a biased coin:

   fun fun fun toss p 0 = 0
     | toss p n = if if if flip p then then then 1+toss p (n-1) else else else toss p (n-1);
   (* val toss = fn : real -> int -> int *)
   distribution (fn fn fn () => toss 0.3 5);
   (* val it = [(0,0.16807), (1,0.36015), (2,0.3087), (3,0.1323),
                (4,0.02835), (5,0.00243)] : (int * real) list *)

Of course, in this particular case, there already exists a simple analytic solution, but the “probabilistic execution” approach also handles less regular experiment protocols, where very dissimilar branches may be taken depending on outcomes of probabilistic choices.

Note that the simulation keeps track of all possible computation paths, at a potentially exponential cost in computation time. In cases where the same net outcome can be achieved in many different ways (as in the example above), it is therefore often useful to add an explicit wrapper,

                      choose (distribution (fn () => E))

around such a subcomputation E. This has no effect on the result computed (almost by definition: it is an instance of the principle μ([E]) = E), but it improves efficiency by consolidating computation paths in a manner analogous to dynamic programming.

4.5.7 Example: continuations

Finally, let us consider the continuation monad (for an arbitrary but fixed answer type). This lets us define both escapes and composable first-class continuations. We already have the functor ContMonad. Let us create a specific instantiation:

structure structure structure StringContMonad : MONAD =
struct struct struct
structure structure structure S = ContMonad (type type type answer = string) open open open S
fun fun fun show t = t (fn fn fn x => x)
end end end
functor functor functor StringContOps (structure structure structure R : RMONAD sharing sharing sharing R.M = StringContMonad):
sig sig sig
val val val mycallcc : (('1a -> '1b) -> '1a) -> '1a
val val val myshift : (('1a -> string) -> string) -> '1a
val val val myreset : (unit -> string) -> string
end end end =
struct struct struct
fun fun fun mycallcc h =
  R.reflect (fn fn fn k => let let let fun fun fun c a = R.reflect (fn fn fn k' => k a)
                     in in in R.reify (fn fn fn () => h c) k end end end)
fun fun fun myshift h =
  R.reflect (fn fn fn k => R.reify (fn fn fn () => h k) (fn fn fn x => x))
fun fun fun myreset t =  R.reify t (fn fn fn x => x)
end end end;
structure structure structure StringContRep = Represent (structure structure structure M = StringContMonad)
structure structure structure FX = StringContOps (structure structure structure R = StringContRep) open open open FX;
StringContRep.run (fn fn fn () => makestring (3 + mycallcc (fn fn fn k => 6 * k 1)));
(* val it = "4" : string *)
StringContRep.run (fn fn fn () => "a" ^myreset (fn fn fn () =>
                               "b" ^myshift (fn fn fn k => k (k "c"))));
(* val it = "abbc" : string *)

4.6 Related work

Different notions of functional or composable continuations have been studied by a number of researchers. Early work [JD88, FWFD88, DF90] presumed explicit support from the compiler or runtime system for the actual implementation, such as the ability to mark or splice together delimited stack segments. However, an encoding in standard Scheme of one variant was devised by Sitaram and Felleisen [SF90]. Still, this embedding was quite complex, relying on dynamically-allocated, mutable data structures, eq?-tests, and the dynamic typing of Scheme.

Another explicitly Scheme-implementable notion of partial continuations was proposed by Queinnec and Serpette [QS91]; the code required is perhaps even more intricate. And more recently, an implementation of a related construct in Standard ML of New Jersey was presented by Gunter, Rémy and Riecke [GRR95].

At least initially, most of these operators appear more general than monadic reflection for continuations, but it is not clear if the additional expressive power is sufficiently useful in practice to justify their fairly complex implementations. The much simpler construction presented in this chapter uses only a single, statically-typed cell holding a continuation, perhaps the minimal increment over call/cc alone.

Much more significantly, however, this implementation is directly derived from and related to the original specification; other efforts gave at most an informal argument that the (usually operationally specified) control construct was correctly implemented by the code. Given the relatively complex correctness proof for even the very simple control operators used in this chapter (A and #), it is not likely that any of the alternatives would be easier to verify.

The term metacontinuation, with a fairly broad meaning, was first used in giving a formal semantics to a notion of computational reflection by Wand and Friedman [WF88]. The more restrictive usage of the term, where the metacontinuation actually arises from a standard continuation-passing transform of an “almost-cps” term, is due to Danvy and Filinski [DF90].

The further observation that the metacontinuation can be represented by a storage cell was first exploited in a preliminary version of the present work [Fil94]. An application of this technique for continuation-based partial evaluation was reported by Lawall and Danvy, who found that a call/cc-based implementation of composable continuations uniformly outperformed the equivalent explicit continuation-passing translation, especially with respect to heap usage [LD94].

The main difference between the variant of composable continuations considered in this chapter and the previous formulations is that we start with an even more abstract specification of the original operators, distinguishing in the type system between computations with and without control effects. Correspondingly, the definitional translation only has a non-trivial effect on computations of the former kind.

This distinction gives us a very simple correspondence between composable continuations and monadic reflection for the continuation monad, further motivating composable continuations as the canonical control effect. (The change was also partially necessitated by the introduction of ambient effects; in [Fil94], the target language of the definitional translation was assumed to be effect-free in the present terminology.)

Conclusions

5.1 Summary

We have analyzed a new approach to incorporating computational effects in a functional language. In many ways, it combines the best features of the existing “purely functional” and “imperative” models for effects, as well as providing a basis for introducing effects incrementally. Let us recapitulate the main properties of the construction:

Convenience. An important advantage of monadic reflection is the ease with which it fits into the familiar programming paradigm of ML-like languages. There is essentially no up-front cost: programs do not have to be (re)written in any particular style, the effects used do not have to be settled upon in advance, and we can directly use the existing type checker, module system, etc.

In fact, there is no need to even explicitly mention monads when writing the bulk of the program. Typically, the programmer simply defines the desired operations (such as raise and handle for exceptions, pick and results for nondeterminism, or spawn and yield for resumptions) using monadic reflection for a suitable monad, then expresses the program in terms of those new primitives alone.

The visible difference from a “manual” implementation of the effects in terms of continuations and state (or, even more markedly, as part of the compiler) is the amount of effort and ingenuity required. Usually, the monad specification consists of only a few lines of simple, effect-free code. Likewise, the exported operations are generally a simple combination of the reflection and/or reification operators. We never have to think about capturing, storing, retrieving, and invoking continuations to implement, say, a backtracking search; all the required low-level code is synthesized mechanically from an abstract specification of nondeterministic choice.

Ease of reasoning. Despite its apparent “imperative” nature, monadic reflection can equally well be viewed as a technique for writing “purely functional” programs in a more concise notation, much like monad comprehensions [Wad92a]. In fact, any imperative program fragment is extensionally equivalent to its monadic-style counterpart, in the sense that there exist language-definable isomorphisms between the two representations.

A crucial point, however, is that this correspondence to monadic style is a means, not an end, for reasoning about programs. Simply “being expressible with a monad”, or “having a translation into purely functional code” are vacuous properties, true of any program using continuations and state (since both are monadic effects), and do not help us prove anything new. Rather, we must exploit the knowledge that a program is expressible with a particular monad, with a more restrictive notion of effects than the continuation-state monad into which it happens to be embedded for implementation purposes.

For example, in an ML-like language defined by exception-passing on top of partiality, it is easy to argue correctness of a source-level transformation such as f x + f x = 2 × f x: the subcomputation f x must either succeed with a value, raise an exception, or diverge; in all three cases, the two expressions are equivalent. On the other hand, if we examine only a hand-coded implementation of exceptions in terms of escapes and state – even if the latter effects are used for no other purpose in the program – we cannot argue nearly as directly that common-subexpression elimination is a valid optimization principle.

Efficiency. Execution efficiency is an important concern for practical uses of effects, and monadic reflection usually fares significantly better than an actual translation into monadic style. If effects are rare, programs run at full speed without the overhead of explicitly performing the administrative manipulations specified by the monad, such as tagging and checking return values for exceptions.

To ensure good performance of the reflection and reification operators as well, we do need to assume a reasonably efficient implementation of call/cc in the host language. In cps-based compilers, providing a cheap first-class continuation facility is generally straightforward [App92]. And even in stack-based implementations, good techniques exist for keeping at least the amortized cost per call/cc acceptably low [HDB90].

Still, if a particular effect is heavily used, it may be preferable to rewrite the program in the corresponding monadic style. For example, if the parameter provided by an environment monad changes very frequently, we should make it an explicit argument to all functions using it. Not only is this likely to be faster than going through the store on every access, but it will probably result in a clearer program as well. Conversely, of course, rarely-used arguments can be made implicit, improving both execution speed and clarity – the latter by focusing attention on the few cases where some value changes, rather than on all the ones where it is merely propagated.

In either case, however, the changeover need not be done all at once, because we can use reflection and reification to interface between program fragments using the two approaches. Indeed, the best solution may well be to make the effect explicit in parts of the program that use it heavily, and implicit in those that are not directly affected by it.

5.2 Future work

Several opportunities for extensions and future investigation arise naturally:

Recursive types in the specification language. Even though our language for defining monadic effects was simply typed, there do not appear to be any fundamental problems in allowing general recursive types. In fact, the logical-relations proofs in Chapter 4 already handle recursion in the answer type for the continuation monads using invariant relations, and similar techniques could in all likelihood be used in Chapter 3 as well.

However, a proper treatment of recursive types would probably include more than merely adding the μ-types from Section 3.2.1 to the specification language. For example, it might be appropriate to also allow recursively-defined computationtypes, i.e., types of the form μb. β, with an explicit notion of computation-type variables and the associated extensions to generalized let, etc.

Even more important, we would want a general treatment of recursive monad specifications, such as used in the continuation-passing translation of Definition 3.34. The required structure seems to be an L_{0}-monad in the usual sense, but parameterized by an L_{0}^{⋆}-type. This would allow us to express, for example, ML-style ref-cells storing procedures, or exceptions carrying non-ground data, without introducing explicit isomorphisms.

Layering effects. Although its potential was not fully realized in this thesis, the organization in terms of ambient and focus effects should generalize directly to multiple, layered effects. In other words, we should be able to integrate different notions of effects in a single language by a series of nested monadic translations, at each step taking the previous focus effect as the new notion of ambient effect.

Moreover, this layered strategy for modularly specifying effects promises to generalize to a modular implementation of such effects in terms of continuations and state. More specifically, we would first relate a heterogeneous tower of monads to a tower of continuation-monads (applying at each level the construction in Chapter 3), then flatten this cps tower into a single-level implementation (as in Chapter 4), with a collection of cells, each holding one meta-continuation of the hierarchy.

Indeed, an apparently-working implementation based on this strategy already exists, and preliminary investigations into both its theoretical justification and practical usefulness have been very encouraging. However, time constraints made it infeasible to include a treatment this generalized construction in the thesis. Fully formalizing and analyzing the multiple-effect case is therefore left as future work.

Practical effect-typing for monadic effects. While one of the goals of the construction was to permit a direct embedding of the effect-enriched language into ML, this does not mean that we could not take advantage of a more refined type system. Some discipline is required when writing programs with effects, and it would be useful to detect violations of effect-stratification statically, rather than during program execution.

Accordingly, there should be a way to optionally make the effects used by a piece of code manifest in its type, especially at module boundaries. We could of course achieve this by always exporting procedures in their “fully reified” form. Such an approach, however, tends to be impractically verbose, and the additional conversions, although semantically transparent, may impose a non-negligible overhead. We would want a concise and unobtrusive way of representing that same information in direct style. Existing work in this area tends to consider mainly low-level notions of effects (jumps and state manipulation) [JG89, KJLS87], rather than application-specific, higher-level concepts. But given the often complex relationship between a monadic specification and its imperative implementation, it seems highly unlikely that an automated analysis based on the latter would be able to detect a higher-level pattern such as an exception-handling system. Moreover, current effect-type systems are generally phrased in terms of Curry-style type inference (i.e., with the semantics of a program given a priori, and independently of its type). The reflection-based approach to effects, on the other hand, also seems well suited for Church-style type reconstruction (where type information is considered an inherent part of the program, only elided for conciseness), as already advocated for ML in [HM93].

5.3 Closing remarks

Perhaps the most concise way of stating the main conclusion of this work is that a functional program can and should distinguish between specification and implementation of computational effects – as it already would for any other abstract data type. Oversimplifying grossly, we could summarize the alternatives by following Hegelian triad:

• Thesis: the implementation is the specification. The meaning of an effect is fully determined by a reference implementation. For example, a Scheme program could be written with intuitive but informal abstractions such as error handlers, backtracking, or threads, ultimately defined only by their expansions into call/cc and set!.

• Antithesis: the specification is the implementation. The behavior of an effect is fully determined by a purely functional executable specification. For example, a Haskell program could be written in monadic style, expanding into explicit exception-passing, success lists, or resumptions.

• Synthesis: the implementation is related to the specification. An effect has a declarative meaning and an imperative behavior, with the latter obtained from the former in a systematic, but not necessarily direct way. For example (but by no means exclusively), a program could be written and analyzed in terms of monadic reflection, but eventually executed using effects built out of escapes and state.

In other words, the tension between Haskell-style monads and Scheme-style primitive effects need not and should not be resolved in unilateral favor of one or the other; it is precisely through their interplay that the best qualities of both are exposed.

Appendix A

Properties of the Predomain Model

In this chapter we summarize a few auxiliary results about the predomain semantics, needed in Chapters 3 and 4, but somewhat tangential to the main development. Most are fairly simple adaptations of standard domain-theoretic results to our predomain setting.

A.1 Recursive type definitions

The proof that all recursive type equations have solutions in the predomain semantics hinges on exhibiting for any type constructor a suitable functorial action in the category of domains and strict continuous functions. That is, in addition to the evident action on objects, we need an action on morphisms.

Although we could construct such functors directly in the model, using the standard notation for continuous functions, it seems more convenient and consistent to use the existing term syntax for effects (fixed to be partiality) in the definitions, and only consider the denotations of the constructed terms in the end.

For the purposes of this appendix only, let us therefore extend our term syntax by introducing the additional computation-type constructor ^{⊥}α and term constructors ^{⊥}M and let^{⊥} x ⇐ M_{1} in M_{2} with types:

\frac{Γ ⊢ M : α}{Γ ⊢ ^{⊥}M : ^{⊥}α}  \frac{Γ ⊢ M_{1} : ^{⊥}α Γ, x: α ⊢ M_{2} : β}{Γ ⊢ let^{⊥} x ⇐ M_{1} in M_{2} : β}

analogous to the existing ambient effects, but always referring to the partiality monad. The let^{⊥} is actually more like the generalized let_{˚ β} than like let˚ , because the result can be of any computation-type. We omit the explicit type subscript in let^{⊥}, however, because we already have a uniform semantic characterization of its meaning at all pointed types:

L[[^{⊥}α]]^{θ} = L[[α]]^{θ}_{⊥}
L[[^{⊥}M]]^{θ}(ρ) = up(L[[^{⊥}M]]^{θ}(ρ))
L[[let^{⊥} x ⇐ M_{1} in M_{2}]]^{θ}(ρ) = (\underline{λ}a.L[[M_{2}]]^{θ}(ρ[x ↦ a]))^{‡}(L[[M_{1}]]^{θ}(ρ))

where f^{‡} is the generalized strict extension from Section 2.1.3(lifting).

For embedding-types, we also define a general case-construct, dispatching among all the possibilities in ℵ:

\frac{Γ ⊢ M : Σℵ ∀i ∈ I. Γ, x: ℵ(i) ⊢ M(i) : α}{Γ ⊢ case(M, i. x_{i}. M(i)) : α}

with semantics

L[[case(i. M, i. x_{i}. M(i))]]^{θ}(ρ) = L[[M(i)]]^{θ}(ρ[x_{i} ↦ a_{i}]) when L[[M]]^{θ}(ρ) = (i, a_{i})

Again, this case is never used in writing actual programs; it merely gives us a convenient way of referring to semantic entities in the predomain model.

Definition A.1 For any value-type α and computation-type β over {a} in L_{1} (i.e., L_{0}^{Σ} extended with an empty type), we define a type constructor Φ_{a.α}( , ) and a computationtype constructor Ψ_{a.β}( , ) by

Φ_{a.α}(α^{−}, α^{+}) = α{α^{−}/a^{−}, α^{+}/a^{+}}  and  Ψ_{a.β}(α^{−}, α^{+}) = β{α^{−}/a^{−}, α^{+}/a^{+}}

where α{α′/a^{+}} means α with α′ substituted for all positive occurrences of a, and analogously for negative occurrences.

Further, we define term constructors Φ_{a.α}( , ) and Ψ_{a.β}( , ) with types:

\frac{f^{−} : α_{1}^{−} →^{⊥}α_{2}^{−} f^{+} : α_{1}^{+} →^{⊥}α_{2}^{+}}{Φ_{a.α}(f^{−}, f^{+}) : Φ_{a.α}(α_{2}^{−}, α_{1}^{+}) →^{⊥}Φ_{a.α}(α_{1}^{−}, α_{2}^{+})}

\frac{f^{−} : α_{1}^{−} →^{⊥}α_{2}^{−} f^{+} : α_{1}^{+} →^{⊥}α_{2}^{+}}{Ψ_{a.β}(f^{−}, f^{+}) : Ψ_{a.β}(α_{2}^{−}, α_{1}^{+}) → Ψ_{a.β}(α_{1}^{−}, α_{2}^{+})}

as follows:

Φ_{a.a}(f^{−}, f^{+}) = λa.f^{+} a
Φ_{a.ι}(f^{−}, f^{+}) = λn.^{⊥}n
Φ_{a.1}(f^{−}, f^{+}) = λu.^{⊥}⟨⟩
Φ_{a.α_{1}×α_{2}}(f^{−}, f^{+}) = λp.let^{⊥} x_{1} ⇐ Φ_{a.α_{1}}(f^{−}, f^{+})(fst p) in let^{⊥} x_{2} ⇐ Φ_{a.α_{2}}(f^{−}, f^{+})(snd p) in ^{⊥}⟨x_{1}, x_{2}⟩
Φ_{a.0}(f^{−}, f^{+}) = λz.^{⊥}z
Φ_{a.α_{1}+α_{2}}(f^{−}, f^{+}) = λs. case(s, x_{1}.let^{⊥} y_{1} ⇐ Φ_{a.α_{1}}(f^{−}, f^{+})x_{1} in ^{⊥}(inl y_{1}), x_{2}.let^{⊥} y_{2} ⇐ Φ_{a.α_{2}}(f^{−}, f^{+})x_{2} in ^{⊥}(inr y_{2}))
Φ_{a.Σℵ}(f^{−}, f^{+}) = λs. case(s, i. x_{i}. let^{⊥} y_{i} ⇐ Φ_{a.ℵ(i)}(f^{−}, f^{+})x_{i} in ^{⊥}(in_{i} y_{i}))
Φ_{a.β}(f^{−}, f^{+}) = λb.^{⊥}(Ψ_{a.β}(f^{−}, f^{+})b)
Ψ_{a.˚ α}(f^{−}, f^{+}) = λm.let˚ x ⇐ m in let^{⊥} y ⇐ Φ_{a.α}(f^{−}, f^{+})x in˚ y
Ψ_{a.1}(f^{−}, f^{+}) = λu.⟨⟩
Ψ_{a.β_{1}×β_{2}}(f^{−}, f^{+}) = λp.⟨Ψ_{a.β_{1}}(f^{−}, f^{+})(fst p), Ψ_{a.β_{2}}(f^{−}, f^{+})(snd p)⟩
Ψ_{a.α→β}(f^{−}, f^{+}) = λg.λx.let^{⊥} y ⇐ Φ_{a.α}(f^{+}, f^{−})x in Ψ_{a.β}(f^{−}, f^{+})(g y)

A few simple properties of these definitions are:

Lemma A.2 (1) When a does not occur free in α or β then

Φ_{a.α}(f^{−}, f^{+}) = λa.^{⊥}a Ψ_{a.β}(f^{−}, f^{+}) = λb.b

More generally, (2) the type-directed actions are compositional:

Φ_{a.α}(Φ_{a.α′}(f^{+}, f^{−}), Φ_{a.α′}(f^{−}, f^{+})) = Φ_{a.α{α′/a}}(f^{−}, f^{+})
Ψ_{a.β}(Φ_{a.α′}(f^{+}, f^{−}), Φ_{a.α′}(f^{−}, f^{+})) = Ψ_{a.β{α′/a}}(f^{−}, f^{+})

Finally, (3) the definitions are functorial in the following sense:

Φ_{a.α}(λx.^{⊥}x, λx.^{⊥}x) = λa.^{⊥}a Ψ_{a.β}(λx.^{⊥}x, λx.^{⊥}x) = λb.b
Φ_{a.α}(λx.let^{⊥} y ⇐ f_{1}^{−} x in f_{2}^{−} y, λx.let^{⊥} y ⇐ f_{1}^{+} x in f_{2}^{+} y)
= λa.let^{⊥} r ⇐ Φ_{a.α}(f_{2}^{−}, f_{1}^{+})a in Φ_{a.α}(f_{1}^{−}, f_{2}^{+})r
Ψ_{a.β}(λx.let^{⊥} y ⇐ f_{1}^{−} x in f_{2}^{−} y, λx.let^{⊥} y ⇐ f_{1}^{+} x in f_{2}^{+} y)
= λb.Ψ_{a.β}(f_{1}^{−}, f_{2}^{+})(Ψ_{a.β}(f_{2}^{−}, f_{1}^{+})b)

(i.e., Φ_{a.α}( , ) is an endofunctor in the Kleisli category of the lifting monad, while Ψ_{a.β}( , ) is a functor from the Kleisli category to the underlying one).

Proof. Simple induction on α and β in all cases. Note, however, that verification of the value-product case of (3) relies on partiality being a commutative effect.

We can also define an “ordinary” functorial operation on functions between lifted types:

Definition A.3 When g^{−} and g^{+} are strict functions (i.e., rigid with respect to ^{⊥}- effects), we define the term constructor

\frac{g^{−} : ^{⊥}α_{1}^{−} →^{⊥}α_{2}^{−} g^{+} : ^{⊥}α_{1}^{+} →^{⊥}α_{2}^{+}}{Φ_{a.α}^{d}(g^{−}, g^{+}) : ^{⊥}Φ_{a.α}(α_{2}^{−}, α_{1}^{+}) →^{⊥}Φ_{a.α}(α_{1}^{−}, α_{2}^{+})}

by

Φ_{a.α}^{d}(g^{−}, g^{+}) = λm.let^{⊥} x ⇐ m in Φ_{a.α}(λx.g^{−} (^{⊥}x), λx.g^{+} (^{⊥}x))x

We then have:

Lemma A.4 Φ_{a.α}^{d}( , ) is functorial in the following sense:

Φ_{a.α}^{d}(id, id) = id
Φ_{a.α}^{d}(g_{2}^{−} ∘ g_{1}^{−}, g_{2}^{+} ∘ g_{1}^{+}) = Φ_{a.α}^{d}(g_{1}^{−}, g_{2}^{+}) ∘ Φ_{a.α}^{d}(g_{2}^{−}, g_{1}^{+})

Proof. Simple verification, using Lemma A.2(3):

Φ_{a.α}^{d}(id, id) = λm.let^{⊥} x ⇐ m in Φ_{a.α}(λx.^{⊥}x, λx.^{⊥}x)x = λm.let^{⊥} x ⇐ m in (λa.^{⊥}a)x

= λm.let^{⊥} x ⇐ m in ^{⊥}x = λm.m = id

Φ_{a.α}^{d}(g_{2}^{−} ∘ g_{1}^{−}, g_{2}^{+} ∘ g_{1}^{+}) = λm.let^{⊥} x ⇐ m in Φ_{a.α}(λa.g_{2}^{−} (g_{1}^{−} (^{⊥}a)), λa.g_{2}^{+} (g_{1}^{+} (^{⊥}a)))x

=^{†} λm.let^{⊥} x ⇐ m in Φ_{a.α}(λa.let^{⊥} y ⇐ g_{1}^{−} (^{⊥}a) in g_{2}^{−} (^{⊥}y), λa.let^{⊥} y ⇐ g_{1}^{+} (^{⊥}a) in g_{2}^{+} (^{⊥}y))x

= λm.let^{⊥} x ⇐ m in let^{⊥} r ⇐ Φ_{a.α}(λy.g_{2}^{−} (^{⊥}y), λa.g_{1}^{+} (^{⊥}a))x in Φ_{a.α}(λa.g_{1}^{−} (^{⊥}a), λy.g_{2}^{+} (^{⊥}y))r

= λm.let^{⊥} r ⇐ (let^{⊥} x ⇐ m in Φ_{a.α}(λy.g_{2}^{−} (^{⊥}y), λa.g_{1}^{+} (^{⊥}a))x) in Φ_{a.α}(λa.g_{1}^{−} (^{⊥}a), λy.g_{2}^{+} (^{⊥}y))r
= λm.Φ_{a.α}^{d}(g_{1}^{−}, g_{2}^{+})(Φ_{a.α}^{d}(g_{2}^{−}, g_{1}^{+})m) = Φ_{a.α}^{d}(g_{1}^{−}, g_{2}^{+}) ∘ Φ_{a.α}^{d}(g_{2}^{−}, g_{1}^{+})

where † uses that for a strict g,

g m = g (let^{⊥} x ⇐ m in ^{⊥}x) = let^{⊥} x ⇐ m in g (^{⊥}x)

Let us also recall some elementary properties of least fixed points:

Lemma A.5 Let fix_{B} : (B → B) → B denote the least-fixed-point operator for a pointed cpo B. Then

1. For any continuous f : B → B and g : B′ → B′, and strict continuous h : B′ → B with f ∘ h = h ∘ g, fix_{B}(f) = h(fix_{B′}(g)).

2. For any continuous f : B → B′ and g : B′ → B, fix_{B}(g ∘ f) = g(fix_{B′}(f ∘ g)).

Proof.

1. Follows directly from the definition of fix:

h(fix_{B′}(g)) = h(⨆_{i} g^{i}(⊥_{B′})) = ⨆_{i} h(g^{i}(⊥_{B′})) = ⨆_{i} f^{i}(h(⊥_{B′})) = ⨆_{i} f^{i}(⊥_{B})

= fix_{B}(f)

2. (We cannot simply use the above result here, because g is not necessarily strict.) Let x = fix_{B′}(f ∘ g) and y = fix_{B}(g ∘ f). First, since g(x) is a fixed point of g ∘ f (because (g ∘ f)(g(x)) = g((f ∘ g)(x)) = g(x)), we have y ⊑ g(x). Analogously, since f(y) is a fixed point of f ∘ g, x ⊑ f(y), and hence by monotonicity of g, g(x) ⊑ g(f(y)) = y. And thus, since ⊑ is a partial order, we get g(x) = y.

Although for the purposes of Chapter 3, all we need is a solution to the type equation (not necessarily the least one), for Chapter 4 we will also need that the relevant isomorphism satisfies an additional equational property:

Definition A.6 Let Cpo_{⊥} be the category of pointed cpos (domains) and strict continuous functions. Let F : Cpo_{⊥}^{op} × Cpo_{⊥} → Cpo_{⊥} be a functor (we call such an F a mixed functor in Cpo_{⊥}); it is locally continuous if its action on morphisms is continuous. A minimal invariant for F is an object X together with an isomorphism i : F (X, X) → X such that

fix_{X→X} (\underline{λ}h.i ∘ F (h, h) ∘ i^{−1}) = id_{X}

One can show that the standard inverse-limit construction for solving recursive domain equation actually yields minimal invariants:

Theorem A.7 Every locally continuous mixed functor in Cpo_{⊥} has a minimal invariant.

Proof. See [Pit99].

Using this, we get for our predomain language:

Corollary A.8 Every recursive type equation in L_{1} has a solution in the predomain model, i.e., for any parameterized type ⊢_{{a}} α type, there exists a cpo A with an isomorphism i : L[[α]]^{a ↦ A} → A. Moreover, interpreting μa. α as A, roll_{a.α} as i, and unroll_{a.α} as i^{−1}, the following equation is satisfied in the model:

fix_{(μa.α)→^{⊥}μa.α} (λf.λa.let^{⊥} x ⇐ Φ_{a.α}(f, f)(unroll_{a.α} a) in ^{⊥}(roll_{a.α} x)) = λa.^{⊥}a

Proof. Every pointed cpo is isomorphic to a lifted cpo. So we can use Theorem A.7 with the functor given by

F (A_{⊥}^{−}, A_{⊥}^{+}) = (L[[Φ_{a.α}(a^{−}, a^{+})]]^{a^{−} ↦ A^{−},a^{+} ↦ A^{+}})_{⊥},
F (g^{−} : A_{1}^{−}_{⊥} → A_{2}^{−}_{⊥}, g^{+} : A_{1}^{+}_{⊥} → A_{2}^{+}_{⊥})
= L[[Φ_{a.α}^{d}(x^{−}, x^{+})]]^{a_{1}^{−} ↦ A_{1}^{−},...,a_{2}^{+} ↦ A_{2}^{+}}(•[x^{−} ↦ g^{−}, x^{+} ↦ g^{+}])

to obtain a pointed cpo A_{⊥} with an isomorphism j : (L[[α]]^{a ↦ A})_{⊥} → A_{⊥}. Moreover, since j is an isomorphism, it both preserves and reflects ⊥, and must hence be expressible as j = i_{⊥} = λm.let^{⊥} x ⇐ m in ^{⊥}(ix) for some isomorphism i : L[[α]]^{a ↦ A} → A.

Further, we get the minimal invariant property for i from the minimal invariant property of j wrt. Φ_{a.α}^{d}( , ), using Lemma A.5(2) to rearrange the fix body:

fix (λf.λx.let^{⊥} r ⇐ Φ_{a.α}(f, f)(ψ x) in ^{⊥}(ϕr)) = fix ((λg.λx.g (^{⊥}x)) ∘ (λf.λm.let^{⊥} x ⇐ m in let^{⊥} r ⇐ Φ_{a.α}(f, f)(ψ x) in ^{⊥}(ϕr)))

= (λg.λx.g (^{⊥}x))

(fix ((λf.λm.let^{⊥} x ⇐ m in let^{⊥} r ⇐ Φ_{a.α}(f, f)(ψ x) in ^{⊥}(ϕr)) ∘ (λg.λx.g (^{⊥}x))))

= λx. fix (λg.λm.let^{⊥} x ⇐ m in let^{⊥} r ⇐ Φ_{a.α}(λx.g (^{⊥}x), λx.g (^{⊥}x))(ψ x) in ^{⊥}(ϕr))(^{⊥}x)
= λx. fix (λg.λm.let^{⊥} x ⇐ m in let^{⊥} r ⇐ Φ_{a.α}^{d}(g, g)(^{⊥}(ψ x)) in ^{⊥}(ϕr))(^{⊥}x)

= λx. fix (λg.λm.(λm.let^{⊥} r ⇐ m in ^{⊥}(ϕr))(Φ_{a.α}^{d}(g, g)(let^{⊥} x ⇐ m in ^{⊥}(ψ x))))(^{⊥}x)

= λx. fix (λg.λm.j (Φ_{a.α}^{d}(g, g)(j^{−1} m)))(^{⊥}x) = λx. id (^{⊥}x) = λx.^{⊥}x

A.2 Admissible relations

In this section we review the properties of (computation-)admissbile relations in the cpo semantics; in particular, we show admissibility of the key relation-forming constructs.

Let there be given a relational correspondence between predomain interpretations L of L and L′ of L′ (Definition 3.16). Most notably, admissibility is then preserved by formation of inverse images and intersections (e.g., [Pit99]):

Lemma A.9 Recall that a (computation-)admissibile relations between closed types α and α′ is a (pointed) chain-complete relation between cpos L[[α]] and L′[[α′]]. We then have:

1. When R ∈ ARel(α, α′), (x_{1}: α_{1}, …, x_{n}: α_{n}) ⊢ M : α and (x′_{1}: α′_{1}, …, x′_{n′}: α′_{n′}) ⊢ M′ : α′ are terms of L and L′ respectively, and for all i ≥ 2, σx_{i} ∈ Val_{L}(α_{i}) and σ′x′_{i} ∈ Val_{L′}(α′_{i}), the relation R_{1} ∈ Rel(α_{1}, α′_{1}) given by

a_{1} R_{1} a′_{1} ⟺ M^{(a_{1}/x_{1},σ)} R M^{′(a′_{1}/x′_{1},σ′)}

is admissible.

Moreover, when α_{1} and α′_{1} are computation-types, R is computation-admissible, and the functions λx_{1}.M^{σ} and λx′_{1}.M^{′σ′} are rigid, then R_{1} is also computationadmissible.

2. When (R_{j})_{j∈J} is an arbitrary (not necessarily finite or even countable) family of admissible relations between α and α′, the relation ⋂_{j∈J} R_{j} is admissible, where

a (⋂_{j∈J} R_{j}) a′ ⟺ ∀j ∈ J. a R_{j} a′

Moreover, if each R_{j} is computation-admissible then so is ⋂_{j∈J} R_{j}.

Proof. Both parts are fairly simple:

1. Define the continuous functions f : L[[α_{1}]] → L[[α]] and f′ : L′[[α′_{1}]] → L′[[α′]] by

f = \underline{λ}a_{1}.L[[M]](•[x_{1} ↦ a_{1}, x_{2} ↦ σx_{2}…, x_{n} ↦ σx_{n}])

and

f′ = \underline{λ}a′_{1}.L′[[M′]](•[x′_{1} ↦ a′_{1}, x′_{2} ↦ σ′x′_{2}, …, x_{n′} ↦ σ′x′_{n′}])

Then a_{1} R_{1} a′_{1} iff f(a_{1}) R f′(a′_{1}).

Now let (a_{1i})_{i∈ω} and (a′_{1i})_{i∈ω} be chains in L[[α_{1}]] and L′[[α′_{1}]], respectively, such that for all i ∈ ω, a_{1i} R_{1} a′_{1i}, i.e., f(a_{1i}) R f′(a′_{1i}). By monotonicity of f, the sequences (f(a_{1i}))_{i∈ω} and (f′(a′_{1i}))_{i∈ω} then form chains in L[[α]] and L′[[α′]], and since R was assumed chain-complete, we have

⨆_{i} f(a_{1i}) R ⨆_{i} f′(a′_{1i})

By continuity of f and f′, this is equivalent to

f(⨆_{i} a_{1i}) R f′(⨆_{i} a′_{1i})

which says that ⨆_{i} a_{1i} R_{1} ⨆ a′_{1i}, meaning that R_{1} is chain-complete.

Further, to show R_{1} pointed, we must show f(⊥) R f′(⊥), which follows from pointedness of R and the fact (Proposition 2.13) that a rigid function is strict.

2. Let R_{j} be a family of chain-complete relations between cpos L[[α]] and L′[[α′]], and let (a_{i})_{i∈ω} and (a′_{i})_{i∈ω} be chains componentwise related by the intersection R of all the R_{j}. That is,

∀i ∈ ω. ∀j ∈ J. a_{i} R_{j} a′_{i}

Exchanging the two universal quantifiers, we get

∀j ∈ J. ∀i ∈ ω. a_{i} R_{j} a′_{i}

Now, since each R_{j} was assumed chain-complete, this implies

∀j ∈ J. ⨆_{i} a_{i} R_{j} ⨆_{i} a′_{i}

And thus, ⨆_{i} a_{i} (⋂_{j∈J} R_{j}) ⨆_{i} a′_{i} as required to show chain-completeness of R.

Similarly, if each R_{j} relates ⊥_{L[[β]]} and ⊥_{L′[[β′]]}, then so must their intersection, and thus ⋂_{j∈J} R_{j} is pointed.

We can also show admissibility of the standard relational actions of the type constructors (where it does not already follow directly from the previous lemma):

Lemma A.10 If the R’s are admissible relations, then so are (1) ι^{r} and (2) R_{1} +^{r} R_{2} (defined as in Lemma 3.18), and (3) the Σ_{i}^{r}R_{i}, given by

s (Σ_{i}^{r}R_{i}) s′ ⟺ ∃i ∈ I. ∃a_{i} R_{i} a′_{i}. s = in_{i} a_{i} ∧ s′ = in_{i} a′_{i}

Moreover, if relation extension is taken as relation-lifting (from Proposition 3.21), then (4)˚ R is computation-admissible.

Proof. The relations determined by the definitions in each case can be written as:

ι^{r} = {(n, n) | n ∈ N}
R_{1} +^{r} R_{2} = {((1, a_{1}), (1, a′_{1})) | (a_{1}, a′_{1}) ∈ R_{1}} ∪ {((2, a_{2}), (2, a′_{2})) | (a_{2}, a′_{2}) ∈ R_{2}}
Σ_{i}^{r}R_{i} = {((i, a_{i}), (i, a′_{i})) | i ∈ I, (a_{i}, a′_{i}) ∈ R_{i}}
˚ R = {(up(a), up(a′)) | (a, a′) ∈ R} ∪ {(⊥, ⊥)}

We then check each case:

1. Case ι^{r}. Since the cpo N of natural numbers was discretely ordered, any chain in N must be constant, so least upper bounds of componentwise related chains are obviously also related.

2. Case R_{1}+^{r}R_{2}. A chain in A_{1}+A_{2} must lie entirely within one of the injects. Assume wlog. that it is of the form ((1, a_{i}))_{i∈ω} for some chain (a_{i})_{i∈ω} in A_{1}. Analogously, the related chain must be of the form ((1, a′_{i}))_{i∈ω}, with a_{i} R_{1} a′_{i}. By assumption on R_{1}, ⨆_{i} a_{i} R_{1} ⨆_{i} a′_{i}. The result then follows by observing that

⨆_{i}(1, a_{i}) = (1, ⨆_{i} a_{i}) (R_{1} + R_{2}) (1, ⨆_{i} a′_{i}) = ⨆_{i}(1, a′_{i})

3. Case Σ_{i}^{r}R_{i} is analogous to +^{r}, only with the set of tags taken as I instead of {1, 2}.

4. Case˚ R. Let (m_{i})_{i∈ω} and (m′_{i})_{i∈ω} be chains such that m_{i} (˚ R) m′_{i}. There are then two possibilities. It could be that for all i, m_{i} = ⊥_{A_{⊥}} and m′_{i} = ⊥_{A′_{⊥}}. In this case,

⨆_{i} m_{i} = ⊥_{A_{⊥}} (˚ R) ⊥_{A′_{⊥}} = ⨆_{i} m′_{i}

by the second disjunct of the definition, and we are done. Or there exists an i_{0} ≥ 0, such that for all i ≥ i_{0}, m_{i} = up(a_{i}) and m′_{i} = up(a′_{i}) for some a_{i} R a′_{i}. By definition of the ordering in A_{⊥}, the a_{i} form a chain (if up(a_{i}) ⊑ up(a_{i+1}) then a_{i} ⊑ a_{i+1}). Analogously for the a′_{i}. Because R was assumed chain-complete, we have ⨆_{i≥i_{0}} a_{i} R ⨆_{i≥i_{0}} a′_{i}. And since the initial segment of ⊥s in a chain does not affect its least upper bound, we get

⨆_{i} m_{i} = ⨆_{i≥i_{0}} up(a_{i}) = up(⨆_{i≥i_{0}} a_{i}) (˚ R) up(⨆_{i≥i_{0}} a′_{i}) = ⨆_{i≥i_{0}} up(a′_{i}) = ⨆_{i} m′_{i}

Also, directly by the second component of the definition,˚ R is pointed.

And finally, we can verify our fixed-point induction principle:

Lemma A.11 Let S be a computation-admissible relation between β and β′. Let f ∈ Val_{L}(β →β) and f′ ∈ Val_{L′}(β′ →β′) be such that ∀b S b′. f b S f′ b′. Then fix_{β} f S fix_{β′} f′.

Proof. We have L[[fix_{β} x]](•[x ↦ f]) = ⨆_{i} f^{i}(⊥_{L[[β]]}), and analogously for f′. Since in particular S is admissible, i.e., chain-complete in the model, it suffices to show that for all i ≥ 0,

f^{i}(⊥_{L[[β]]}) S f^{′i}(⊥_{L[[β′]]})

This follows by a simple induction on i. For i = 0, we get the result from pointedness (computation-admissibility) of S. And for the inductive step, we use that if f^{i}(⊥) S f^{′i}(⊥) then by assumption on f and f′,

f^{i+1}(⊥) = f(f^{i}(⊥)) S f′(f^{′i}(⊥)) = f^{′i+1}(⊥)

A.3 Isomorphisms of recursive types

The definition of Φ_{a.α}(f^{−}, f^{+}) allows it to act on partial functions (i.e., total functions into a lifted cpo); we need this generality for solving recursive type equations, because the approximants will not be total. But when the type-directed functor acts on known isomorphisms between cpos (not necessarily pointed), a simpler definition is possible:

Definition A.12 When φ is a term constructor denoting an isomorphism (e.g., roll or unroll), we define term constructors Φ^{i}_{a.α}(φ) and Ψ^{i}_{a.β}(φ) with types:

\frac{φ : α_{1} →^{∼} α_{2}}{Φ^{i}_{a.α}(φ) : α\{α_{1}/a\} →^{∼} α\{α_{2}/a\}} and \frac{φ : α_{1} →^{∼} α_{2}}{Ψ^{i}_{a.β}(φ) : β\{α_{1}/a\} →^{∼} β\{α_{2}/a\}}

as follows:

Φ^{i}_{a.a}(φ)a = φa
Φ^{i}_{a.ι}(φ)n = n
Φ^{i}_{a.1}(φ)u = ⟨⟩
Φ^{i}_{a.α_{1}×α_{2}}(φ)p = ⟨Φ^{i}_{a.α_{1}}(φ)(fst p), Φ^{i}_{a.α_{2}}(φ)(snd p)⟩
Φ^{i}_{a.0}(φ)z = z
Φ^{i}_{a.α_{1}+α_{2}}(φ)s = case(s, a_{1}.inl (Φ^{i}_{a.α_{1}}(φ)a_{1}), a_{2}.inl (Φ^{i}_{a.α_{2}}(φ)a_{2}))
Φ^{i}_{a.Σℵ}(φ)s = case(s, i. a_{i}. in_{i} (Φ^{i}_{a.ℵ(i)}(φ)a_{i}))
Φ^{i}_{a.β}(φ)b = Ψ^{i}_{a.β}(φ)b
Ψ^{i}_{a.˚ α}(φ)m = let˚ x ⇐ m in˚ (Φ^{i}_{a.α}(φ)x)
Ψ^{i}_{a.1}(φ)o = ⟨⟩
Ψ^{i}_{a.β_{1}×β_{2}}(φ)p = ⟨Ψ^{i}_{a.β_{1}}(φ)(fst p), Ψ^{i}_{a.β_{2}}(φ)(snd p)⟩
Ψ^{i}_{a.α→β}(φ)g = λx.Ψ^{i}_{a.β}(φ)(g (Φ^{i}_{a.α}(φ^{−1})x))

Lemma A.13 The functorial actions on isomorphisms are related to their general counterparts as follows:

⊥(Φ^{i}_{a.α}(φ)a) = Φ_{a.α}(λx.^{⊥}(φ^{−1} x), λy.^{⊥}(φy))a
Ψ^{i}_{a.β}(φ)b = Ψ_{a.β}(λx.^{⊥}(φ^{−1} x), λy.^{⊥}(φy))b

Proof. Straightforward induction on α and β.

Note also that we have Φ^{i}_{a.α}(Φ^{i}_{a′.α′}(φ)) = Φ^{i}_{a′.α{α′/a}}(φ).

Lemma A.14 Let F and G be type constructors of L_{1} (not necessarily covariant), and let α = μa. F (Ga) and α′ = μa′. G(F a′) be the solutions to the corresponding recursive type equations. Then in the predomain model, there exists an isomorphism χ : Gα →^{∼} α′, which further satisfies the following two (equivalent) coherence equations:

x: Gα ⊢ roll_{a′.G(F a′)} (Φ^{i}_{a′.G(F a′)}(χ)(Φ^{i}_{a.Ga}(unroll_{a.F (Ga)})x)) = χx : α′
y: α′ ⊢ Φ^{i}_{a.Ga}(roll_{a.F (Ga)})(Φ^{i}_{a′.G(F a′)}(χ^{−1})(unroll_{a′.G(F a′)} y)) = χ^{−1} y : Gα

Proof. When φ : α_{1} →^{∼} α_{2} is a term constructor, we define the function

φ^{♯} : ^{⊥}α_{1} →^{⊥}α_{2} = λm^{^{⊥}α_{1}}.let^{⊥} x ⇐ m in ^{⊥}(φx)

For terms, take F (f^{−}, f^{+}) = Φ_{a.F a}^{d}(f^{−}, f^{+}) and also write F̂(f) for F (f, f). Analogously for G. Further, we define the usual abbreviations ϕ = roll_{a.F (Ga)} and ϕ′ = roll_{a′.G(F a′)}, with ψ and ψ′ for the inverses.

A.3. ISOMORPHISMS OF RECURSIVE TYPES 127

Now let i = ϕ^{♯} : ^{⊥}F (Gα) → ^{⊥}α and j = ϕ^{′♯} : ^{⊥}G(F α′) → ^{⊥}α′ be the minimal invariants for the corresponding functors. We first show that there exists an isomorphism l : ^{⊥}G(α) →^{⊥}α′. Take

(l′, l) = fix (λ\begin{matrix}(h^{^{⊥}α′→^{⊥}Gα}, k^{^{⊥}Gα→^{⊥}α′}). \\ (G(i^{−1}, i) ∘ G(F (h, k), F (k, h)) ∘ j^{−1}, j ∘ G(F (k, h), F (h, k)) ∘ G(i, i^{−1}))) \end{matrix}

We want to show that l′ is actually the two-sided inverse of l. Accordingly, consider the strict function c = λ(h, k). k ∘ h. Lemma A.4 then gives us:

c ∘ (λ(h, k). (G(i^{−1}, i) ∘ G(F (h, k), F (k, h)) ∘ j^{−1}, j ∘ G(F (k, h), F (h, k)) ∘ G(i, i^{−1}))) = λ(h, k). j ∘ G(F (k, h), F (h, k)) ∘ G(i, i^{−1}) ∘ G(i^{−1}, i) ∘ G(F (h, k), F (k, h)) ∘ j^{−1} = λ(h, k). j ∘ G(F (k, h), F (h, k)) ∘ G(i^{−1} ∘ i, i^{−1} ∘ i)) ∘ G(F (h, k), F (k, h)) ∘ j^{−1} = λ(h, k). j ∘ G(F (k, h), F (h, k)) ∘ G(id_{^{⊥}F Gα}, id_{^{⊥}F Gα}) ∘ G(F (h, k), F (k, h)) ∘ j^{−1} = λ(h, k). j ∘ G(F (k, h), F (h, k)) ∘ id_{^{⊥}GF Gα} ∘ G(F (h, k), F (k, h)) ∘ j^{−1} = λ(h, k). j ∘ G(F (h, k) ∘ F (k, h), F (h, k) ∘ F (k, h)) ∘ j^{−1} = λ(h, k). j ∘ G(F (k ∘ h, k ∘ h), F (k ∘ h, k ∘ h)) ∘ j^{−1} = λ(h, k). j ∘ Ĝ(F̂(k ∘ h)) ∘ j^{−1} = λ(h, k). (λf.j ∘ Ĝ(F̂(f)) ∘ j^{−1})(k ∘ h) = (λf.j ∘ Ĝ(F̂(f)) ∘ j^{−1}) ∘ c

From Lemma A.2(2) and a let-simplification, we obtain that

Φ_{a.Ga}^{d}(Φ_{a.F a}^{d}(f, f), Φ_{a.F a}^{d}(f, f)) = Φ_{a.G(F a)}^{d}(f, f)

so by Lemma A.5(1) and the fact that j is a minimal invariant for GF, we get

l′ ∘ l = c(fix (λ(h, k). · · ·)) = fix (λf.j ∘ Ĝ(F̂(f)) ∘ j^{−1}) = id_{^{⊥}α′} = id_{α′}^{♯}

In the other direction, taking c′ = λ(h, k). h ∘ k, we similarly get:

c′ ∘ (λ(h, k). (G(i^{−1}, i) ∘ G(F (h, k), F (k, h)) ∘ j^{−1}, j ∘ G(F (k, h), F (h, k)) ∘ G(i, i^{−1}))) = λ(h, k). G(i^{−1}, i) ∘ G(F (h, k), F (k, h)) ∘ j^{−1} ∘ j ∘ G(F (k, h), F (h, k)) ∘ G(i, i^{−1}) = λ(h, k). G(i^{−1}, i) ∘ G(F (h, k), F (k, h)) ∘ G(F (k, h), F (h, k)) ∘ G(i, i^{−1}) = λ(h, k). G(i^{−1}, i) ∘ G(F (h ∘ k, h ∘ k), F (h ∘ k, h ∘ k)) ∘ G(i, i^{−1}) = (λg. G(i^{−1}, i) ∘ G(F (g, g), F (g, g)) ∘ G(i, i^{−1})) ∘ c′ = (λg. G(i ∘ F (g, g) ∘ i^{−1}, i ∘ F (g, g) ∘ i^{−1})) ∘ c′ = (λg. Ĝ(i ∘ F̂(g) ∘ i^{−1})) ∘ c′

And thus we have, using both parts of Lemma A.5 and the minimal-invariant property of i:

l ∘ l′ = c′ (fix (λ(h, k). · · ·)) = fix (λg. Ĝ(i ∘ F̂(g) ∘ i^{−1})) = Ĝ(fix (λf. i ∘ F̂(Ĝ(f)) ∘ i^{−1}))

= Ĝ(id_{^{⊥}α}) = id_{^{⊥}Gα} = id_{Gα}^{♯}

We can thus take χ to be the unique isomorphism such that χ^{♯} = l.

Further, knowing that l and l′ are actually inverses, we get the second part of the result by unrolling their fixed-point definition once:

(l′, l) = (G(i^{−1}, i) ∘ G(F (l′, l), F (l, l′)) ∘ j^{−1}, j ∘ G(F (l, l′), F (l′, l)) ∘ G(i, i^{−1}))

Now, take advantage of the following simple relationship between the functorial actions on an isomorphisms:

Φ_{a.α}^{d}(φ^{−1}^{♯}, φ^{♯}) = Φ_{a.α}^{d}(λm.let^{⊥} x ⇐ m in ^{⊥}(φ^{−1} x), λm.let^{⊥} x ⇐ m in ^{⊥}(φx)) = λm.let^{⊥} x ⇐ m in Φ_{a.α}(λx.let^{⊥} x ⇐^{⊥}x in ^{⊥}(φ^{−1} x), ⋯ φ ⋯)x = λm.let^{⊥} x ⇐ m in Φ_{a.α}(λx.^{⊥}(φ^{−1} x), λx.^{⊥}(φx))x = λm.let^{⊥} x ⇐ m in ^{⊥}(Φ^{i}_{a.α}(φ)x) = Φ^{i}_{a.α}(φ)^{♯}

From this we get:

χ^{♯} = l = j ∘ G(F (l, l′), F (l′, l)) ∘ G(i, i^{−1})
= ϕ^{′♯} ∘ Φ_{a.Ga}^{d}(Φ_{a.F a}^{d}(χ^{♯}, χ^{−1}^{♯}), Φ_{a.F a}^{d}(χ^{−1}^{♯}, χ^{♯})) ∘ Φ_{a.Ga}^{d}(ϕ^{♯}, ψ^{♯})

= ϕ^{′♯} ∘ Φ_{a.Ga}^{d}(Φ^{i}_{a.F a}(χ^{−1})^{♯}, Φ^{i}_{a.F a}(χ)^{♯}) ∘ Φ^{i}_{a.Ga}(ψ)^{♯} = ϕ^{′♯} ∘ Φ^{i}_{a.Ga}(Φ^{i}_{a.F a}(χ))^{♯} ∘ Φ^{i}_{a.Ga}(ψ)^{♯}

So, in particular,

⊥(ϕ′ (Φ^{i}_{a.G(F a)}(χ)(Φ^{i}_{a.Ga}(ψ)x))) = (ϕ^{′♯} ∘ Φ^{i}_{a.Ga}(Φ^{i}_{a.F a}(χ))^{♯} ∘ Φ^{i}_{a.Ga}(ψ)^{♯})(^{⊥}x) = χ^{♯} (^{⊥}x)
= ^{⊥}(χx) ,

and since lifting is injective, we get the first coherence equation. The second one is analogous.

A.4 Invariant relations over recursive types

We now want to show that certain principles for constructing relations over recursive types are valid; specifically, that a class of well-behaved relational actions allows us to solve “recursive relation equations”. The following presents only the specific results we need for the proofs in Chapter 4; for a general treatment of the subject, see [Pit99].

Throughout this section, let us assume a fixed relational correspondence between predomain interpretations L of L and L′ of L′, with a computation-extension of relations. In keeping with the general convention in this appendix, we also write ^{⊥}R for relationlifting. We first characterize a particularly well-behaved way of constructing admissible relations:

Definition A.15 Let F and F′ be type constructors. A (mixed) relational action F for F and F′ assigns to every pair of relations R^{−} ∈ ARel(α^{−}, α^{′−}) and R^{+} ∈ ARel(α^{+}, α^{′+}) a relation F(R^{−}, R^{+}) ∈ ARel(Φ_{a.F a}(α^{−}, α^{+}), Φ_{a.F′a}(α^{′−}, α^{′+})). We say that this action is admissible if it satisfies:

(∀x R_{1}^{−} x′. f^{−} x (^{⊥}R_{2}^{−}) f^{′−} x′) ∧ (∀x R_{1}^{+} x′. f^{+} x (^{⊥}R_{2}^{+}) f^{′+} x′)

⇒ ∀y F(R_{2}^{−}, R_{1}^{+}) y′. Φ_{a.F a}(f^{−}, f^{+})y (^{⊥}F(R_{1}^{−}, R_{2}^{+})) Φ_{a.F′a}(f^{′−}, f^{′+})y′

Likewise, for computation-type constructors G and G′, a relational action is called computation-admissible if it maps R^{−} ∈ ARel(α^{−}, α^{′−}) and R^{+} ∈ ARel(α^{+}, α^{′+}) to a relation G(R^{−}, R^{+}) ∈ CARel(Ψ_{a.Ga}(α^{−}, α^{+}), Ψ_{a.G′a}(α^{′−}, α^{′+})) such that

(∀x R_{1}^{−} x′. f^{−} x (^{⊥}R_{2}^{−}) f^{′−} x′) ∧ (∀x R_{1}^{+} x′. f^{+} x (^{⊥}R_{2}^{+}) f^{′+} x′)

⇒ ∀y G(R_{2}^{−}, R_{1}^{+}) y′. Ψ_{a.Ga}(f^{−}, f^{+})y G(R_{1}^{−}, R_{2}^{+}) Ψ_{a.G′a}(f^{′−}, f^{′+})y′

It is easy to see from the definition of Φ_{a.β}(f^{−}, f^{+}) that any computation-admissible action is also admissible. Moreover, we have a number of standard ways of constructing (computation-)admissible relational actions:

Lemma A.16 The following relational actions are all admissible (and computationadmissible where noted):

1. F(R^{−}, R^{+}) = R^{+} for F a = F′a = a.

2. F(R^{−}, R^{+}) = R_{0} for F a = α_{0} and F′a = α′_{0}, where α_{0} and α′_{0} do not depend on a, and R_{0} ∈ ARel(α_{0}, α′_{0}) is an arbitrary admissible relation. F is also computationadmissible if R_{0} is.

3. F(R^{−}, R^{+}) = F_{1}(R^{−}, R^{+})×^{r} F_{2}(R^{−}, R^{+}) for F a = F_{1}a×F_{2}a and F′a = F′_{1}a×F′_{2}a, where F_{1} is admissible for F_{1} and F′_{1}, and F_{2} is admissible for F_{2} and F′_{2}. F is also computation-admissible if both F_{1} and F_{2} are.

4. F(R^{−}, R^{+}) = F_{1}(R^{−}, R^{+}) +^{r} F_{2}(R^{−}, R^{+}) for F a = F_{1}a + F_{2}a and F′a = F′_{1}a + F′_{2}a, where F_{1} is admissible for F_{1} and F′_{1}, and F_{2} is admissible for F_{2} and F′_{2}.

5. F(R^{−}, R^{+}) = Σ_{i}^{r}F_{i}(R^{−}, R^{+}) for F a = Σ_{i}F_{i}a and F a = Σ_{i}F′_{i}a, where for every i ∈ I, F_{i} is admissible for F_{i} and F′_{i}.

6. G(R^{−}, R^{+}) = F(R^{+}, R^{−})→^{r} G_{1}(R^{−}, R^{+}) for Ga = F a→G_{1}a and G′a = F′a→G′_{1}a, where F is admissible for F and F′, and G_{1} is computation-admissible for G_{1} and G′_{1}. G is also computation-admissible.

7. G(R^{−}, R^{+}) =˚ F(R^{−}, R^{+}) for Ga =˚ (F a) and G′a =˚ (F′a), where F is admissible for F and F′. G is also computation-admissible.

8. F(R^{−}, R^{+}) = ⋂_{j∈J} F_{j}(R^{−}, R^{+}) for any F and F′, when for every j ∈ J, F_{j} is admissible for F and F′. F is also computation-admissible if each F_{j} is.

Proof. Simple verification in all cases. For example, and since it is somewhat nonstandard, let us go through the details of (7), i.e.,˚ F(R^{−}, R^{+}). First, we note that for any computation-extension of relations we have that if ∀a R_{1} a′. f a (˚ R_{2}) f′ a′ then

∀m (^{⊥}R_{1}) m′. let^{⊥} x ⇐ m in f x (˚ R_{2}) let^{⊥} x′ ⇐ m′ in f′ x′

(Note that this is simply condition (0) of a monad relation from Definition 3.26 in the case where the ambient effect is partiality, and R̊_{2} is taken as the R_{2} of the definition.) This holds because R̊_{2} is by definition computation-admissible and hence pointed. Thus, since m (^{⊥}R_{1}) m′ means that either both m and m′ are ⊥, or both are liftings of R_{1}-related elements, we get the required relationship in either case.

We can now verify admissibility of the action (R^{−}, R^{+}) ↦˚ F(R^{−}, R^{+}) for the type constructors Ga =˚ (F a) and G′a =˚ (F′a) when F is an admissible action for F and F′. Let the Rs and fs be as in Definition A.15; we must then show:

∀y (˚ F(R_{2}^{−}, R_{1}^{+})) y′. Ψ_{a.˚ F a}(f^{−}, f^{+})y (˚ F(R_{1}^{−}, R_{2}^{+})) Ψ_{a.˚ F′a}(f^{′−}, f^{′+})y′

So assume y (˚ F(R_{2}^{−}, R_{1}^{+})) y′. Expanding Ψ_{a.˚ F a}(f^{−}, f^{+}) according to Definition A.1, we must then establish that

let˚ x ⇐ y in let^{⊥} r ⇐ Φ_{a.F a}(f^{−}, f^{+})x in˚ r (˚ F(R_{1}^{−}, R_{2}^{+})) let˚ x′ ⇐ y′ in ⋯

By property (2) of relation-extension and the assumption on y and y′, it suffices to show that

∀x F(R_{2}^{−}, R_{1}^{+}) x′.
let^{⊥} r ⇐ Φ_{a.F a}(f^{−}, f^{+})x in˚ r (˚ F(R_{1}^{−}, R_{2}^{+})) let^{⊥} r′ ⇐ Φ_{a.F′a}(f^{′−}, f^{′+})x′ in˚ r′

Let x F(R_{2}^{−}, R_{1}^{+}) x′ be given. By assumption on F, we then have that

Φ_{a.F a}(f^{−}, f^{+})x (^{⊥}F(R_{1}^{−}, R_{2}^{+})) Φ_{a.F′a}(f^{′−}, f^{′+})x′

And hence, by the observation at the beginning of the proof, it suffices to show

∀r F(R_{1}^{−}, R_{2}^{+}) r′.˚ r (˚ F(R_{1}^{−}, R_{2}^{+}))˚ r′

which follows immediately from property (1) of relation extension.

We also have the following principle for constructing new computation-admissible actions from old ones:

Lemma A.17 Let G_{1} be a computation-admissible relational action for G_{1} and G′_{1}, and let G and G′ be another pair of computation-type constructors. Further let h be a rigid natural transformation from G to G_{1}, i.e., satisfying

h(Ψ_{a.Ga}(f^{−}, f^{+})x) = Ψ_{a.G_{1}a}(f^{−}, f^{+})(hx)

and analogously for h′. Then the relational action G for G and G′ defined by

x G(R^{−}, R^{+}) x′ ⟺ hx G_{1}(R^{−}, R^{+}) h′ x′

is computation-admissible.

Proof. We first note that G(R^{−}, R^{+}) is a computation-admissible relation by assumption on G_{1} and rigidity of h and h′. Further, let the Rs and fs be as in Definition A.15; we must show that

∀y G(R_{2}^{−}, R_{1}^{+}) y′. Ψ_{a.Ga}(f^{−}, f^{+})y G(R_{1}^{−}, R_{2}^{+}) Ψ_{a.G′a}(f^{′−}, f^{′+})y′

I.e., that

∀y, y′. hy G_{1}(R_{2}^{−}, R_{1}^{+}) h′ y′ ⇒ h(Ψ_{a.Ga}(f^{−}, f^{+})y) G_{1}(R_{1}^{−}, R_{2}^{+}) h′ (Ψ_{a.G′a}(f^{′−}, f^{′+})y′)

Now, by assumption on h and h′, this is equivalent to

∀y, y′. hy G_{1}(R_{2}^{−}, R_{1}^{+}) h′ y′ ⇒ Ψ_{a.G_{1}a}(f^{−}, f^{+})(hy) G_{1}(R_{1}^{−}, R_{2}^{+}) Ψ_{a.G′_{1}a}(f^{′−}, f^{′+})(h′ y′)

and that follows form the assumption that G_{1} was admissible (taking y and y′ in the definition of admissibility to be the hy and h′ y′ above).

From this, we get admissibility of the action relating ambient computations to their continuation-passing counterparts:

Lemma A.18 Let γ be a type of L′, and for any R ∈ ARel(α, α′) let the relation ¯˚ R ∈ CARel(˚ α, (α′ →˚ γ) →˚ γ) be given by:

m (¯˚ R) u

⟺ ∀α_{0} type_{L}, O ∈ ARel(α_{0}, γ). λk^{α→˚ α_{0}}.let˚ x ⇐ m in k x ((R →˚ O) →˚ O) u

Further, let F and F′ be type constructors, with an admissible relational action F. Then the relational action G given by

G(R^{−}, R^{+}) = ¯˚ F(R^{−}, R^{+})

is computation-admissible for Ga =˚ (F a) and G′a = (F′a →˚ γ) →˚ γ.

Proof. First note that for any α_{0} and O ∈ ARel(α_{0}, γ), the action G_{1}^{O} given by

G_{1}^{O}(R^{−}, R^{+}) = (F(R^{−}, R^{+}) →^{r}˚ O) →^{r}˚ O

is computation-admissible for G_{1}^{α_{0}}a = (F a →˚ α_{0}) →˚ α_{0} and G′ by Lemma A.16(2,6).

Let h = λm.λk.let˚ x ⇐ m in k x. This mapping is a natural transformation between the functors derived from G and G_{1}^{α_{0}}:

h(Ψ_{a.˚ F a}(f^{−}, f^{+})m) = h(let˚ r ⇐ m in let^{⊥} y ⇐ Φ_{a.F a}(f^{−}, f^{+})r in˚ y)
= λk.let˚ x ⇐ (let˚ r ⇐ m in let^{⊥} y ⇐ Φ_{a.F a}(f^{−}, f^{+})r in˚ y) in k x
= λk.let˚ r ⇐ m in let^{⊥} y ⇐ Φ_{a.F a}(f^{−}, f^{+})r in k y
= λk.(hm)(λr.let^{⊥} y ⇐ Φ_{a.F a}(f^{−}, f^{+})r in k y) = λk.(hm)(Ψ_{a.F a→˚ α_{0}}(f^{+}, f^{−})k)

= λk.let^{⊥} y ⇐^{⊥}(Ψ_{a.F a→˚ α_{0}}(f^{+}, f^{−})k) in (hm) y = Ψ_{a.(F a→˚ α_{0})→˚ α_{0}}(f^{−}, f^{+})(hm)

(using that Ψ_{a.˚ α_{0}}(f^{−}, f^{+}) = id_{˚ α_{0}} by Lemma A.2(1), because a cannot appear free in α_{0}). Thus, taking h′ as the identity in Lemma A.17, we get that the action given by G^{O},

m (G^{O}(R^{−}, R^{+})) u = λk.let˚ x ⇐ m in k a (G_{1}^{O}(R^{−}, R^{+})) u

is computation-admissible for G and G′. And finally, since G(R^{−}, R^{+}) is the intersection of all G^{O}(R^{−}, R^{+}), we get the result by Lemma A.16(8).

We can now state the main result motivating the definition of admissible actions:

Theorem A.19 Let F be an admissible relational action for type constructors F and F′. Then there exists an invariant relation for F, i.e., a relation Δ ∈ ARel(μa. F a, μa. F′a) such that a Δ a′ ⟺ unroll_{a.F a} a F(Δ, Δ) unroll_{a.F′a} a′.

Proof. (The proof technique is due to Pitts and can essentially be found in [Pit99]. However, since we are working with binary relations instead of unary ones, and a few details are slightly more involved for predomains than for domains, it seems worth spelling out the construction.)

As usual, we abbreviate roll as ϕ and unroll as ψ.

We first note that a functorial action on relations preserves inclusions. For let R_{1}^{−} ⊆ R_{2}^{−} and R_{1}^{+} ⊆ R_{2}^{+}. Take f^{−} = f^{+} = λa.^{⊥}a and f^{′−} = f^{′+} = λa′.^{⊥}a′. Then clearly f^{−} (R_{1}^{−} →^{⊥}R_{2}^{−}) f^{′−} and f^{+} (R_{1}^{+} →^{⊥}R_{2}^{+}) f^{′+}. Hence for any y F(R_{2}^{−}, R_{1}^{+}) y, we get from Lemma A.2(3) that

⊥y = Φ_{a.F a}(λa.^{⊥}a, λa.^{⊥}a)y (^{⊥}F(R_{1}^{−}, R_{2}^{+})) Φ_{a.F′a}(λa′.^{⊥}a′, λa′.^{⊥}a′)y′ = ^{⊥}y′ .

Moreover, we easily see that^{⊥}x (^{⊥}R) ^{⊥}x′ iff x R x′ (by definition of relation-lifting specifically, not true for computation-extension of relations in general), so we get y F(R_{1}^{−}, R_{2}^{+}) y′, i.e., F(R_{2}^{−}, R_{1}^{+}) ⊆ F(R_{1}^{−}, R_{2}^{+}). In other words, F is monotone in its second argument and antimonotone in the first one.

Let R^{−} and R^{+} be arbitrary relations in ARel(μa. F a, μa. F′a), and define the relations R^{∓}, R^{±} ∈ ARel(μa. F a, μa. F′a) by

a R^{∓} a′ ⟺ ψ a F(R^{+}, R^{−}) ψ′ a′ and a R^{±} a′ ⟺ ψ a F(R^{−}, R^{+}) ψ′ a′ .

We can then define an operator Θ, mapping (R^{−}, R^{+}) to (R^{∓}, R^{±}), antimonotone in the first position and monotone in the second. Further, the set of admissible relations between two types is closed under arbitrary intersection, so ARel(μa. F a, μa. F′a)^{op} × ARel(μa. F a, μa. F′a) forms a complete lattice. Hence, by the Knaster-Tarski fixed-point theorem, Θ has a least fixed point (Δ^{−}, Δ^{+}), with Δ^{−}, Δ^{+} ∈ ARel(μa. F a, μa. F′a) satisfying

a Δ^{−} a′ ⟺ ψ a F(Δ^{+}, Δ^{−}) ψ′ a′ and a Δ^{+} a′ ⟺ ψ a F(Δ^{−}, Δ^{+}) ψ′ a′ .

Moreover, (Δ^{+}, Δ^{−}) is clearly also a fixed point of Θ, and so must be greater than the least one, giving (for both components) the inclusion Δ^{+} ⊆ Δ^{−}.

It thus remains to show containment in the other direction. Consider the relation ∇ ∈ CARel(μa. F a →^{⊥}μa. F a, μa. F′a →^{⊥}μa. F′a) determined by

h ∇ h′ ⟺ ∀a Δ^{−} a′. ha (^{⊥}Δ^{+}) h′ a′

(∇ is computation-admissible because it is given by an intersection over inverse images (by application, which is rigid) of the computation-admissible ^{⊥}Δ^{+}.) Now define the functional H : (μa. F a →^{⊥}μa. F a) → (μa. F a →^{⊥}μa. F a) by:

H = λh^{μa.F a→^{⊥}μa.F a}.λa^{μa.F a}.let^{⊥} x ⇐ Φ_{a.F a}(h, h)(ψ a) in ^{⊥}(ϕx)

and analogously for H′. We want to show that when h ∇ h′ then also H h ∇ H′ h′, i.e., that

∀a Δ^{−} a′. let^{⊥} x ⇐ Φ_{a.F a}(h, h)(ψ a) in ^{⊥}(ϕx) (^{⊥}Δ^{+}) let^{⊥} x′ ⇐ Φ_{a.F′a}(h′, h′)(ψ′ a′) in ^{⊥}(ϕ′ x′)

This follows from the usual properties of relation-extension, the equations defining Δ^{−} and Δ^{+} above, and the functorial action of F (taking f^{−} = f^{+} = h, f^{′−} = f^{′+} = h′, R_{1}^{−} = R_{1}^{+} = Δ^{−}, and R_{2}^{−} = R_{2}^{+} = Δ^{+}).

Thus, since the relation ∇ was computation-admissible, we get by fixed-point induction (Lemma 3.19) that fix H ∇ fix H′. And because fix H = λx.^{⊥}x by the minimalinvariant property (Corollary A.8), we have

∀a Δ^{−} a′.^{⊥}a (^{⊥}Δ^{+}) ^{⊥}a′ .

Finally, by the same argument about ^{⊥}R as at the beginning of the proof, this simplifies to Δ^{−} ⊆ Δ^{+}, completing the proof that we can take Δ = Δ^{−} = Δ^{+} as the invariant relation for F.

From this, we immediately get that – much like recursive type equations – a large class of recursive relation equations has solutions:

Corollary A.20 Let F and F′ be type constructors, and let ^{•} be a formal relation constructor, built out of (1) the standard relational actions of L_{1}-type constructors, (2) constant admissible relations (computation-admissible for computation-types), and (3) the relation constructor ¯ ˚ (for any γ); so that ^{•} maps any relation R ∈ ARel(α, α′) to •R ∈ ARel(F α, F′α′).

Then there exists a relation μR.^{•}R ∈ ARel(μa. F a, μa. F′a) such that

a (μR.^{•}R) a′ ⟺ unroll_{a.F a} a ^{•}(μR.^{•}R) unroll_{a.F′a} a′ .

Proof. By induction on ^{•}, using Lemmas A.16 and A.18, we directly obtain an admissible mixed relational action F, such that

F(R, R) = ^{•}R

By Theorem A.19, this F has an invariant relation Δ. And because of the equation above, we can simply take μR.^{•}R to be Δ.

Bibliography

[AM91] Andrew W. Appel and David B. MacQueen. Standard ML of New Jersey. In Third International Symposium on Programming Language Implementation and Logic Programming, number 528 in Lecture Notes in Computer Science, pages 1–13, Passau, Germany, August 1991.

[App92] Andrew W. Appel. Compiling with Continuations. Cambridge University Press, 1992.

[CM93] Pietro Cenciarelli and Eugenio Moggi. A syntactic approach to modularity in denotational semantics. In Proceedings of the Conference on Category Theory and Computer Science, Amsterdam, September 1993. CWI Tech. Report.

[CR91] William Clinger and Jonathan Rees. Revised^{4} report on the algorithmic language Scheme. Lisp Pointers, 4(3):1–55, July 1991.

[DF90] Olivier Danvy and Andrzej Filinski. Abstracting control. In Proceedings of the 1990 ACM Conference on Lisp and Functional Programming, pages 151–160, Nice, France, June 1990.

[DF92] Olivier Danvy and Andrzej Filinski. Representing control: A study of the CPS transformation. Mathematical Structures in Computer Science, 2(4):361–391, December 1992.

[Esp95] David A. Espinosa. Semantic Lego. PhD thesis, Graduate School of Arts and Sciences, Columbia University, May 1995.

[Fel88] Matthias Felleisen. The theory and practice of first-class prompts. In Proceedings of the Fifteenth Annual ACM Symposium on Principles of Programming Languages, pages 180–190, San Diego, California, January 1988.

[FH92] Matthias Felleisen and Robert Hieb. The revised report on the syntactic theories of sequential control and state. Theoretical Computer Science, 103(2):235–271, September 1992.

[Fil94] Andrzej Filinski. Representing monads. In Proceedings of the 21st ACM SIGPLAN-SIGACT Symposium on Principles of Programming Languages, pages 446–457, Portland, Oregon, January 1994.

[Fis72] Michael J. Fischer. Lambda calculus schemata. In Proceedings of the ACM Conference on Proving Assertions about Programs, pages 104–109. SIGPLAN Notices, Vol. 7, No 1 and SIGACT News, No 14, January 1972. Revised version in Lisp and Symbolic Computation, 6(3/4), 1993.

[FWFD88] Matthias Felleisen, Mitchell Wand, Daniel P. Friedman, and Bruce F. Duba. Abstract continuations: A mathematical semantics for handling full functional jumps. In Proceedings of the 1988 ACM Conference on Lisp and Functional Programming, pages 52–62, Snowbird, Utah, July 1988.

[Gir72] Jean-Yves Girard. Interprétation Fonctionelle et Élimination des Coupures de l’Arithmétique d’Ordre Supérieur. Thèse d’état, Université Paris VII, 1972.

[GRR95] Carl A. Gunter, Didier Rémy, and Jon G. Riecke. A generalization of exceptions and control in ML-like languages. In Functional Programming and Computer Architecture, 1995.

[Hay87] Christopher T. Haynes. Logic continuations. The Journal of Logic Programming, 4(2):157–176, June 1987.

[HDB90] Robert Hieb, R. Kent Dybvig, and Carl Bruggeman. Representing control in the presence of first-class continuations. In Proceedings of the ACM SIGPLAN ’90 Conference on Programming Languages Design and Implementation, pages 66–77, White Plains, New York, June 1990.

[HDM93] Robert Harper, Bruce F. Duba, and David MacQueen. Typing first-class continuations in ML. Journal of Functional Programming, 3(4):465–484, October 1993. (A preliminary version appeared in Proceedings of the 1991 Symposium on Principles of Programming Languages).

[HM93] Robert Harper and John C. Mitchell. On the type structure of Standard ML. ACM Transactions on Programming Languages and Systems, 15(2):211–252, April 1993.

[JD88] Gregory F. Johnson and Dominic Duggan. Stores and partial continuations as first-class objects in a language and its environment. In Proceedings of the Fifteenth Annual ACM Symposium on Principles of Programming Languages, pages 158–168, San Diego, California, January 1988.

[JG89] Pierre Jouvelot and David K. Gifford. Reasoning about continuations with control effects. In Proceedings of the ACM SIGPLAN’89 Conference on Programming Languages Design and Implementation, pages 218–226, Portland, Oregon, June 1989.

[KJLS87] David K.Gifford, Pierre Jouvelot, John M. Lucassen, and Mark A. Sheldon. FX-87 reference manual (edition 1.0). Technical Report MIT/LCS/TR-407, Laboratory for Computer Science, Massachusetts Institute of Technology, September 1987.

[KW93] David J. King and Philip Wadler. Combining monads. In J. Launchbury and P. M. Sansom, editors, Functional Programming, Glasgow 1992, pages 134–143, Ayr, Scotland, 1993. Springer-Verlag.

[LD94] Julia L. Lawall and Olivier Danvy. Continuation-based partial evaluation. In Proceedings of the 1994 ACM Conference on Lisp and Functional Programming, pages 227–238, Orlando, Florida, June 1994.

[LHJ95] Sheng Liang, Paul Hudak, and Mark Jones. Monad transformers and modular interpreters. In Proceedings of the 22nd ACM SIGPLAN-SIGACT Symposium on Principles of Programming Languages, pages 333–343, San Francisco, California, January 1995.

[LPJ95] John Launchbury and Simon L. Peyton Jones. State in Haskell. Lisp and Symbolic Computation, 8(4):293–341, December 1995.

[M^{+}62] John McCarthy et al. LISP 1.5 Programmer’s Manual. MIT Press, Cambridge, Massachusetts, 1962.

[Mit96] John C. Mitchell. Foundations for Programming Languages. MIT Press, 1996. To appear.

[ML71] Saunders Mac Lane. Categories for the Working Mathematician, volume 5 of Graduate Texts in Mathematics. Springer-Verlag, 1971.

[Mog89] Eugenio Moggi. Computational lambda-calculus and monads. In Proceedings of the Fourth Annual Symposium on Logic in Computer Science, pages 14–23, Pacific Grove, California, June 1989. IEEE.

[Mog90] Eugenio Moggi. An abstract view of programming languages. Technical Report ECS-LFCS-90-113, Laboratory for Foundations of Computer Science, University of Edinburgh, Edinburgh, Scotland, April 1990.

[Mog91] Eugenio Moggi. Notions of computation and monads. Information and Computation, 93(1):55–92, July 1991.

[Mor93] J. Gregory Morrisett. Generalizing first-class stores. In ACM SIGPLAN Workshop on State in Programming Languages, pages 73–87, Copenhagen, Denmark, June 1993. (Technical report YALEU/DCS/RR-968, Department of Computer Science, Yale University).

[MTH90] Robin Milner, Mads Tofte, and Robert Harper. The Definition of Standard ML. The MIT Press, 1990.

[MW85] Albert R. Meyer and Mitchell Wand. Continuation semantics in typed lambdacalculi (summary). In Rohit Parikh, editor, Logics of Programs – Proceedings, number 193 in Lecture Notes in Computer Science, pages 219–224, Brooklyn, June 1985.

[Pit99] Andrew M. Pitts. Relational properties of domains. Information and Computation, 199? Revised version of Cambridge Computer Laboratory Technical Report Number 321. To appear.

[Plo75] Gordon D. Plotkin. Call-by-name, call-by-value and the λ-calculus. Theoretical Computer Science, 1(2):125–159, December 1975.

[Plo77] Gordon D. Plotkin. LCF considered as a programming language. Theoretical Computer Science, 5(3):223–255, December 1977.

[PW93] Simon L. Peyton Jones and Philip Wadler. Imperative functional programming. In Proceedings of the Twentieth Annual ACM Symposium on Principles of Programming Languages, pages 71–84, Charleston, South Carolina, January 1993.

[QS91] Christian Queinnec and Bernard Serpette. A dynamic extent control operator for partial continuations. In Proceedings of the Eighteenth Annual ACM Symposium on Principles of Programming Languages, pages 174–184, Orlando, Florida, January [Rey72] John C. Reynolds. Definitional interpreters for higher-order programming lan-

guages. In Proceedings of 25th ACM National Conference, pages 717–740, Boston, August 1972.

[Rey74a] John C. Reynolds. On the relation between direct and continuation semantics. In Jacques Loeckx, editor, 2nd Colloquium on Automata, Languages and Programming, number 14 in Lecture Notes in Computer Science, pages 141–156, Saarbrücken, West Germany, July 1974.

[Rey74b] John C. Reynolds. Towards a theory of type structure. In B. Robinet, editor, Programming Symposium, number 19 in Lecture Notes in Computer Science, pages 408–425, Paris, France, April 1974.

[Rey93] John C. Reynolds. The discoveries of continuations. Lisp and Symbolic Computation, 6(3/4):233–247, November 1993.

[Sch86] David A. Schmidt. Denotational Semantics: A Methodology for Language Development. Allyn and Bacon, Inc., 1986.

[SF90] Dorai Sitaram and Matthias Felleisen. Control delimiters and their hierarchies. Lisp and Symbolic Computation, 3(1):67–99, January 1990.

[SF93] Amr Sabry and Matthias Felleisen. Reasoning about programs in continuationpassing style. Lisp and Symbolic Computation, 6(3/4):289–360, November 1993. (An earlier version appeared in Proceedings of the 1992 ACM Conference on Lisp and Functional Programming).

[Smi82] Brian C. Smith. Reflection and Semantics in a Procedural Language. PhD thesis, Massachusetts Institute of Technology, Cambridge, Massachusetts, January 1982. MIT-LCS-TR-272.

[ST80] Ravi Sethi and Adrian Tang. Constructing call-by-value continuation semantics. Journal of the ACM, 27(3):580–597, July 1980.

[Ste94] Guy L. Steele, Jr. Building interpreters by composing monads. In Proceedings of the 21st ACM SIGPLAN-SIGACT Symposium on Principles of Programming Languages, pages 472–492, Portland, Oregon, January 1994.

[Sto81] Joseph E. Stoy. The congruence of two programming language definitions. Theoretical Computer Science, 13(2):151–174, February 1981.

[SW74] Christopher Strachey and Christopher P. Wadsworth. Continuations: A mathematical semantics for handling full jumps. Technical Monograph PRG-11, Oxford University Computing Laboratory, Programming Research Group, Oxford, England, 1974.

[Wad85] Philip Wadler. How to replace failure by a list of successes. In Functional Programming Languages and Computer Architecture, number 201 in Lecture Notes in Computer Science, Nancy, France, September 1985.

[Wad90] Philip Wadler. Comprehending monads. In Proceedings of the 1990 ACM Conference on Lisp and Functional Programming, pages 61–78, Nice, France, June 1990.

[Wad92a] Philip Wadler. Comprehending monads. Mathematical Structures in Computer Science, 2(4):461–493, December 1992. (An earlier version appeared in Proceedings of the 1990 ACM Conference on Lisp and Functional Programming).

[Wad92b] Philip Wadler. The essence of functional programming (invited talk). In Proceedings of the Nineteenth Annual ACM Symposium on Principles of Programming Languages, pages 1–14, Albuquerque, New Mexico, January 1992.

[Wad94] Philip Wadler. Monads and composable continuations. Lisp and Symbolic Computation, 7(1):39–56, January 1994.

[Wan80] Mitchell Wand. Continuation-based multiprocessing. In Conference Record of the 1980 LISP Conference, pages 19–28, Stanford, California, August 1980.

[WF88] Mitchell Wand and Daniel P. Friedman. The mystery of the tower revealed: A non-reflective description of the reflective tower. Lisp and Symbolic Computation, 1(1), May 1988.