Under Consideration for Publication in J. Functional Programming Implicit Self-adjusting Computation for Purely Functional Programs

Computational problems that involve dynamic data, such as physics simulations and program development environments, have been an important subject of study in programming languages. Building on this work, recent advances in self-adjusting computation have developed techniques that enable programs to respond automatically and efficiently to dynamic changes in their inputs. Self-adjusting programs have been shown to be efficient for a reasonably broad range of problems but the approach still requires an explicit programming style, where the programmer must use specific monadic types and primitives to identify, create and operate on data that can change over time. We describe techniques for automatically translating purely functional programs into self-adjusting programs. In this implicit approach, the programmer need only annotate the (top-level) input types of the programs to be translated. Type inference finds all other types, and a type-directed translation rewrites the source program into an explicitly self-adjusting target program. The type system is related to information-flow type systems and enjoys decidable type inference via constraint solving. We prove that the translation outputs well-typed self-adjusting programs and preserves the source program's input-output behavior, guaranteeing that translated programs respond correctly to all changes to their data. Using a cost semantics, we also prove that the translation preserves the asymptotic complexity of the source program.


Introduction
Dynamic changes are pervasive in computational problems: physics simulations often involve moving objects; robots interact with dynamic environments; compilers must respond to slight modifications in their input programs. Such dynamic changes are often small, or incremental, and result in only slightly different output, so computations can often respond to them asymptotically faster than performing a complete re-computation. Such asymptotic improvements can lead to massive speedup in practice but tradition-Permission to make digital or hard copies of all or part of this work for personal or classroom use is granted without fee provided that copies are not made or distributed for profit or commercial advantage and that copies bear this notice and the full citation on the first page. To copy otherwise, to republish, to post on servers or to redistribute to lists, requires prior specific permission and/or a fee. ICFP'11, September 19-21, 2011, Tokyo, Japan. Copyright © 2011 ally require careful algorithm design and analysis (e.g., Chiang and Tamassia [1992]; Guibas [2004]; Demetrescu et al. [2005]), which can be challenging even for seemingly simple problems.
Motivated by this problem, researchers have developed languagebased techniques that enable computations to respond to dynamic data changes automatically and efficiently (see Ramalingam and Reps [1993] for a survey). This line of research, traditionally known as incremental computation, aims to reduce dynamic problems to static (conventional or batch) problems by developing compilers that automatically generate code for dynamic responses. This is challenging, because the compiler-generated code aims to handle changes asymptotically faster than the source code. Early proposals [Demers et al. 1981;Pugh and Teitelbaum 1989;Field and Teitelbaum 1990] were limited to certain classes of applications (e.g., attribute grammars), allowed limited forms of data changes, and/or yielded suboptimal efficiency. Some of these approaches, however, had the important advantage of being implicit: they required little or no change to the program code to support dynamic change-conventional programs could be compiled to executables that respond automatically to dynamic changes.
Recent work based on self-adjusting computation made progress towards achieving efficient incremental computation by providing algorithmic language abstractions to express computations that respond automatically to changes to their data [Ley-Wild et al. 2008;]. Self-adjusting computation can deliver asymptotically efficient updates in a reasonably broad range of problem domains [Acar et al. 2007[Acar et al. , 2010a, and have even helped solve challenging open problems [Acar et al. 2010b]. Existing self-adjusting computation techniques, however, require the programmer to program explicitly by using a certain set of primitives [Carlsson 2002;Ley-Wild et al. 2008;]. Specifically the programmer must manually distinguish stable data, which remains the same, from changeable data, which can change over time, and operate on changeable data via a special set of primitives. As a result, rewriting a conventional program into a self-adjusting program requires extensive changes to the code. For example, a purely functional program will need to be rewritten in imperative style using write-once, monadic references.
In this paper, we present techniques for implicit self-adjusting computation that allow conventional programs to be translated automatically into efficient self-adjusting programs. Our approach consists of a type system for inferring self-adjusting computation types from purely functional programs and a type-guided translation algorithm that rewrites purely functional programs into selfadjusting programs.
The type system hinges on a key observation connecting selfadjusting computation to information flow [Pottier and Simonet 2003;Sabelfeld and Myers 2003]: both involve tracking data dependencies (of changeable data and sensitive data, respectively) as well as dependencies between expressions and data. Specifi-cally, we show that a type system that encodes the changeability of data and expressions in self-adjusting computation as secrecy of information suffices to statically enforce the invariants needed by self-adjusting computation. The type system uses polymorphism to capture stable and changeable uses of the same data or expression. Our type system admits a constraint-based formulation where the constraints are a strict subset of those needed by traditional information-flow type systems. Consequently, as with information flow, our type system admits an HM(X) inference algorithm [Odersky et al. 1999] that can infer all type annotations from top-level type specifications on the input of a program.
For this work, determination of types via type inference is not an end unto itself but a means for translating purely functional programs into self-adjusting programs. To achieve this, we first present a set of compositional, non-deterministic transformation rules. Guided by the types, the rules identify the set of all changeable expressions that operate on changeable data and rewrite them into the self-adjusting target language. We then present a deterministic translation algorithm that applies the compositional rules judiciously, considering the type and context (enclosing expressions) of each translated subexpression, to generate a well-typed self-adjusting target program.
Taken together, the type system, its inference algorithm, and the translation algorithm enable translating purely functional source programs to self-adjusting target programs using top-level type annotations on the input type of the source program. These top-level type annotations simply mark what part of the input data is subject to change. Figure 1 illustrates how source programs written in Level ML, a purely functional subset of ML with level types, can be translated to self-adjusting programs in the target language AFL, a language for self-adjusting computation with explicit primitives [Acar et al. 2006]. We prove three critical properties of the approach.
• Type soundness. On source code of a given type, the translation algorithm produces well-typed self-adjusting code of a corresponding target type (Theorem 6.1).
• Observational equivalence. The translated self-adjusting program, when evaluated, produces the same value as the source program (Theorem 6.5).
• Asymptotic complexity. The time to evaluate the translated program is asymptotically the same as the time to evaluate the source program (Theorem 6.9).
Type soundness and observational equivalence together imply a critical consistency property: that self-adjusting programs respond correctly to changing data (via the consistency of the target selfadjusting language [Acar et al. 2006]). The third property shows that the translated program takes asymptotically as long to evaluate (from scratch) as the corresponding source program. In addition, it places a worst-case bound on the time taken to self-adjust via change propagation, which can and often does take significantly less time when data changes are small. To prove this complexity result, we use a cost semantics [Sands 1990;Sansom and Peyton Jones 1995] that enables precise reasoning about the complexity of the evaluation time. We do not, however, prove tighter bounds on the complexity of self-adjustments; this would be beyond the scope of this paper.
We intend to complete an implementation of our approach as an extension of Standard ML and the MLton compiler [MLton]. However, we expect the proposed approach could be implemented in other languages such as Haskell, where self-adjusting libraries also exist [Carlsson 2002]. In general, since our approach simply generates target code, it is agnostic to implementation details of  Figure 1. Visualizing the translation between the source language Level ML and the target language AFL, and related properties. the explicit self-adjusting-computation mechanisms employed in the target language and thus can be applied broadly.
Paper guide. We find it better to give an overview of the proposed approach by focusing on the translation problem and working back to the type system in a "top-down" manner (Section 2). The details of the translation algorithm and our theorems, however, rely on the type system. We therefore take a more "bottom-up" approach in the rest of the paper: we first present the static semantics (the syntax and the type system) (Sections 3 and 4), and then describe the target language AFL (Section 5) and the translation (Section 6). Finally, we discuss related work (Section 7) and conclude. Due to space restrictions, we include all the proofs in the appendix [Chen et al. 2011].

Overview
We present an informal overview of our approach via examples. First we briefly describe explicit self-adjusting computation, as laid out in previous work, and which we use as a target language. Then we outline our proposed approach.

Explicit Self-Adjusting Computation
The key concept behind explicit approaches is the notion of a modifiable (reference), which stores changeable values that can change over time [Acar et al. 2006]. The programmer operates on modifiables with mod, read, and write constructs to create, read from, and write into modifiables. The run-time system of a selfadjusting language uses these constructs to represent the execution as a graph, enabling efficient change propagation when the data changes in small amounts.
As an example, consider a trivial program that computes x 2 +y: To make this program self-adjusting with respect to changes in y, while leaving x unchanging or stable, we assign y the type int mod (of modifiables containing integers) and read the contents of the modifiable. The body of the read is a changeable expression ending with a write. This function has a changeable arrow type → C : squareplus_SC: int * int mod → C int fun squareplus_SC (x, y) = let x2 = x * x in read y as y' in let r = x2 + y' in write(r) The read operation delineates the code that depends on the changeable value y, and the changeable arrow type ensures a critical consistency property: → C -functions can only be called within the context of a changeable expression. If we change the value of y, change propagation can update the result, re-executing only the read and its body, reusing the computation of the square x2. Suppose we wish to make x changeable while leaving y stable. We need to read x and place x2 into a modifiable (because we can only read within the context of a changeable expression), and immediately read back x2 and finish by writing the sum. (To avoid creating this modifiable would require further structural changes to the code.) squareplus_CS: int mod * int → C int fun squareplus_CS (x, y) = let x2 = mod (read x as x' in write(x' * x')) in read x2 as x2' in let r = x2' + y in write(r) As this example shows, rewriting even a trivial program can require modifications to the code, and different choices about what is or is not changeable lead to different code. Moreover, if we need squareplus SC and squareplus CS-for instance, if we want to pass squareplus to various higher-order functions-we must write, and maintain, both versions.
Conservatively treating all data as changeable would require writing just one version, but treating all data as modifiable can introduce unacceptably high overhead. At the other extreme, making everything stable requires no rewriting, but forgoes the benefits of change propagation. Instead, we take an approach where data is modifiable only where necessary.

Implicit Self-Adjusting Computation
To make self-adjusting computation implicit, we use type information to insert reads, writes, and mods automatically. The user annotates the input type of the program; we infer types for all expressions, and use this information to guide a translation algorithm. The translation algorithm returns well-typed self-adjusting target programs. The translation requires no expression-level annotations. For the example function squareplus above, we can automatically derive squareplus SC and squareplus CS from just the type of the function (expressed in a slightly different form, as we discuss next).

Level types.
To uniformly describe source functions (more generally, expressions) that differ only in their "changeability", we need a more general type system than that of the target language. This type system refines types with levels S (stable) and C (changeable). The type int δ is an integer whose level is δ; for example, to get squaresum CS we can annotate squaresum's argument with the type int C × int S .
Level types are an important connection between informationflow types [Pottier and Simonet 2003] and those needed for our translation: high-security secret data (level H) behaves like changeable data (level C), and low-security public data (level L) behaves like stable data (level S). In information flow, data that depends on secret data must be secret; in self-adjusting computation, data that depends on changeable data must be changeable. Building on this connection, we develop a type system with several features and mechanisms similar to information flow. Among these is level polymorphism; our type system assigns level-polymorphic types to expressions that accommodate various "changeabilities". (As with ML's polymorphism over types, our level polymorphism is prenex.) Another similarity is evident in our constraint-based type inference system, where the constraints are a strict subset of those in Pottier and Simonet [2003]. As a corollary, our system admits a constraintbased type inference algorithm [Odersky et al. 1999].
Translation. The main purpose of our type system is to support translation. Given a source expression and its type, translation in- ... ( * inc, map, mapPair same as in Figure 1. * ) Figure 3. Function mapPair in Level ML, with level types serts the appropriate mod, read, and write primitives and restructures the code to produce an expression that is well-typed in the target language. The type system of the target language, which is explicitly self-adjusting, is monomorphic in the levels or changeability, while the implicitly self-adjusting source language is polymorphic over levels. Consequently, translation also needs to monomorphize the source code. Our translation generates code that is welltyped, has the same input-output behavior as the source program, and is, at worst, a constant factor slower than the source program.
Since the source and target languages differ, proving these properties is nontrivial; in fact, the proofs critically guided our formulation of the type system and translation algorithm.
A more detailed example: mapPair. To illustrate how our translation works, consider a function mapPair that takes two integer lists and increments the elements in both lists. This function can be written by applying the standard higher-order map over lists. Figure 2 shows the purely functional code in an ML-like language for an implementation of mapPair, with a datatype α list, an increment function inc, and a polymorphic map function. Type signatures give the types of functions.
To obtain a self-adjusting mapPair, we first decide how we wish to allow the input to change. Suppose that we want to allow insertion and deletion of elements in the first list, but we expect the length of the second list to remain constant, with only its elements changing. We can express this with the versions of the list type with different changeability: • α list C for lists of α with changeable tails; • α list S for lists of α with stable tails.
Then a list of integers allowing insertion and deletion has type int S list C , and one with unchanging length has type int C list S . Now we can write the type annotation on mapPair shown in Figure 3. Given only that annotation, type inference can find appropriate types for inc and map and our translation algorithm generates self-adjusting code from these annotations. Note that to obtain a self-adjusting program, we only had to provide types for the function. We call this language with level types Level ML.
Target code for mapPair. Translating the code in Figure 3 produces the self-adjusting target code in Figure 4. Note that inc and map have level-polymorphic types. In map inc l we increment sta- ble integers, and in map inc a we increment changeable integers, so the type inferred for inc must be generic: ∀δ. int δ → δ int δ . Our translation produces two implementations of inc, one per instantiation (δ=S and δ=C): inc S and inc C (in Figure 4). Sine we want to use inc with the higher-order function map, we need to generate a "selector" function that takes an instantiation and picks out the appropriate implementation: In mapPair itself, we pass a level instantiation to the selector: inc [δ=S]. (This instantiation is known statically, so it could be replaced with inc S at compile time.) Observe how the single annotation on mapPair led to duplication of the two functions it uses. While inc S is the same as the original inc, the changeable version inc C adds a read and a write. Note also that the two generated versions of map are both different from the original.

The interplay of type inference and translation.
Given user annotations on the input, type inference finds a satisfying type assignment, which then guides our translation algorithm to produce selfadjusting code. In many cases, multiple type assignments could satisfy the annotations; for example, subsumption allows any stable type to be promoted to a changeable type. Translation yields target code that satisfies the crucial type soundness, operational equivalence, and complexity properties under any satisfying assignment. Figure 5. Levels, constraints, types, and type schemes But some type assignments are preferable, especially when one considers constant factors. Choosing C levels whenever possible is always a viable strategy, but treating all data as changeable results in more overhead. As in information flow, where we want to consider data secret only when absolutely necessary, inference yields principal typings that are minimally changeable, always preferring S over C.

From Information Flow Types to SAC
Self-adjusting computation separates the computation and data into two parts: stable and changeable. Changeable data refers to data that can change over time; all non-changeable data is stable. Similarly, changeable expressions refers to expressions that operate (via elimination forms) on changeable data; all non-changeable expressions are stable. Evaluation of changeable expressions (that is, changeable computations) can change as the data that they operate on changes: changes in data cause changes in control flow. These distinctions are critical to effective self-adjustment: previous work shows that it suffices to track and remember changeable data and evaluations of changeable expressions because stable data and evaluations of stable expressions remain invariant over time. Previous work therefore presents languages that enable the programmer to separate stable and changeable data, and type systems that enforce the correct usage of these constructs.
In this section, we describe the self-adjusting computation types that we infer for purely functional programs. A key insight behind our approach is that in information-flow type systems, secret (highsecurity) data is infectious: any data that depends on secret data itself must be secret. This corresponds to self-adjusting computation: data that depends on changeable data must itself be changeable. In addition, self-adjusting computation requires expressions that inspect changeable data-elimination forms-to be changeable. To encode this invariant, we extend function types with a mode, which is either stable or changeable; only changeable functions can inspect changeable data. This additional structure preserves the spirit of information flow-based type systems, and, moreover, supports constraint-based type inference in a similar style.
The starting point for our formulation is Pottier and Simonet [2003]. Our types include two (security) levels, stable and changeable. We generally follow their approach and notation. The two key differences are that (1) since Level ML is purely functional, we need no "program counter" level "pc"; (2) we need a mode ε on function types.

Levels.
The levels S (stable) and C (changeable) have a total order: To support polymorphism and enable type inference, we allow level variables α, β to appear in types.
Types. Types consist of integers tagged with their level, products 1 and sums with an associated level, and arrow (function) types. Function types (τ1 → ε τ2) δ carry two level annotations ε and δ.
Outer-stable and outer-changeable types, and equality up to outer levels The mode ε is the level of the computation encapsulated by the function. This mode determines how a function can manipulate changeable values: a function in stable mode cannot directly manipulate changeable values; it can only pass them around. By contrast, a changeable-mode function can directly manipulate changeable values. The outer level δ is the level of the function itself, as a value. We say that a type is ground if it contains no level variables.
Subtyping. Figure 6 shows the subtyping relation τ <: τ , which is standard except for the levels. It requires that the outer level of the subtype is smaller than the outer level of the supertype and that the modes match in the case of functions: a stable-mode function is never a subtype or supertype of a changeable-mode function. (It would be sound to make stable-mode functions subtypes of changeable-mode functions, but changeable mode functions are more expensive; silent coercion would make performance less predictable.)

Levels and types.
We rely on several relations between levels and types to ascertain various invariants. A type τ is higher than δ, written δ ¡ τ , if the outer level of the type is at least δ. In other words, δ is a lower bound of the outer level(s) of τ . Figure 7 defines this relation. We distinguish between outer-stable and outerchangeable types (Figure 8). We write τ O.S. if the outer level of τ is S. Similarly, we write τ O.C. if the outer level of τ is C. Finally, two types τ1 and τ2 are equal up to their outer levels, written τ1 τ2, if τ1 = τ2 or they differ only in their outer levels.

Constraints.
To perform type inference, we extend levels with level variables α and β, and use a constraint solver to find solu- . Abstract syntax of the source language Level ML tions for the variables. Our constraints C, D include level-variable comparisons ≤ and level-type comparisons δ ¡τ , which type inference composes into conjunctions of satisfiability predicates ∃ α.C. The subtyping and lower bound relations defined in Figures 6 and 7 consider closed types only. For type inference, we can extend these with a constraint to allow non-closed types.
A (ground) assignment, written φ, substitutes concrete levels S and C for level variables. An assignment φ satisfies a constraint C, written φ C, if and only if C holds true after the substitution of variables to ground types as specified by φ. We say that C entails D, written C D, if and only if every assignment φ that satisfies C also satisfies D. We write φ(α) for the solution of α in φ, and [φ]τ for the usual substitution operation on types. For example, if

Type schemes.
A type scheme σ is a type with universally quantified level variables: σ = ∀ α [D]. τ . We say that the variables α are bound by σ. The type scheme is bounded by the constraint D, which specifies the conditions that must hold on the variables. As usual, we consider type schemes equivalent under capture-avoiding renaming of their bound variables. Ground types can be written as type schemes, e.g. int C as ∀∅ [true]. int C .

Static Semantics
Syntax. Figure 9 shows the syntax for our source language Level ML, a purely functional language with integers (as base types), products, and sums. The expressions consist of values (integers, pairs, tagged values, recursive functions), projections, case expressions, function applications, and let bindings. For convenience, we consider only expressions in A-normal form, which names intermediate results. A-normal form simplifies some technical issues, while maintaining expressiveness.
Constraint-based type system. We could define types as Such a type system would be completely standard. Instead, we use a richer type system that allows us to directly translate Level ML programs into self-adjusting programs in AFL. This constraint-based type system has the level-decorated types, constraints, and type schemes in Figure 5 and described in Section 3. After discussing the rules themselves, we will look at type inference (Section 4.2).
Typing takes place in the context of a constraint formula C and a typing environment Γ that maps variables to type schemes: Γ ::= · | Γ, x : σ. The typing judgment C; Γ ε e : τ has a constraint C and typing environment Γ, and infers type τ for expression e in mode ε. Beyond the usual typing concerns, there are three important aspects of the typing rules: the determination of modes and levels, level polymorphism, and constraints. To help separate concerns, we discuss constraints later in the section-at this time, the reader can ignore the constraints in the rules and read C; Γ ε e : τ as Γ ε e : τ , read C δ ¡ τ2 as δ ¡ τ2, and so on.
The mode of each typing judgment affects the types that can be used "directly" by the expression being typed. Specifically, the mode discipline prevents the elimination forms from being applied C; Γ ε e : τ Under constraint C and source typing environment Γ, source expression e has type τ C; Γ ε n : int S (SInt) Figure 10. Typing rules for Level ML to changeable values in the stable mode. This is a key principle of the type system.
No computation happens in values, so they can be typed in either mode. The typing rules for variables (SVar), integers (SInt), pairs (SPair), and sums (SSum) are otherwise standard (we omit the symmetric judgment inr v). Rule (SVar) instantiates a variable's polymorphic type. For clarity, we also make explicit the renaming of the quantified type variables α to some fresh β (which will be instantiated later by constraint solving). To type a function (SFun), we type the body in the mode ε specified by the function type (τ1 → ε τ2) δ , and require the result type τ2 to be higher than the mode, ε ¡ τ2. As a result, a changeable-mode function must have a changeable return type. This captures the idea that a changeablemode function is a computation that depends on changeable data, and thus its result must accommodate changes to that data. Primitive operators ⊕ take two stable integers and return a stable integer result.
As is common in Damas-Milner-style systems, when typing let we can generalize variables in types (in our system, level variables) to yield a polymorphic value only when the bound expression is a value. This value restriction is not essential because Level ML is pure, but facilitates adding side effects at a later date. In the first case (SLetE), the expression bound may be a non-value, so we do not generalize and simply type the body in the same mode as the whole let, assuming that the bound expression has the specified type in any mode ε . 2 We allow subsumption only when the subtype and supertype are equal up to their outer levels, e.g. from a bound expression e1 of subtype int S to an assumption x : int C . This simplifies the translation, with no loss of expressiveness: to handle "deep" subsumption, such as (int S → S int S ) S <: (int S → S int C ) C , we can insert coercions into the source program before typing it with these rules. (This process could easily be automated.) In the second case (SLetV), when the expression bound is a value, we type the let expression in mode ε by typing the body in the same mode ε, assuming that the value bound is typed in the stable mode (the mode is ignored in the rules typing values). As in (SLetE), we allow subsumption on the bound value only when the types are equal up to their outer level. Because we are binding a value, we generalize its type by quantifying over the type's free level variables.
Function application, ⊕, fst, and case are the forms that eliminate values of changeable type. An application is typed in the mode ε of the function being applied because changeable functions can operate on changeable values; the typing mode must match (ε = ε). Furthermore, the result of the function must be higher than the function's level: if a function is itself changeable, (τ1 → ε τ2) C , then it could be replaced by another function and thus the result of this application must be changeable. (Due to letsubsumption, checking this in (SFun) alone is not enough.) The rule (SCase) types a case expression, in either mode ε, by typing each branch in ε. The mode ε must be higher than the level δ of the scrutinee to ensure that a changeable sum type is not inspected at the stable mode. Furthermore, the level of the result τ must also be higher than δ: if the scrutinee changes, we may take the other branch, requiring a changeable result.
Rule (SFst) enforces a condition, similar to (SCase), that we can project out of a changeable tuple of type (τ1 × τ2) C only in changeable mode. We omit the symmetric rule for snd.
Our premises on variables, such as the scrutinee of (SCase), are stable-mode ( S ), but this was an arbitrary decision; since (SVar) is the only rule that can derive such premises, their mode is irrelevant.

Constraints and Type Inference
Many of the rules simply pass around the constraint C. An implementation of rules with constraint-based premises, such as (SFun), implicitly adds those premises to the constraint, so that C = . . . ∧ (ε ¡ τ2). Rule (SLetV) generalizes level variables instead of type variables, with the "occurs check" α ∩ F V (C, Γ) = ∅.
Standard techniques in the tradition of Damas and Milner [1982] can infer types for Level ML. In particular, our rules and constraints fall within the HM(X) framework [Odersky et al. 1999], permitting inference of principal types via constraint solving. As always, we cannot infer the types of polymorphically recursive functions.
Using a constraint solver that, given the choice between assigning S or C to some level variable, prefers S, inference finds principal typings that are minimally changeable. Thus, data and computations will only be made changeable-and incur tracking overhead-where necessary to satisfy the programmer's annotation. This corresponds to preferring a lower security level in information flow [Pottier and Simonet 2003].

Dynamic Semantics
The call-by-value semantics of source programs is defined by a big-step judgment e ⇓ v, read "e evaluates to value v". Our rules in Figure 13 are standard; we write [v/x]e for capture-avoiding substitution of v for the variable x in e.

Target Language
The target language AFL ( Figure 11) is a self-adjusting language with modifiables. In addition to integers, products, and sums, the target type system makes a modal distinction between ordinary types (e.g. int) and modifiable types (e.g. int mod). It also distinguishes stable-mode and changeable-mode functions. Level polymorphism is supported through an explicit select construct and an explicit polymorphic instantiation. In Section 6, we describe how polymorphic source expressions become selects in AFL. The values of the language are integers, variables, polymorphic variable instantiation x[ α = δ], locations (which appear only at runtime), pairs, tagged values, stable and changeable functions, and the select construct, which acts as a function and case expression on levels: if x is bound to select {(α = S) ⇒ e1 | (α = C) ⇒ e2} then x[α = S] yields e1. The symbol x stands for a bare variable x or an instantiation x[ α = δ].
We distinguish stable expressions e S from changeable expressions e C . Stable expressions create purely functional values; apply S applies a stable-mode function. The mod construct evaluates a changeable expression and writes the output value to a modifiable, yielding a location, which is a stable expression. Changeable expressions are computations that end in a write of a pure value. Changeable-mode application apply C applies a changeable-mode function.
The let construct is either stable or changeable according to its body. When the body is a changeable expression, let enables a changeable computation to evaluate a stable expression and bind its result to a variable. The case expression is likewise stable or changeable, according to its case arms. The read expression binds the contents of a modifiable x to a variable y and evaluates the body of the read.

Dynamic Semantics
For the source language, our big-step evaluation rules (Figure 13) are standard. In the target language AFL, our rules (Figure 14) model the evaluation of a first run of the program: modifiables are created, written to (once), and read from (any number of times), but never updated to reflect changes to the program input. Both sets of rules permit expressions that are not in A-normal form, enabling

Translation
We specify the translation from Level ML to the target language AFL by a set of a rules. Because AFL is a modal language that distinguishes stable and changeable expressions, with a corresponding type system (Section 5), the translation is also modal: the translation in the stable mode → S produces a stable AFL expression e S , and the translation in the changeable mode → C produces a changeable expression e C . It is not enough to generate AFL expressions of the right syntactic form; they must also have the right type. To achieve this, the rules are type-directed: we translate a source expression e at type τ . But we are transforming expressions from one language to another, where each language has its own type system; translating some e : τ cannot produce some e : τ , but some e : τ where τ is a target type that corresponds to τ . To express this vital property, we need to translate types, as well as expressions. We developed the translation of expressions and types together (along with the proof that the property holds); the translation of types was instrumental in getting the translation of expressions right. To understand how to translate expressions, it is helpful to first understand how we translate types. Figure 15 defines the translation of types via two mutually recursive functions from Level ML types to AFL types. The first function, τ , tells us what type the target expression e S should have when we translate e in the stable mode, e : τ → S e S . We also use it to translate the types in the environment Γ. The second function, τ C , makes sense in two related situations: translating the type τ of an expression e in the changeable mode (e : τ → C e C ) and translating the codomain of changeable functions.

Translating Types
In the stable mode, values of stable type can be used and created directly, so the "stable" translation int S of a stable integer is ρ e ⇓ (ρ w) In the store ρ, target expression e evaluates to w with updated store ρ Figure 14. Dynamic semantics for first runs of AFL programs just int. In contrast, a changeable integer cannot be inspected or directly created in stable mode, but must be placed into a modifiable: int C = int mod. The remaining parts of the definition follow this pattern: the target type is wrapped with mod if and only if the outer level of the source type is C. When we translate a changeable-mode function type (with C below the arrow), its codomain is translated "output-changeable": The reason is that a changeable-mode function can only be applied in the changeable mode; the function result is not placed into a modifiable until we return to the stable mode, so putting a mod on the codomain would not match the dynamic semantics of AFL.
The second function τ C defines the type of a changeable expression e that writes to a modifiable containing τ , yielding a changeable target expression e C . The source type has an outer C, so when the value is written, it will be placed into a modifiable and have mod type. But while evaluating e C , there is no outer mod. Thus the translation τ C ignores the outer level (using the function − S , which replaces an outer level C with S), and never Figure 15. Stabilization of types τ S ; translations τ and τ C of types; translation of typing environments Γ returns a type of the form (· · · mod). However, since the value being returned may contain subexpressions that will be placed into modifiables, we use − for the inner types. For instance, (τ1 + τ2) δ C = τ1 + τ2 . These functions are defined on closed types-types with no free level variables. Before applying one of these functions to a type found by the constraint typing rules, we always need to apply the satisfying assignment φ to the type, so for convenience we write τ φ for [φ]τ , and so on. Because the translation only makes sense for closed types, type schemes ∀ α [D]. τ cannot be translated. The translation Γ therefore translates only monomorphic types τ ; type schemes are left alone (except for replacing the symbol ∀ with Π) until instantiation. Once instantiated, the type scheme is an ordinary closed source type, and can be translated by rule (TVar).

Translating Expressions
We define the translation of expressions as a set of type-directed rules. Given (1) a derivation of C; Γ ε e : τ in the constraintbased typing system and (2) a satisfying assignment φ for C, it is always possible to produce a correctly typed stable target expression e S and a correctly typed changeable target expression e C (see Theorem 6.1 below). The environment Γ in the translation rules is a source typing environment, but must have no free level variables. Given an environment Γ from the constraint typing, we apply the satisfying assignment φ to eliminate its free level variables before using it for the translation: [φ]Γ. With the environment closed, we need not refer to C.
Many of the rules in Figure 17 are purely syntax-directed and are similar to the constraint-based rules. One exception is the (Var) rule, which needs the source type to know how to instantiate the level variables in the type scheme. For example, given the polymorphic x : ∀α [true]. (int α → α int α ) S , we need the type from C; Γ ε x : (int C → C int C ) S so we can instantiate α in the translated term Under source typing Γ, renaming the "head" x in e to x : τ yields expression e Figure 16. Renaming the variable to be read (elimination forms) Our rules are nondeterministic, avoiding the need to "decorate" them with context-sensitive details. Our algorithm in Section 6.3 resolves the nondeterminism through type information.
Stable rules. The rules (Int), (Var), (Pair), (Fun), (Sum), (Fst) and (Prim) can only translate in the stable mode. To translate to a changeable expression, use a rule that shifts to changeable mode.
Shifting to changeable mode. Given a translation of e in the stable mode to some e S , the rules (Write) and (ReadWrite) at the bottom of Figure 17 translate e in the changeable mode, producing an e C . If the expression's type τ is outer stable (say, int S ), the (Write) rule simply binds it to a variable and then writes that variable. If τ is outer changeable (say, int C ) it will be in a modifiable at runtime, so we read it into r and then write it. (The let-bindings merely satisfy the requirements of A-normal form.) Shifting to stable mode. To generate a stable expression e S based on a changeable expression e C , we have the (Lift) and (Mod) rules. These rules require the source type τ to be outer changeable: in (Lift), the premise τ S = τ requires that τ S is defined, and it is defined only for outer changeable τ ; in (Mod), the requirement is explicit: τ O.C. (Mod) is the simpler of the two: if e translates to e C at type τ , then e translates to the stable expression mod e C at type τ . In (Lift), the expression is translated not at the given type τ but at its stabilized τ S , capturing the "shallow subsumption" in the constraint typing rules (SLetE) and (SLetV): a bound expression of type τ S 0 can be translated at type τ S 0 to e S , and then "promoted" to type τ C 0 by placing it inside a mod.
Reading from changeable data. To use an expression of changeable type in a context where a stable value is needed-such as passing some x : int C to a function expecting int S -the (Read) rule generates a target expression that reads the value out of x : int C into a variable x : int S . The variable-renaming judgment Γ e Y (x x : τ e ) takes the expression e, finds a variable x about to be used, and yields an expression e with that occurrence replaced by x . For example, Γ case x of . . . Y (x x : τ case x of . . .). This judgment is derivable only for apply, case, fst, and ⊕, because these are the elimination forms for outerchangeable data. For ⊕(x1, x2), we need to read both variables, so we have one rule for each. The rules are given in Figure 16.
Γ e : τ → ε e ε Under closed source typing environment Γ, source expression e is translated at type τ in mode ε to target expression e ε Γ n : int δ → S n (Int) Γ e : τ → C let r = e S in read r as r in write(r ) (ReadWrite) Figure 17. Monomorphizing translation

Monomorphization.
A polymorphic source expression has no directly corresponding target expression: the map function from Section 2 corresponds to the two functions map SC and map CS. Given a polymorphic source value v : ∀ α [D]. τ , the (LetV) rule translates v once for each instantiation δi that satisfies the constraint D (each δi such that α = δi D). That is, we translate the value at source type [ δi/ α]τ . This yields a sequence of source expressions e1, . . . , en for the n possible instances. For example, given ∀α [true]. τ , we translate the value at type [S/α]τ yielding e1 and at type [C/α]τ yielding e2. Finally, the rule produces a select expression, which acts as a function that takes the desired instance δi and returns the appropriate ei.
Since (LetV) generates one function for each satisfying δi, it can create up to 2 n instances for n variables. However, dead-code elimination can remove functions that are not used. Moreover, the functions that are used would have been handwritten in an explicit setting, so while the code size is exponential in the worst case, the saved effort is as well.

Algorithm
The system of translation rules in Figure 17 is not deterministic. In fact, if the wrong choices are made it can produce painfully inefficient code. Suppose we have 2 : int C , and want to translate it to a stable target expression. Choosing rule (Int) yields the target expression 2. But we could use (Int), then (ReadWrite)-which generates an e C with a let, a read and a write-then (Mod), which wraps that e C in a mod. Clearly, we should have stopped with (Int).
To resolve this nondeterminism in the rules would complicate them further. Instead, we give the algorithm in Figure 18, which examines the source expression e and, using type information, applies the rules necessary to produce an expression of mode ε.

Properties
Given a constraint-based source typing derivation and assignment φ for some term e, there are translations from e to (1) a stable e S and (2) a changeable e C , with appropriate target types: Theorem 6.1 (Translation Type Soundness). If C; Γ ε e : τ and φ is a satisfying assignment for C then (1) there exists e S such that [φ]Γ e : [φ]τ → S e S and ·; Γ φ S e S : τ φ and, if e is a value, then e S is a value; (2) there exists e C such that [φ]Γ e : [φ]τ → C e C and ·; Γ φ C e C : τ C φ . The proof (in the appendix [Chen et al. 2011]) is by induction on the height of the given derivation of C; Γ ε e : τ . If the concluding rule was (SLetE), we use a substitution property (Lemma A.2) for each δi to get a monomorphic constraint typing derivation; that derivation is not larger than the input derivation, so we can apply the induction hypothesis to get a translated e i . The proof constructs the same translation derivations as the algorithm in Figure 18 (in fact, we extracted the algorithm from the proof).
We also prove that running a translated program gives the same result as running the source program. Theorem 6.5 states that in an initially empty store ·, if evaluating the translated program e yields v with new store ρ , then e evaluates to v where v corresponds to [ρ ]v (the result of substituting values in the store ρ for locations appearing in v ).
To define this correspondence, we use a device somewhat similar to logical relations: a relation on source and target expressions, allowing us to show that if e : τ e : τ then v : τ [ρ]v : τ . Both e and e must be closed. Our definition is weaker than the equivalence relations used in logical relations proofs: apply(id, 4) 4, for example. It does not attempt to equate all programs that have the same meaning, but only particu- Figure 18. Translation algorithm lar Level ML terms to AFL terms that are similarly structured, but have overhead (mod, write, etc.). Thus, integers are related to integers, pairs are related if their components are related, and so forth. The definition essentially ignores mod and write and ignores the mode in apply ε . Since translated programs can have "extra" read and let expressions, these are "substituted out" in the relation, so that 3 let x = 3 in x. Functions are related if, given related arguments, they produce related results. Note that we will not induct over this relation; neither the term, nor the type, gets smaller. if, for all k from 1 to n, we have v k : τ k w k : τ k . The key lemma (Lemma 6.3) is that if e → ε e , the target program e is related to e. Combined with Theorem 6.4, which shows that related programs evaluate to related values, this means that the translated program e evaluates to the same value that e does. (Actually, e is a value related to that value; only at int δ /int, and products thereof, are they identical.) We begin by defining a store substitution operation: Finally, we extend Theorem 6.5 to further show that the size W (D) of the derivation of the target-language evaluation is within a constant factor of the size W (D ) of the derivation of e ⇓ v. We need a few definitions and intermediate results, which can be found in the appendix. The proof hinges on classifying the keywords added by the translation, such as write, as "dirty": a dirty keyword will lead to applications of the dirty rule (TEvWrite) in the evaluation derivation; such applications have no equivalent in the source-language evaluation.
We then define the "head cost" HC of terms and derivations, which counts the number of dirty rules applied near the root of the term, or the root of the derivation, without passing through clean parts of the term or derivation. Just counting all the dirty keywords in a term would not rule out a β-reduction duplicating a particularly dirty part of the term. By defining head cost and proving that the translation generates terms with bounded head cost-including for all subterms-we ensure that no part of the term is too dirty; consequently, substituting a subterm during evaluation yields terms that are not too dirty.   Acar et al. [2006] proved that given a well-typed AFL program, change propagation updates the output consistently with an initial run. Using Theorems 6.1 and 6.5, this implies that change propagation is consistent with an initial run of the source program.

Related Work
Incremental computation. Self-adjusting computation provides an approach to incremental computation, which has been studied extensively [Ramalingam and Reps 1993;Demers et al. 1981;Pugh and Teitelbaum 1989;Abadi et al. 1996]. Key techniques behind self-adjusting computation include dynamic dependence graphs, which allows a fully general change propagation mechanism [Acar et al. 2006], and a form of memoization that allows inexact computations to be reused via memoized computations that are (recursively) self-adjusting ]. Programming-language features allow writing self-adjusting programs but these require syntactically separating stable and changeable data, as well as code that operates on such data [Acar et al. 2006Ley-Wild et al. 2008;Hammer et al. 2009]. DITTO [Shankar and Bodik 2007] shows the benefits of eliminating user annotations. By customizing dependency tracking for invariant checking programs, DITTO provides a fully automatic incremental invariant checker. The ap-proach, however, is domain-specific and only works for certain programs (e.g., functions cannot return arbitrary values): it is unsound in general.

Information flow and constraint-based type inference.
A number of information flow type systems have been developed to check security properties, including the SLam calculus [Heintze and Riecke 1998], JFlow [Myers 1999] and a monadic system [Crary et al. 2005]. Our type system uses many ideas from Pottier and Simonet [2003], including a form of constraint-based type inference [Odersky et al. 1999], and is also broadly similar to other systems that use subtyping constraints [Simonet 2003;Foster et al. 2006].

Cost semantics.
To prove that our translation yields efficient selfadjusting target programs, we use a simple cost semantics. The idea of instrumenting evaluations with cost information goes back to the early '90s [Sands 1990]. Cost semantics is particularly important in lazy [Sands 1990;Sansom and Peyton Jones 1995]) and parallel languages [Spoonhower et al. 2008] where it is especially difficult to relate execution time to the source code, as well as in selfadjusting computation [Ley-Wild et al. 2009].

Conclusion
This paper presents techniques for translating purely functional programs to programs that can automatically self-adjust in response to dynamic changes to their data. Our contributions include a constraint-based type system for inferring self-adjustingcomputation types from purely functional programs, a type-directed translation algorithm that rewrites purely functional programs into self-adjusting programs, and proofs of critical properties of the translation: type soundness and observational equivalence, as well as the intrinsic property of time complexity. Perhaps unsurprisingly, the theorems and their proofs were critical to the determination of the type systems and the translation algorithm: many of our initial attempts at the problem resulted in target programs that were not type sound, that did not ensure observational equivalence, or were asymptotically slower than the source.
These results take an important step towards the development of languages and compilers that can generate code that can respond automatically to dynamically changing data correctly and asymptotically optimally, without substantial programming effort. Remaining open problems include generalization to imperative programs with references, techniques and proofs to determine or improve the asymptotic complexity of dynamic responses, and a complete and careful implementation and its evaluation.