This paper is about the extension of -calculus with explicit constructors for sharing. The simplest such construct is a expression, standing for where will be substituted by , that we also write more concisely as and call ES (for explicit sharing, or explicit subsitution111 expressions and explicit substitutions usually come with different operational semantics: expressions substitute in just one step, while explicit substitutions substitute in many micro steps, percolating through the term structure. They follow however the same typing principles. Moreover, explicit substitutions have many different formulations. In this paper we see expressions as yet another form of explicit substitutions, and thus conflate the two terminologies.). Thanks to ES, -reduction can be decomposed into more atomic steps. The simplest decomposition splits -reduction as .
It is well-known that ES are somewhat redundant, as they can always be removed, by simply coding them as -redexes. They are however more than syntactic sugar, as they provide a simple and yet remarkably effective tool to understand, implement, and program with -calculi and functional programming languages.
From a logical point of view, ES are the proof terms corresponding to the extension of natural deduction with a cut rule, and the cut rule is the rule representing computation, according to Curry-Howard. From an operational semantics point of view, they allow elegant formulations of subtle strategies such as call-by-need evaluation—various presentations of call-by-need use ES (Wadsworth, 1971; Launchbury, 1993; Maraist et al., 1998; Ariola et al., 1995; Sestoft, 1997; Kutzner and Schmidt-Schauß, 1998) and a particularly simple one is by Accattoli, Barenbaum, and Mazza in (Accattoli et al., 2014). From a programming point of view, they are part of most functional languages we are aware of. From a rewriting point of view, they enable proof techniques not available within the -calculus (e.g. reducing a global rewriting properties such as standardization to a local form, see Accattoli (2012)). Finally, sharing is used in all implementations of tools based on the -calculus to circumvent size explosion, the degenerate behaviour for which the size of -terms may grow exponentially with the number of -steps.
Once sharing is added to the -calculus, it enables a representation of terms where a sharing point is associated with every constructor of the term. Such a special form, roughly, is obtained by (recursively) decomposing iterated applications by introducing an ES in between any two of them. For instance, the representation of the term is
Note that the transformation involves also function bodies (i.e. turns into ), that ES are grouped together unless forbidden by abstractions, and that ES are flattened out, i.e. they are not nested unless nesting is forced by abstractions.
This work studies such a representation, called crumbled as it crumbles a term by means of ES. Our crumbling transformation closely resembles—while not being exactly the same—the transformation into a(dministrative) normal form (shortened to ANF), introduced by Flanagan et al. (1993), building on work by Sabry and Felleisen (1993), itself a variant of the continuation-passing style (CPS) transformation.
A delicate point is to preserve crumbled forms during evaluation. ES often come together with commutation rules to move them around the term structure. These rules are often used to unveil redexes during evaluation or to preserve specific syntactic forms. They may introduce significant overhead that, if not handled carefully, can even lead to asymptotic slowdowns as shown by Kennedy (2007). One of the contributions of this work is to show that crumbled forms can be evaluated and preserved with no need of commutation rules, therefore avoiding Kennedy’s potential slowdown.
The focus of our work is on the impact of crumbled forms on the design and asymptotic overhead of abstract machines with weak evaluation (i.e. out of abstractions) on closed terms, and the scalability to (possibly) open terms. Bounding the overhead of abstract machines is a new trend, according to which the machine overhead has to be proved polynomial or even linear in the number of -steps (Accattoli et al., 2014, 2015; Accattoli and Sacerdoti Coen, 2015; Accattoli, 2016; Accattoli and Guerrieri, 2017; Accattoli and Barras, 2017). Open terms—that are not needed to implement functional languages—are used to implement the more general and subtle case of proof assistants. The two topics actually motivate each other: the naive handling of open terms with the techniques for functional languages gives abstract machines with exponential overhead (Accattoli and Sacerdoti Coen, 2015; Accattoli and Guerrieri, 2017) which then pushes to develop more efficient machines.
We anticipate here the main results of the paper: crumbled forms induce abstract machines for weak evaluation with less data structures and the transformation does not introduce any asymptotic overhead. Moreover, these facts smoothly scale up to open terms.
Why study crumbled forms.
Our interest in studying crumbled forms comes precisely from the fact that they remove some data structures from the design of abstract machines. The relevance of this fact becomes evident when one tries to design abstract machines for strong evaluation (that is, evaluating under abstraction). The study of such machines is extremely technical (see also section Sect. 7) because they have more data structures and more transitions than in the closed and open cases. The many additional transitions are in particular due to the handling of the various data structures. In call-by-name, the situation is still manageable (Crégut, 1990; García-Pérez et al., 2013; Accattoli et al., 2015; Accattoli, 2016), but in call-by-value/need the situation becomes quickly desperate—it is not by chance that there is not a single strong abstract machine for call-by-value/need in the literature.
This work is then preliminary to a detailed study of strong abstract machines for call-by-value and call-by-need. The aim is to explore the subtleties in frameworks that are well understood, such as the closed and open call-by-value cases, and show that there are no slow downs in turning to a crumbled representation.
The next sub-sections continue the introduction with a lengthy overview of the role of environments, the content of the paper, the relationship with the ANF, the asymptotic study of abstract machines, and related work.
ES are often grouped together instead of being scattered all over the term, in finite sequences called environments. Abstract machines typically rely on environments. Crumbled forms also rely on packing ES together, as pointed out before, but depart from the ordinary case as environments may appear also under abstractions.
The notion of environment induced by crumbled forms, named here crumbled environments, is peculiar. Crumbled environments indeed play a double role: they both store delayed substitutions, as also do ordinary environments, and encode evaluation contexts. In ordinary abstract machines, the evaluation context is usually stored in data structures such as the applicative stack or the dump. Roughly, they implement the search for the redex in the ordinary applicative structure of terms. For crumbled forms, the evaluation context is encoded in the crumbled environment, and so the other structures disappear.
Operations on Crumbled Environments.
There are two subtle implementative aspects of crumbled environments, that set them apart from ordinary ones. Ordinary environments are presented with a sequential structure but they are only accessed randomly (that is, not sequentially)—in other words, their sequential structure does not play a role. Crumbled environments, as the ordinary ones, are accessed randomly, to retrieve delayed substitutions, but they are also explored sequentially—since they encode evaluation contexts—in order to search for redexes. Therefore, their implementation has to reflect the sequential structure.
The second subtlety is that crumbled machines also have to concatenate environments, that is an operation never performed by ordinary machines, and that has to be concretely implemented as efficiently as possible, i.e. in constant time. That this point is subtle is proved by the fact that Kennedy’s slowdown (Kennedy, 2007) amounts to a quadratic overhead in evaluating terms in ANF due to the concatenation of environments.
To address these points, we provide a prototype OCaml implementation of crumbled environments in Appendix F (p. F), to be compared with the one of global environments in Accattoli and Barras (2017), that does not concretely implement the sequential structure. In particular, our implementation concatenates environments in constant time and does not suffer from Kennedy’s slowdown. Essentially, Kennedy’s slowdown amounts to the fact that his implementation concatenates ANF environments in linear rather than constant time (see Appendix A).
1.2. Content of the Paper
The Closed Case.
First, we define crumbled forms and an abstract machine evaluating them, the Crumble GLAM, and show that it implements Plotkin’s closed small-step call-by-value (CbV for short) -calculus (extended with conditionals, see below). Moreover, we study the overhead of the machine, and show that it is linear in the number of -steps and in the size of the initial term, exactly as the best machines for CbV executing ordinary terms. Therefore, the crumbling transformation does not introduce any asymptotic overhead. The study is detailed and based on a careful and delicate spelling of the invariants of the machine. In particular, our approach does not suffer from Kennedy’s potential slowdown.
The second ingredient of the new trend of abstract machines (Accattoli et al., 2014, 2015; Accattoli and Sacerdoti Coen, 2015; Accattoli, 2016; Accattoli and Guerrieri, 2017; Accattoli and Barras, 2017)—the first being complexity analyses—is studying evaluation in presence of (possibly) open terms or even strong evaluation (i.e. under abstraction), which is required in order to implement proof assistants. Apart from few exceptions—Crégut (1990), Grégoire and Leroy (2002), and García-Pérez et al. (2013)—the literature before the new wave mostly neglected these subtle cases, and none of those three papers addressed complexity.
The open case, in which evaluation is weak but terms are possibly open is strictly harder than the closed one, and close in spirit to the strong case, but easier to study—it is for instance the one studied by Grégoire and Leroy (2002) when modelling (an old version of) the abstract machine of the kernel of Coq.
Open evaluation for CbV—shortened Open CbV—is particularly subtle because, as it is well-known, Plotkin’s operational semantics is not adequate when dealing with open terms—see Accattoli and Guerrieri (2016, 2018). Open CbV has been studied deeply by Accattoli and Sacerdoti Coen (2015); Accattoli and Guerrieri (2016, 2017, 2018), exploring different presentations, their rewriting, cost models, abstract machines, and denotational semantics. One of the motivations of this work is to add a new piece to the puzzle, by lifting the crumbling technique to the open case.
Our second contribution is to show that the crumbling technique smoothly scales up to Open CbV. We provide an abstract machine, the Open Crumble GLAM, and we show that it implements the fireball calculus—the simplest presentation of Open CbV—and that, as in the closed case, it only has a linear overhead. Two aspects of this study are worth pointing out. First, the technical development follows almost identically the one for the closed case, once the subtler invariants of the new machine have been found. Second, the substitution of abstractions on demand, a technical optimisations typical of open/strong cases (introduced in Accattoli and Dal Lago (2016) and further studied in Accattoli and Sacerdoti Coen (2015); Accattoli and Guerrieri (2017)), becomes superfluous as it is subsumed by the crumbling transformation.
1.3. The Relationship with ANF
As long as one sticks to the untyped -calculus, crumbled forms coincide with ANF. The ANF, we said, is a variant of the CPS transformation. Roughly, the difference is that the ANF does not change the type, when terms are typed (here we work without types).
Kennedy (2007) pointed out two problems with the ANF. One is the already discussed quadratic overhead, that does not affect our approach. The second one is the fact that the ANF does not smoothly scale up when the
-calculus is extended to further constructs such as conditionals or pattern matching. Essentially, the ANF requires conditionals and pattern matching to be out of ES, that is, to never have an expression such as. Unfortunately, these configurations can be created during evaluation. To preserve the ANF, one is led to add so-called commuting conversions such as:
Clearly, there is an efficiency issue: the commutation causes the duplication of the subterm . A way out is to use a continuation-like technique, which makes Kennedy conclude that then there is no point in preferring ANF to CPS.
This is where our crumble representation departs from the ANF, as we do not require conditionals and pattern matching to be out of ES. Kennedy only studies the closed case. Our interest in open and strong evaluation is to explore the theory of implementation needed for proof assistants. In these settings, commutations of conditionals and pattern matching such as those hinted at by Kennedy are not valid, as they are not validated by dependent type systems like those of Coq or Agda. For example, adding the CC rule above when the conditional is dependently typed breaks the property of subject reduction, as typed terms reduce to ill-typed terms. Consider the term:
that has type because the type of is convertible to . By applying rule CC, we obtain:
which is clearly ill-typed.
The problem in the open case is actually more general, as not even the CPS would work: its properties do not scale up to open terms. In Appendix A, indeed, we provide a counter-example to the simulation property in the open case.222Danvy and Filinski (1992) claim that the CPS transformation scales up to open terms (their Theorem 2). However, as we discuss in Appendix A, they consider only Plotkin’s operational semantics, which is not adequate for open terms.
To sum up, neither commuting conversions nor the CPS transformation can be used in our framework. Therefore, we accept that conditionals and pattern matching may appear in ES (in contrast to Kennedy) and so depart from the ANF.
In the paper we treat the cases of the closed and open CbV calculi extended with conditionals. The essence of the study is the crumbling of -reduction, not the conditionals. Conditionals are included only to stress the difference with respect to the ANF (pattern matching can be handled analogously), but they do not require a special treatment.
1.4. The Complexity of Abstract Machines
Asymptotic Bounds Benchmarking
The study of asymptotic bounds for abstract machines is meant to complement the use of benchmarking, by covering all possible cases, that certainly cannot be covered via benchmarking.
The relevance of such a study is particularly evident when one considers open terms or strong evaluation. For strong evaluation, for instance, for more than 25 years in the literature there has been only Cregut’s abstract machines (Crégut, 1990), which on size exploding families of terms actually has exponential overhead (in the number of -steps and the size of the initial term). A polynomial machine, developed via a careful asymptotic study, is in Accattoli (2016). Similarly, the abstract machine for open terms described in Grégoire and Leroy (2002) suffers of exponential overhead on size exploding families (even if the authors then in practice implement a slightly different machine with polynomial overhead). The asymptotic study of this case is in Accattoli and Sacerdoti Coen (2015); Accattoli and Guerrieri (2017).
Abstract machines vs compilation
Abstract machines and compilation to machine language are two distinct techniques to execute a program. Compilation is typically more efficient, but it only handles the case where terms are closed and evaluation is weak, that is, the one of functional languages. Strong evaluation is sometimes employed during compilation to optimise the compiled code, but typically only on linear redexes where size and time explosions are not an issue. Abstract machines are the only execution technique implemented in interactive theorem provers based on dependent types, that need strong evaluation.
Kennedy (2007) argues that CPS-based translations are superior to ANF also because the CPS makes join points explicit as continuations, so that invocation of the continuation can be compiled efficiently using jumps. The argument is only valid for compilation and it does not affect abstract machines.
We study abstract machines, that on purpose ignore many details of concrete implementations, such as garbage collection, that is an orthogonal topic. In particular, garbage collection is always at most polynomial, if not linear, and so its omission does not hide harmful blowups. As far as we know, no abstract machine implemented in interactive provers performs garbage collection.
1.5. Related Work
In a recent work, Accattoli and Barras (2017) compare various kinds of environments, namely, global, local, and split, from implementative and complexity points of view. The crumbling transformation can be studied with respect to every style of environment. Here we focus on crumbled global environments because they are simpler and because we also consider the open case, where all kinds of environment induce the same complexity.
Administrative Normal Forms.
The literature on ANF is scarce. Beyond the already cited original papers, Danvy has also studied them and their relationship to CPS, but usually calling them monadic normal forms (Danvy, 1994; Hatcliff and Danvy, 1994; Danvy, 2003) because of their relationship with Moggi’s monadic -calculus (Moggi, 1991). That terminology however sometimes describes a more liberal notion of terms, for instance in Kennedy (2007). Kennedy’s paper is also another relevant piece in the literature on ANF.
2. The Pif Calculus
The grammars and the small-step operational semantics of the Pif calculus , that is, Plotkin’s calculus (Plotkin, 1975) for Closed CbV evaluation extended with booleans and an construct, plus error handling for clashing constructs, are in Fig. 1.
A term is either an application of two terms, an , or a value, which is in turn either a variable, a (-)abstraction, , , or an error . We distinguish values that are not variables, noted and called practical values, following Accattoli and Sacerdoti Coen (2017). The body of an abstraction is and the bodies of a conditional are its two branches and . Terms are always identified up to -equivalence and the set of free variables of a term is denoted by ; is closed if , open otherwise. We use for the term obtained by the capture-avoiding substitution of for each free occurrence of in .
In general, contexts are denoted by and are terms with exactly one occurrence of a special constant called the hole, that is a placeholder for a removed subterm. In the paper we use various notions of contexts in different calculi—for the relevant notion is right (evaluation) v-context (see Fig. 1). The basic operation on (whatever notion of) contexts is the plugging of a term for the hole in : simply the hole is removed and replaced by , possibly capturing variables.
According to the definition of right v-context, CbV evaluation in is weak, i.e. it does not reduce under -abstractions and in the branches of an . CbV evaluation is defined for any (possibly open) term. But it is well-known that this operational semantics is adequate only for closed terms, as first noticed by Paolini and Ronchi Della Rocca (Ronchi Della Rocca and Paolini, 2004), see also Accattoli and Guerrieri (Accattoli and Guerrieri, 2016, 2018). When restricted to closed terms, is called Closed (Conditional) CbV: in this setting, evaluation can fire a -redex only if the argument is a closed value, i.e. a closed -abstraction, a boolean, or ; and in the production for the definition of right v-contexts, is always a closed value. Note that we work with right-to-left evaluation—this is forced by the production in the definition of right evaluation v-contexts. In the closed case one could as well work with left-to-right evaluation, the choice is inessential.
The error constant is generated during evaluation by the two cases of construct clashes: when the condition for an is an abstraction and when a boolean is applied to a term. Both cases would be excluded by typing, but in our untyped setting they are possible, and handled via errors. Similarly, errors are also propagated when they appear as conditions for and as left terms of an application. These cases are handled by rules and . Note that errors do not propagate when they occur as arguments of applications: if the left sub-term of the application becomes an abstraction that erases the error then the error is handled and it is not observable.
A key property of Plotkin’s Closed CbV is harmony: a closed term is -normal if and only if it is a (closed) value i.e. a (closed) -abstraction. Therefore, every closed term either diverges or it evaluates to a (closed) -abstraction. Harmony extends to .
Proposition 2.1 (Pif harmony).††margin: Proof p. B
Let be a closed term. is -normal if and only if is a value.
Pif calculus .
3. Crumbled Evaluation, Informally
The idea is to forbid the nesting of non-value constructs such as applications and without losing expressive power. To ease the explanation, we focus on nested applications and forget about —they do not pose any difficulty. Terms such as or are then represented as and where is a fresh variable. It is usually preferred to use expressions rather than introducing -redexes, so that one would rather write and , or, with ES (aka environment entries), rather write
If the crumbling transformation is applied to the whole term—recursively on , and in our examples—all applications have the form , i.e. they only involve values. If moreover CbV evaluation is adopted, then such a crumbled form is stable by evaluation (reduction steps are naturally defined so that a crumbled form reduces to a crumbled form), as variables can only be replaced by values.
Simulation and no evaluation contexts.
Let us now have a look at a slightly bigger example and discuss the recursive part of the crumbling transformation. Let be the identity and consider the term whose right-to-left evaluation is
The crumbling transformation decomposes all applications, taking special care of grouping all the environment entries together, flattening them out (that is, avoiding having them nested one into the other), and reflecting the evaluation order in the arrangement of the environment. For instance, the crumbled representation of the term above is
and evaluation takes always place at the end of the environment, as follows:
where the steps correspond exactly to steps in the ordinary evaluation of and steps simply eliminate the explicit substitution when its content is a value. Note how the transformation makes the redex always appear at the end of the environment, so that the need for searching for it—together with the notion of evaluation context—disappears.
Let us also introduce some terminology. Values and applications of values are bites. The transformation, called crumbling translation, turns a term into a bite with an environment—such a pair is called a crumble.
Turning to micro-step evaluation.
The previous example covers what happens when the crumbling transformation is paired with small-step evaluation. Abstract machines, however, employ a finer mechanism that we like to call micro-step evaluation, where the substitutions due to -redexes are delayed and represented as new environment entries, and moreover substitution is decomposed as to act on one variable occurrence at a time. In particular, such a more parsimonious evaluation never removes environment entries because they might be useful later on—garbage collection is assumed to be an orthogonal and independent process. To give an idea of how micro steps work, let’s focus on the evaluation of the subterm of our example (because micro-step evaluations are long and tedious), that proceeds as follows:
where steps now introduce new environment entries. Now the redex is not always at the end of the environment, but it is always followed on the right by an environment whose entries are all abstractions, so that the search for the next redex becomes a straightforward visit from right to left of the environment—the evaluation context has been coded inside the sequential structure of the environment.
Abstraction bodies and the concatenation of environments.
There is a last point to explain. We adopt weak evaluation—that only evaluates out of abstractions—but the crumbling transformation also transforms the bodies of abstractions and the branches of into crumbles. Let us see another example. The crumbled representation of then is
Micro-step evaluation goes as follows:
At this point, the reduction of the -redex (involving ) has to combine the crumble of the redex itself with the one of the body of the abstraction, by concatenating the environment of the former (here ) at the end of the environment of the latter (), interposing the entry created by the redex itself (), thus producing the new crumble:
The key conclusion is that evaluation needs to concatenate crumbled environments, which is an operation that ordinary abstract machines instead never perform.
Note that transforming abstraction bodies may produce nested ES, if the abstraction occurs in an ES. This is the only kind of nesting of ES that is allowed.
4. The Crumbling Transformation
In this section we formally define the language of crumbled forms and the crumbling transformation.
Terms are replaced by crumbles, which are formed by a bite and an environment, where in turn
a bite is either a crumbled value (i.e. a variable, a boolean, an error, or an abstraction over a crumble), an application of crumbled values, or a on a crumbled value whose alternatives are crumbles, and
an environment is a finite sequence of explicit substitutions of bites for variables.
Formally, the definition is by mutual induction:
Bodies: the bodies of abstractions and are themselves crumbles—the forthcoming crumbling transformation is indeed strong, as it also transforms bodies.
Crumbles are not closures: the definition of crumbles may remind one of closures in abstract machines with local environments, but the two concepts are different. The environment of a crumble , indeed, does not in general bind all the free variables of the bite .
We freely consider environments as lists extendable on both ends, and whose concatenation is obtained by simple juxtaposition. Given a crumble and an environment the appending of to is .
Free variables, -renaming, and all that.
All syntactic expressions are not considered up to -equivalence. Free variables are defined as expected for bites. For environments and crumbles they are defined as follows (via the auxiliary notion of domain of environments; this is because global environments are used here):
Let be an environment: we denote the lookup of in by . We say that a crumble or an environment are well-named if all the variables occurring on the lhs of ES outside abstractions in or are pairwise distinct.
The crumbling translation.
A term is turned into a crumble via the following crumbling translation , which uses an auxiliary translation from values into crumbled values.
if is not a value and , and is fresh.
According to the definition, if and are not values, with , and fresh.
Example 4.1 ().
Let and : , (as ), and . So,
The crumbling translation is not surjective: the crumble is such that for any term .
There is a left inverse for the crumbling translation, called read-back and defined by:
Proposition 4.2 (Read-back and the crumbling translation).††margin: Proof p. C
For every term and every value , one has and .
Remark 4.1 (Crumbling translation, free variables).
For any term and any value , one has and ; in particular, is closed if and only if is so.
For any bite and any crumble , and .
The crumbling translation commutes with the renaming of free variables.
The crumbling translation and the read-back map values to values.
For crumbled forms, we need contexts both for environments and crumbles:
Crumbles can be plugged into both notions of contexts. Let us point out that the following definition of plugging is slightly unusual as it does a little bit more than just replacing the hole, because simply replacing would not provide a well-formed syntactic object: plugging indeed extracts the environment from the plugged crumble and concatenates it with the environment of the context. Such an unusual operation—that may seem ad-hoc—is actually one of the key technical points in order to obtain a clean proof of the implementation theorem.
Definition 4.3 (Plugging in crumbled contexts).
Let be an environment context, be a crumble context, and be a crumble. The plugging of in and the plugging of in are defined by
Example 4.4 ().
In Ex. 4.1 we have seen that , where we set for any bite . We have that with and
The notions of well-named, , and can be naturally extended to crumble contexts. The definition of read back is extended to crumble contexts by setting and . Note however that the unfolding of a crumble context is not necessarily a context, because the hole can be duplicated or erased by the unfolding. For instance, let . Then is not a context.
Lemma 4.5 provides the properties of the translation needed to prove the invariants of machines in the next sections.
Lemma 4.5 (Properties of crumbling).††margin: Proof p. C
For every term :
Freshness: is well-named.
Closure: if is closed, then .
Disjointedness: if .
Bodies: every body in is the translation of a term.
Contextual decoding: if , then is a right v-context.
5. The Closed Case
Here we show how to evaluate crumbled forms with a micro-step operational semantics. We builds over the work of Accattoli and co-authors, who employ the following terminology:
Calculus: for a small-step semantics where both substitution and search for the redex are meta-level operations;
Linear calculus: for a micro-step semantics where substitution is decomposed—the calculus has ES and possibly a notion of environment if the ES are grouped together—but the search for the redex is still meta-level and expressed via evaluation contexts;
Abstract machine: for a micro-step semantics where both substitution and search for the redex are decomposed. The search for redexes is handled via one or more stacks called applicative stack, dump, frame, and so on; the management of names is also explicit, i.e. not up-to -equivalence.
The crumbling transformation blurs the distinction between a linear calculus and an abstract machine because it allows using the sequential structure of the environment as the only stack needed to search for redexes.
The operational semantics for crumbled forms that we present next is in the style of a linear calculus, because spelling out the straightforward search for redexes is not really informative. Nonetheless, we do call it an abstract machine, because of the blurred distinction in the crumble case and because we manage names explicitly. In Appendix D.4 (p. D.4) we spell out the actual abstract machine.
5.1. The Crumble GLAM
To introduce the Crumble GLAM (GLAM stands for Global Leroy Abstract Machine) we need some definitions. First, environments and crumbles made out of practical values only are defined and noted as follows:
Essentially, a -environment stands for the already evaluated coda of the environment described in the paragraph about micro-steps in Sect. 3, while -crumbles are fully evaluated crumbles (i.e. final states of the machine), as we show below.
Second, given a crumble we use for a crumble obtained by -renaming the names in the domain of with fresh ones so that is well-named.
The transitions act on crumbles whose environments are -environments. The top level transitions are:
Transitions are then closed by crumble contexts: for every define if . The transition relation of the Crumble GLAM is defined as the union of all these rules. Let us explain each transition:
: (forget about the
-renaming for the moment—see the next paragraph) the rule removes a-redex and introduces an ES instead of performing the meta-level substitution. Moreover, the environment of the body of the abstraction and the external environment are concatenated (via the appending operation ) interposing .
Conditional and error transitions : these transitions simply mimics the analogous rules on the Pif calculus, with no surprises.
Substitution transitions : the variable is substituted by the corresponding crumbled value in the environment , if any. In the closed case, a forthcoming invariant guarantees that is always defined so that side-condition (3) is actually always satisfied. There are no rules to substitute on the right of an application, we explain this below.
Note that, according to the definitions of plugging and top level transitions, the transition relation follows right-to-left evaluation, since the environment on the right of a redex is a -environment, i.e. it is made of practical values only, which means that it has already been evaluated (see the harmony property for Crumble GLAM in Prop. 5.3 below). Adopting right-to-left evaluation implies that the Crumble GLAM does not need a rule symmetrical to , whose top level shape would be with : indeed, if is a variable then applies to the same redex , otherwise is an abstraction and then applies to .
The cost and the place of -renaming.
Abstract machines with global environments have to -rename at some point, this is standard333Local environments do allow to avoid renamings, but the simplification is an illusion, as the price is payed elsewhere—see Accattoli and Barras (2017)—there is no real way out.. In our implementation, renaming is implemented as a copy function. And the cost of renaming is under control because of forthcoming invariants of the machine. This is all standard (Accattoli and Barras, 2017). Often the burden of renaming/copying is put on the substitution rules. It is less standard to put it on the -transition, as we do here, but nothing changes. Last, a technical remark: in rule the
-renaming at top level has to pick names that are fresh also with respect to the crumble context enclosing it. This point may seem odd but it is necessary to avoid name clashes, and it is trivially obtained in our concrete implementation, where variable names are memory locations and picking a fresh name amounts to allocating a new location, that is of course new globally.
Definition 5.1 (Reachable crumble).
A crumble is reachable (by the Crumble GLAM) if it is obtained by a sequence of transitions starting from the translation of a closed term .
The substitution performed by the rule may seem an unneeded optimization; quite the opposite, it fixes an issue causing quadratic overhead in the machine. The culprits are malicious chains of renamings, i.e. environments of the form substituting variables for variables and finally leading to an abstraction. Accattoli and Sacerdoti Coen (2015) showed that the key to linear overhead is to perform substitution steps while going through the chain from right to left.
Example 5.2 ().
Consider the crumble , where ; then:
Consider now the open crumble
The crumble is normal because its only possible decomposition of the form is for (as is not a practical value), and no transitions apply to the rightmost entry since is free.
The Crumble GLAM satisfies a harmony property.
Proposition 5.3 (Harmony for the Crumble GLAM).††margin: Proof p. D.1
A closed crumble is normal if and only if it is a -crumble.
5.2. The Implementation Theorem
To show that the Crumble GLAM correctly implements the Pif calculus, we apply an abstract approach introduced by Accattoli and Guerrieri (2017), which we reuse as well in the following sections for other crumble abstract machines and other evaluation strategies of the -calculus.
The implementation theorem, abstractly.
In Accattoli and Guerrieri (2017) it is proven that, given
a generic abstract machine , which is a transitions relation over a set of states that splits into
principal transitions , that corresponds to the evaluation steps on the calculus, and
overhead transitions , that are specific of the machine,
an evaluation strategy in the -calculus, and
a decoding of states of into terms,
correctly implements via whenever forms an implementation system, i.e. whenever the following conditions are fulfilled (where and stand for generic states of ):
Initialization: there is an encoding of terms such that ;
Principal projection: implies ;
Overhead transparency: implies ;
Determinism: is deterministic;
Halt: final states (to which no transition applies) decode to -normal terms;
Overhead Termination: terminates.
Our notion of implementation, tuned towards complexity analyses, requires a perfect match between the number of steps of the strategy and the number of principal transitions of the execution.
Theorem 5.4 (Machine Implementation, (Accattoli and Guerrieri, 2017)).
If a machine , a strategy on -terms and a decoding form an implementation system then:
Executions to derivations: for any -execution there is a -derivation