Parameterized cast calculi and reusable meta-theory for gradually typed lambda calculi

Abstract The research on gradual typing has led to many variations on the Gradually Typed Lambda Calculus (GTLC) of Siek & Taha (2006) and its underlying cast calculus. For example, Wadler and Findler (2009) added blame tracking, Siek et al. (2009) investigated alternate cast evaluation strategies, and Herman et al. (2010) replaced casts with coercions for space efficiency. The meta-theory for the GTLC has also expanded beyond type safety to include blame safety (Tobin-Hochstadt & Felleisen, 2006), space consumption (Herman et al., 2010), and the gradual guarantees (Siek et al., 2015). These results have been proven for some variations of the GTLC but not others. Furthermore, researchers continue to develop variations on the GTLC, but establishing all of the meta-theory for new variations is time-consuming. This article identifies abstractions that capture similarities between many cast calculi in the form of two parameterized cast calculi, one for the purposes of language specification and the other to guide space-efficient implementations. The article then develops reusable meta-theory for these two calculi, proving type safety, blame safety, the gradual guarantees, and space consumption. Finally, the article instantiates this meta-theory for eight cast calculi including five from the literature and three new calculi. All of these definitions and theorems, including the two parameterized calculi, the reusable meta-theory, and the eight instantiations, are mechanized in Agda making extensive use of module parameters and dependent records to define the abstractions.


Introduction
The theory of gradual typing has grown at a fast pace, since the idea crystallized in the mid 2000s (Siek & Taha, 2006a;Tobin-Hochstadt & Felleisen, 2006;Matthews & Findler, 2007;Gronski et al., 2006). Researchers have discovered many choices regarding the design and formalization of gradually typed languages. For example, a language designer can choose between runtime casts that provide lazy, eager, or even partially eager semantics (Siek et al., 2009;García-Pérez et al., 2014). Alternatively, the designer might apply the methodology of Abstracting Gradual Typing (AGT) to derive the semantics (Garcia et al., 2016). When a runtime casts fails, there is the question of who to blame, using either the D or UD blame-tracking approaches (Siek et al., 2009). Furthermore, with the need to address the problems of space efficiency (Herman et al., 2010), one might choose to use threesomes (Siek & Wadler, 2010), supercoercions (Garcia, 2013), or coercions in one of several normal forms (Siek & Garcia, 2012;Siek et al., 2015a).
The last decade has also seen tremendous progress in the mechanization of programming language theory (Aydemir et al., 2005). It has become routine for researchers to use proof assistants such as Coq (The Coq Dev. Team, 2004), Isabelle (Nipkow et al., 2007), or Agda (Bove et al., 2009) to verify the proofs of the meta-theory for a programming language. From the beginning, researchers in gradual typing used proof assistants to verify type safety (Siek & Taha, 2006c,b;Tobin-Hochstadt & Felleisen, 2008). They continue to mechanize the type soundness of new designs (Siek & Vitousek, 2013;Chung et al., 2018) and to mechanize proofs of new properties such as open-world soundness and the gradual guarantee (Siek et al., 2015b;Vitousek & Siek, 2016b;Xie et al., 2018).
While machine-checked proofs provide the ultimate degree of trust, they come at a high development cost. With this in mind, it would be useful to reduce the cost by reusing definitions, theorems, and proofs about gradually typed languages. Agda provides particularly nice support for reuse: its combination of parameterized modules, dependent types, and more specifically, dependent records, provide a high degree of flexibility at a relatively low cost in complexity. For this reason, we choose to develop a new mechanization in Agda instead of building on prior mechanizations of gradual typing in other proof assistants.
This article starts at the beginning, focusing on a language with first-class functions and constants, that is, the Gradually Typed Lambda Calculus (GTLC) (Siek & Taha, 2006a), with the addition of pairs and sums whose presence helps to reveal some patterns that require abstraction.
The Gradually Typed Lambda Calculus. We present a formalization of the static semantics of the GTLC in Section 2, including the type system and definitions of important relations such as consistency and type precision. We present a proof of the static gradual guarantee: that changing type annotations to be less precise preserves typing. This is not the first mechanization of the static gradual guarantee for the GTLC (Siek et al., 2015b); it is here for the sake of completeness.
The dynamic semantics of the GTLC is defined by translation to a cast calculus. We present that translation in Section 4 and prove that it preserves types and is monotone with respect to precision. There are many different cast calculi to choose from, with subtle

Gradually Typed Lambda Calculus
In this section, we formalize the static semantics of the GTLC of Siek & Taha (2006a) with the addition of products and sums. We use de Bruijn representation of variables and define the terms by leveraging the Abstract Binding Tree library. Their typing rules are defined as an Agda data type. The dynamic semantics of the GTLC is defined by translation to a cast calculus in Section 4.
Types. Figure 1 defines the set of types T of the GTLC, which includes function types, product types, sum types, and atomic types. The atomic types include the base types (natural numbers, Booleans, etc.) and the unknown type, written ? (aka. the dynamic type). As usual, the unknown type represents the absence of static type information. Figure 1 defines typing contexts , which are sequences of types, that is, they map variables (represented by de Bruijn indices) to types. Figure 1 defines the consistency relation at the heart of gradual typing. We say that two types are consistent, written A ∼ B, if they are equal except in spots where either type contains the unknown type. For example, Nat → Bool ∼ ? → Bool Because ? ∼ Nat and Bool ∼ Bool. The rules for consistency in Figure 1 are labeled with their proof constructors. For example, the following is a proof of the above example: We use a colon for Agda's "proves" relation, which can also be read as a "has-type" relation thanks to the Curry-Howard correspondence (Howard, 1980). In the rule Fun∼, the A and A flip in the premise, which is unusual but does not matter; it just makes some of the Agda proofs easier. Figure 1 also defines a join function that computes the least upper bound of two types with respect to the precision relation (with ? at the bottom) (Siek & Taha, 2006a;Siek & Vachharajani, 2008), which is defined in Figure 4 of Section 2.1. The join function is typically defined on a pair of types and is a partial function where it is defined if and only if the two types are consistent. Here, we instead make a total function over proofs of consistency.
Finally, Figure 1 defines the matching relation A B that is used in the typing rules for eliminators (such as function application and accessing elements of a pair). The matching relation handles the situation where the function or pair expression has type ?.
Variables. The function V, defined in Figure 3, maps a typing context and type A to the set of all variables that have type A in context . As stated above, variables are de Bruijn indices, that is, natural numbers where the number x refers to the xth enclosing lambda abstraction. There are two constructors for variables: Z (zero) and S (plus one). The two rules in Figure 3 correspond to the signatures of these two constructors, where premises (above the line) are the parameter types and the conclusion (below the line) is the result type. The variable Z refers to the first lexical position in the enclosing context, so Z takes no parameters, and its result type is V A if is a non-empty typing context where type A is at the front. A variable of the form S x refers to one position further out than that of x. So the constructor S has one parameter, a variable in V A for some and A, and its result type is a variable in V ( · B) A, for any type B. An expression formed by combinations of the constructors Z and S is a proof of a proposition of the form V A. For example, S S Z is a proof of V (∅ · Bool · Nat · Int) Bool because Bool is at position 2 in the typing context ∅ · Bool · Nat · Int.

Constants.
We represent the constants of GTLC by a particular set of Agda values. First, we carve out the subset of GTLC types that a constant is allowed to have, which are the base types and n-ary functions over base types. We call them primitive types and inductively define the predicate P A in Figure 3 to identify them. We then define a mapping · from P A to Agda types (elements of Set), also in Figure 3.
Terms. The syntax of GTLC terms is defined in Figure 2 and their typing rules are presented in Figure 3 Tree library, so they are extrinsically typed. As usual, we define the typing judgment as G M : A, represented by an Agda data type. The constant $k has type P in context provided that the Agda value k has type P and P proves that A is a primitive type. A variable 'x has type A in context if x is a de Bruijn index in V A (explained above).
As usual, a lambda abstraction (λ[A] M) has type A → B in context provided that M has type B in context · A. Lambda abstraction does not include a parameter name because we represent variables as de Bruijn indices. An application (L M) has type B if L has some type A that matches a function type A 1 → A 2 , M has some type B, and B is consistent with A 1 . The blame label is a unique identifier for this location in the source code. Each blame label has a complement and the complement operation is involutive, that is, = .
The term if L M N requires that the type of L is consistent with Bool, M and N have consistent types, and the type of the if as a whole is the join of the types of M and N. The rules for pairs and projection are straightforward. Regarding sums, in case L M N, the type of L is consistent with the sum type B 1 + C 1 . A variable of type B 1 goes into scope for the branch M and a variable of type C 1 goes into scope for N. The types of the branches, B 2 and C 2 , must be consistent and the type of the case is the join of B 2 and C 2 .
Examples. The following are a few example terms and their typing derivations in the GTLC. Figure 4 presents the definitions of the precision relations on GTLC types, terms, and typing contexts. In particular, a type A is less precise than A , written A A , if A and A are equal except in the places where A has type ?. The precision relation * between typing contexts is straightforward. We require that each variable has types that are related by in the respective typing contexts. We say term M in GTLC is more precise than M if the type annotations in the former are more precise than the corresponding annotations in the latter, written M G M . For example, An important property of a gradual type system is that reducing the precision of type annotations in a well-typed term should yield a well-typed term, a property known as the static gradual guarantee (Siek et al., 2015b). Here, we extend this result for the GTLC to include products and sums. Case L M The induction hypothesis for L produces two subcases: The term L in L M G L M can be either of ? or of function type. If L is of type ?, the theorem is proved by type matching and ? being less precise than any type. On the other hand, if L is of function type, it is proved by the fact that the codomain types satisfy the precision relation.

Static gradual guarantee
Other cases follow the same structure as function application: by the induction hypotheses for subterms and casing on whether the left side of a precision relation can be ? whenever necessary.

Parameterized Cast Calculus
Recall that the dynamic semantics of the GTLC is defined by translation to a cast calculus. There are many cast calculi in the literature with differing semantics and efficiency guarantees. In this section, we define the Parameterized Cast Calculus CC(⇒) that captures the similarities in many of those calculi, for the purposes of language specification. (As a guide to implementation, we present the Space-Efficient Parameterized Cast Calculus in Section 6.) This section begins with the static semantics of CC(⇒) and a number of parameters packaged into PreCastStruct (Section 3.1), which is then used to express the notion of Value, evaluation contexts (Section 3.2), and eta-style cast reductions (Section 3.3). We introduce another parameter, for applying a cast to a value, in CastStruct (Section 3.4), and then define the dynamic semantics of CC(⇒), with definitions of substitution (Section 3.5) and the reduction semantics (Section 3.6).
We present CC(⇒) as an intrinsically typed calculus with de Bruijn representation of variables, analogous to the formalization of the Simply Typed Lambda Calculus in the DeBruijn Chapter of PLFA. As such, the terms are represented by derivations of their typing judgment. The judgment has the form A, which says that type A can be inhabited in context . So terms, ranged over by L, M, N, are proofs of propositions of the form A. For example, a typing judgment normally written M : A here has the form M : A. The term constructors for CC(⇒) are defined in Figure 5. Like most cast calculi, CC(⇒) extends the Simply Typed Lambda Calculus with the unknown type ? and explicit runtime casts. Unlike other cast calculi, the CC(⇒) calculus is parameterized over the representation of casts, that is, the parameter ⇒ is a function that, given a source and target type, returns the representation type for casts from A to B. So c : A ⇒ B says that c is a cast from A to B.
The types and variables of the Parameterized Cast Calculus are the same as those of the GTLC (Section 2). The intrinsically typed terms of the Parameterized Cast Calculus are defined in Figure 5. Cast application is written M c where the cast representation c is not concrete but is instead specified by the parameter ⇒. In the literature, some cast calculi annotate the cast application form with a blame label. In other cast calculi, such as coercion-based calculi or threesomes, blame labels instead appear inside the cast c. For purposes of uniformity, we place the responsibility for blame tracking inside the cast representation, which means they do not appear directly in the cast application form of the Parameterized Cast Calculus. As usual there is an uncatchable exception blame .

The PreCastStruct structure
We introduce the first of several structures (as in algebraic structures) that group together parameters needed to define the notion of values, frames, and the reduction relation for the Parametric Cast Calculus. The structures are represented in Agda as dependent records.
The PreCastStruct structure includes the operations and predicates that do not depend on the terms of the cast calculus so that this structure can be reused for different cast calculi, such as the space-efficient cast calculus in Section 6. The CastStruct structure extends PreCastStruct with the one additional operation that depends on terms, which is applyCast.
One of the main responsibilities of the PreCastStruct structure is categorizing casts as either active or inert. An active cast is one that needs to be reduced by invoking applyCast (see reduction rule (cast) in Figure 8). An inert cast is one that does not need to be reduced, which means that a value with an inert cast around it forms a larger value (see the definition of Value in Figure 6). Different cast calculi may make different choices regarding which casts are active and inert, so the PreCastStruct structure includes the two predicates Active and Inert to parameterize these differences. The proof of Progress (Theorem 14) needs to know that every cast can be categorized as either active or inert, so PreCastStruct also includes the ActiveOrInert field which is a (total) function from casts to their categorization as active or inert.
The reduction semantics must also identify casts whose source and target type have the same head type constructor, such as a cast from A × B to C × D. We refer to such casts as cross casts and include the Cross predicate in the PreCastStruct structure. When a cross cast is inert, the reduction semantics must decompose the cast when reducing the elimination form for that type. For example, when accessing the fst element of a pair V that is wrapped in an inert cast c, we decompose the cast using an operator, also called fst, provided in the PreCastStruct structure. Here is the relevant reduction rule, which is (fst-cast) in Figure 8: Finally, the proof of Progress (Theorem 14) also needs to know that the cast is a cross cast when the target type of an inert cast is a function, product, or sum type. Thus, the PreCastStruct includes the fields InsertCross→, InsertCross×, and InsertCross+. The target type of an inert cast may not be a base type (field baseNotInert) to ensure that the canonical forms at base type are just constants.
The fields of the PreCastStruct record as follows: Given the source and target type, this returns the Agda type for casts.
This predicate categorizes the inert casts, that is, casts that when combined with a value, form a value.
This predicate categorizes the active casts, that is, casts that require a reduction rule to specify what happens when they are applied to a value. ActiveOrInert : ∀AB. (c : A ⇒ B) → Active c Inert c All casts must be active or inert, which is used in the proof of Progress.
This predicate categorizes the cross casts, that is, casts from one type constructor to the same type constructor, such as A → B ⇒ C → D. This categorization is needed to define other fields below, such as dom.
An inert cast whose target is a function type must be a cross cast. This field and the following two fields are used in the proof of Progress.
An inert cast whose target is a pair type must be a cross cast.
An inert cast whose target is a sum type must be a cross cast. baseNotInert : ∀Ab. (c : A ⇒ b) → ¬Inert c A cast whose target is a base type must never be inert. This field is used in the proof of Progress. dom : Given a cross cast between function types, dom returns the part of the cast between their domain types. As usual, domains are treated contravariantly, so the result is a cast from B 1 to A 1 .
Given a cross cast between function types, cod returns the part of the cast between the codomain types. fst : Given a cross cast between pair types, fst returns the part of the cast between the first components of the pair. snd : Given a cross cast between pair types, snd returns the part of the cast between the second components of the pair. inl : Given a cross cast between sum types, inl returns the part of the cast for the first branch. inr : Given a cross cast between sum types, inr returns the part of the cast for the second branch.

Values and frames of CC(⇒)
This section is parameterized by a PreCastStruct. So, for example, when we refer to ⇒ and Inert, we mean those fields of the PreCastStruct. The values of the Parameterized Cast Calculus are defined in Figure 6. The judgment Value M says that the term M is a value. Let V and W range over values. The only rule specific to gradual typing is Vcast which states that a cast application V c is a value if c is an inert cast.
A value of type ? must be a cast application where the cast is inert and its target type is ?. In the reduction semantics, we use single-term evaluation contexts, called frames, to collapse the many congruence rules that one usually finds in a structural operational semantics into a single congruence rule. Unlike regular evaluation contexts, frames are not recursive. Instead that recursion is in the congruence reduction rule.
The definition of frames for the Parameterized Cast Calculus is given in Figure 6. The definition is typical for a call-by-value calculus. We also define the plug function at the bottom of Figure 6, which replaces the hole in a frame with a term, producing a term.
The plug function is type preserving. This is proved in Agda by embedding the statement of this lemma into the type of plug (see Figure 6) and then relying on Agda to check that the definition of plug satisfies its declared type.

Lemma 4 (Frame Filling). If M :
A and F : A B, then plug M F : B.

The eta cast reduction rules
This section is parameterized by a PreCastStruct. Some cast calculi include reduction rules that resemble η-reduction (Flanagan, 2006;Siek & Taha, 2006a). For example, the following rule reduces a cast between two function types, applied to a value V , by η-expanding V and inserting the appropriate casts: Here, we define three auxiliary functions that apply casts between two function types, two pair types, and two sum types, respectively. Each of these functions requires the cast c to be a cross cast. These auxiliary functions are used by cast calculi that choose to categorize these cross casts as active casts.

The CastStruct structure
The CastStruct record type extends PreCastStruct with one more field, for applying an active cast to a value. Thus, this structure depends on terms of the cast calculus:

Substitution in CC(⇒)
We define substitution functions ( Figure 7)   variables in M. The notation M[N] is meant to be used for β reduction, where M is the body of the lambda abstraction and N is the argument. What M[N] does is substitute Z for N in M and also transports M above the lambda by incrementing the other free variables. All this is accomplished by building a substitution substZero N (also defined in Figure 7) and then applying it to M. Substitution is type preserving, which is established by the following sequences of lemmas. As usual, we prove one theorem per function. In Agda, these theorems are proved by embedding their statements into the types of the four functions. Given a sequence S, we write S!i to access its ith element. Recall that and range over typing contexts (which are sequences of types).

If M :
A, then rename ρ M : A.

Reduction semantics of CC(⇒)
This section is parameterized by CastStruct. Figure 8 defines the reduction relation for CC(⇒). The last eight rules are typical of the Simply-Typed Lambda Calculus, including rules for function application, conditional branching, projecting the first or second element of a pair, and case analysis on a sum. The congruence rule (ξ ) says that reduction can happen underneath a single frame. The rule (ξ -blame) propagates an exception up one frame. Perhaps the most important rule is (cast), for applying an active cast to a value. This reduction rule simply delegates to the applyCast field of the CastStruct. The next four rules (fun-cast, fst-cast, snd-cast, and case-cast) handle the possibility that the CastStruct categorizes casts between functions, pairs, or sums as inert casts. In such situations, we need reduction rules for when cast-wrapped values flow into an elimination form. First, recall that the PreCastStruct record includes a proof that every inert cast between two function types is a cross cast. Also recall that the PreCastStruct record includes fields for decomposing a cross cast between function types into a cast on the domain and codomain. Putting these pieces together, the reduction rule (fun-cast) says that applying the cast-wrapped function V c to argument W reduces to an application of V to W dom c x followed by the cast cod c x, where x is the proof that c is a cross cast. The story is similar for for pairs and sums.

Determinism of CC(⇒)
The reduction relation of the Parameterized Cast Calculus is deterministic. We prove this fact by defining a function named hop that maps any term that is not a value to the same term that would result from one step of reduction. (See the Agda formalization for details.)  Of course, a term that reduces cannot be a value.
The determinism of the Parameterized Cast Calculus follows directly from these lemmas.

Type safety of CC(⇒)
The Preservation theorem is a direct consequence of the type that we give to the reduction relation and that it was checked by Agda.

Theorem 13 (Preservation). If M :
A and M −→ M , then M : A.
We prove the Progress theorem by defining an Agda function named progress that takes a closed, well-typed term M and either (1) returns a redex inside M, (2) identifies M as a value, or (3)  Proof sketch. To convey the flavor of the proof, we detail the cases for function application and cast application. The reader may read the proofs of the other cases in the Agda development.
Case M 1 M 2 The induction hypothesis for M 1 yields the following subcases.
Subcase M 2 −→ M 2 . By rule (ξ ), using Value M 1 , we conclude that Subcase M 2 ≡ blame . By rule (ξ -blame), using Value M 1 , we conclude that We proceed by cases on Value M 1 , noting it is of function type.
Subcase M 1 ≡ λM 11 . By rule (β), using Value M 2 , we conclude that Subcase M 1 ≡ V c and i : Inert c. The field InertCross→ of record PreCastStruct gives us a proof x that c is a cross cast. So by rule (fun-cast), we have We proceed by cases on Value M 2 , most of which lead to contradictions and are therefore vacuously true. Suppose M 2 ≡ k 2 . By rule (δ), we conclude that Then c is a cast on base types, which contradicts that c is inert thanks to the baseNotInert field of PreCastStruct.
Case M c The induction hypothesis for M yields three subcases.
Here we use the ActiveOrInert field of the PreCastStruct on the cast c. Suppose c is active, so we have a : Active c. By rule (cast), using Value M, we conclude that Suppose c is inert. Then, we conclude that Value M c .

The CC (⇒) variant
Siek et al. (2015b), in their Isabelle mechanization of the GTLC and the dynamic gradual guarantee, make a syntactic distinction between cast applications as unevaluated expressions versus cast applications that are part of values. (The paper glosses over this detail.) In particular, they introduce a term constructor named Wrap for casts between function types that are part of a value and another term constructor named Inj for casts into ? that are part of a value. The reason they make this distinction is to enable a smaller simulation relation in their proof of the dynamic gradual guarantee. 1 To aid the proof the Dynamic Gradual Guarantee in Section 3.11, we introduce a variant of CC(⇒), named CC (⇒) (Figure 9), that makes a syntactic distinction between arbitrary casts and inert casts. We write inert cast application as M c . Instead of making the further distinction as in Wrap and Inj, we rely on the source and target types of the casts to distinguish those cases. With the addition of inert casts, we change the definition of Value M, removing the Vcast rule and replacing it with Vwrap, which states that V c is a value if c is inert. We also change the reduction rules (fun-cast), (fst-cast), (snd-cast), and (case-cast) to eliminate values of the form V c instead of V c . This change regarding inert casts does not affect the observable behavior of programs with respect to CC(⇒).
We also change case expressions to include the variable binding, so all of the reduction rules for case must be changed. We change the rules (β-caseL), (β-caseR), and (case-cast) to the rules (β-caseL-alt), (β-caseR-alt), and (case-cast-alt), which use substitution. This change allows us to avoid relating terms to their η-expansions. We conjecture that this change is not necessary for the proof but would require a more complex simulation relation to take η-expansion into account. The change to (case-cast) is an observable difference. Consider an expression of the form: Suppose variable Z does not occur in M and the cast (inl c x) always fails. With (case-cast), there is an error but with (case-cast-alt) there is not.

Blame-Subtyping Theorem for CC (⇒)
Recall that the Blame-Subtyping Theorem (Siek et al., 2015b) states that a cast from A to B cannot be blamed (cause a runtime error), if the source type is a subtype of the target type, that is, A <: B. We identify a cast with a blame label . Thanks to the Blame-Subtyping Theorem, a programmer or static analyzer can inspect the source type, the target type, and the blame label of a cast and decide whether the cast is entirely safe or whether it might cause a runtime error. During execution, casts with different labels can be composed (see Section 6), producing casts that contain multiple blame labels. So more generally, a cast is safe w.r.t a label if the cast will not give rise to a runtime error labeled .
In this section, we develop a parameterized proof of the Blame-Subtyping Theorem for the Parameterized Cast Calculus CC (⇒) following the approach of Wadler & Findler (2009). The idea is to use a preservation-style proof but preserve cast safety instead of typing. However, the appropriate subtyping relation and definition of safety for casts depends on the representation and semantics of casts (Siek et al., 2009), which is a parameter of CC (⇒). Thus, the definition of safety is a parameter of the proof: the CastBlameSafe predicate. The proof is not parameterized on the subtyping relation, clients of the proof typically use subtyping to define the CastBlameSafe predicate.
We require that applying a blame-safe cast to a value does not raise a runtime error, that is, if c is blame-safe for , then applyCast V c a = blame . However, to handle the application of casts to higher-order values, we must generalize this requirement. The output of applyCast can be any term, so in addition to ruling out blame we also need to rule out terms that contain casts that are not blame-safe for . Thus, we define a predicate, written M safe for (Wadler & Findler, 2009), that holds if all the casts in M are blame-safe for , defined in Figure 10. Most importantly, blame is safe for only when = . The other important case is the one for casts: M c is safe for if CastBlameSafe c and M is recursively safe for .
We also require that CastBlameSafe should be preserved under the operations on casts: dom, cod, fst, snd, inl, and inr.
The first structure includes the CastBlameSafe predicate and the fields that preserve blame safety under the casts operations ( Figure 11). The second structure includes the requirement that applyCast preserves blame safety. The structure BlameSafe extends PreBlameSafe and CastStruct. It also includes the following field which, roughly speaking, requires that applying a blame-safe cast to a blame-safe value V results in a blame-safe term. We turn to the proof of blame safety. We first prove that "safe for" is preserved during reduction (Lemma 15). The proof depends on a number of technical lemmas about renaming and substitution. They are omitted here but can be found in the Agda formalization.

Lemma 15 (Preservation of Blame Safety). If M :
A, M : A, M safe for , and M −→ M , then M safe for .
Using Preservation of Blame Safety (Lemma 15), we prove the following Blame-Subtyping Theorem. The theorem says that if every cast with label is blame-safe in M, then these casts never fail and is guaranteed not to be blamed.
Proof sketch. By induction on the reduction sequence and by inversion on the "safe for" predicate.

Dynamic gradual guarantee
Recall that the dynamic gradual guarantee (Siek et al., 2015b) states that changing type annotations in a program to be more precise should either result in the same observable behavior or, in the event of a conflict between the new type annotation and some other part of the program, the program could produce a runtime cast error. On the other hand, changing the program to be less precise should always result in the same observable behavior.
Our proof of the dynamic gradual guarantee follows the same structure as that of Siek et al. (2015b). Their proof involves two main lemmas: (1) compilation from GTLC to its cast calculus preserves precision and (2) a more-precise program in the cast calculus simulates any less-precise version of itself. In this article, the proof that compilation preserves precision is in Section 4. Our proof of the simulation is in this section and is parameterized with respect to the cast representation, using two new structures, so that it can be applied to different cast calculi.
The second of those structures, CastStructWithPrecision, consists of five properties that must be proved for each cast representation. The proofs of these properties together account for approximately one half of the total effort of proving the dynamic gradual guarantee for a particular calculus. This places a higher burden on each cast representation compared to the generic proofs of type safety (Section 3.8), blame-subtyping (Section 3.10), and the space efficiency theorem (Section 6.5). So far we have proved that λB satisfies CastStructWithPrecision (Section 5.3.3) but we have not yet had the time for the proofs for the other cast calculi. An interesting open question is whether there exists a better structure that enables more of the proof of the dynamic gradual guarantee to be generic with respect to cast representations.

Precision structures and precision for CC
The definition of term precision relies on a notion of precision for the cast representation, so precision for casts must be a parameter of the proof. In particular, we define PreCastStructWithPrecision as an extension of PreCastStruct in Figure 12. This structure includes three fields that define the precision relation between two casts and precision between a cast and a type (on the left or right). The record also includes four requirements on those relations that correspond to the precision rules in Figure 9 of Siek et al. (2015b): →lpit corresponds to the forward direction of the bottom right rule, while lpii→ , lpit→ , and lpti→ are inversion properties about the top left, bottom right, and bottom left rules, respectively.
We define the precision relation between CC (⇒) terms in Figure 13. The rules Cast C , CastL C , and CastR C correspond to the precision rules for casts in Figure 9 of Siek et al. (2015b). The rule Wrap C corresponds to the rules p_wrap_wrap and p_inj in the Isabelle mechanization of Siek et al. (2015b). The side condition that B = ? implies B = ? ensures that our Wrap C rule handles the same cases as p_inj (where both B = ? and B = ?) and p_wrap_wrap (where neither B = ? nor B = ?). The WrapL C rule corresponds to the rules p_wrap_r and p_injr. 2 The WrapR C rule corresponds to the rule p_wrap_l. The side condition A = ? prevents overlap with the Wrap C rule which we explain as follows. If A = ?, then by Lemma 3 the term M would be an injection and therefore the Wrap C rule would apply. (For the same reason, there is no p_injl rule in the mechanization of Siek et al. (2015b).) The term blame is treated as more precise than any term, just as in Siek et al. (2015b). The rest of the rules are equivalent to the precision rules for GTLC ( Figure 4).
There are five technical lemmas of Siek et al. (2015b) used in the simulation proof that directly involve the cast representation, so those too become structure fields. We extend CastSruct to create the structure CastStructWithPrecision in Figure 14

Proof of the simulation
This section is parameterized by CastStructWithPrecision and contains two lemmas that lead up to the proof of the simulation. The first lemma states that a term that is less precise than a value will catch up by reducing to a value.

Lemma 17 (Catchup to Value). Suppose M :
A and V : Proof sketch. The proof is by induction on the precision relation M C V . All the cases are trivial, using induction hypotheses about subterms, except the one case for CASTL. Suppose M ≡ N c . By the induction hypothesis for N, we have N −→ * V for some V and Inert c by rule wrap; if active, then it is proved by the field applyCast-catchup of CastStructWithPrecision ( Figure 14).
Next, we prove that substitution preserves precision.
Lemma 18 (Substitution Preserves C ). If σ s σ and N C N , then subst σ N C subst σ N .
Proof sketch. The proof is by induction on the precision relation N C N . We come to the proof of the main lemma that a program simulates any more-precise version of itself. Subcase ξ , where frame F = ( _). By the induction hypothesis about the precision between L and L , there exists L 2 such that L −→ * L 2 and L 2 satisfies the precision relation. Since multi-step reduction is a congruence, there exists M 2 ≡ L 2 M that satisfies M 1 −→ * M 2 and M 2 C M 2 . Subcase ξ , where frame F = (_ ). By Proposition 17, there exists L 2 such that: 1) L 2 is a value; 2) L −→ * L 2 ; 3) L 2 C L . By the induction hypothesis about M and M , there exists N 2 such that M −→ * N 2 and N 2 satisfies the precision relation. Since multi-step reduction is a congruence and is transitive, there exists M 2 ≡ L 2 N 2 that satisfies M 1 −→ * M 2 and M 2 C M 2 . Subcase ξ -blame, where frame F = ( _). L M itself satisfies the precision relation since blame is more precise than any term, so there exists M 2 ≡ L M. Subcase ξ -blame, where frame F = (_ ). Same as the previous case. Subcase β. Using Proposition 17 twice, L M −→ * V W, where V and W are both values and V C L , W C M . We know that L ≡ λ N for some N due to β. By induction on the precision relation V C λ N and inversion on Value V , it further generates two sub subcases: that satisfies both the reduction (from V W by β) and the precision. Sub-subcase: V is Vwrap Suppose V ≡ V 1 i for some value V 1 and i : Inert c: By using Proposition 17 and congruence of reduction: for some value W 1 . Note that V 1 , and W 1 are now both values, about which by induction hypothesis there exists N such that V 1 W 1 −→ * N and N satisfies the precision relation. Subcase δ. Similar to the previous case, by repeatedly using Proposition 17 ("catchup") and the induction hypothesis. Subcase fun−cast. Similar to the previous case, by repeatedly using Proposition 17 ("catch-up") and the induction hypothesis.
Cast (M 1 ≡ M c and M 1 ≡ M c ). We case on the reduction M c −→ M 2 , which yields four subcases: Subcase ξ . By the induction hypothesis about subterms M and M and that multi-step reduction is a congruence. Subcase ξ -blame. M c itself satisfies the precision relation since blame is more precise than any term. Subcase cast. By Proposition 17 and that M is value, there exists value V such that M −→ * V and V C M . Then this case is directly proved by the field sim-cast of CastStructWithPrecision ( Figure 14). Subcase wrap. Similar to the previous case, first use Proposition 17 and then use field sim-wrap of CastStructWithPrecision (Figure 14).
Similarly, fields castr-cast and castr-wrap of CastStructWithPrecision are used in the case for castr, which is omitted since the idea is the same as the cast case described above. Subcase ξ . By the induction hypothesis about subterms M and M and that multi-step reduction is a congruence. Subcase ξ -blame. M i itself satisfies the precision relation since blame is more precise than any term.

Compilation of GTLC to CC(⇒) and CC(⇒)
This section is parameterized by the cast representation ⇒ and by a cast constructor, written A ⇒ B , that builds a cast from a source type A, target type B, blame label , and (implicitly) a proof that A and B are consistent.
The compilation functions C − and C − are defined in Figure 15 and map a welltyped term of the GTLC to a well-typed term of the Parameterized Cast Calculus CC(⇒) or CC (⇒), respectively. The two functions are the same except in the equation for case, so we only show that equation for C − . The Agda type signatures of the compilation functions ensure that they are type preserving.
Lemma 20 (Compilation Preserves Types). The compilation functions also preserve precision, which is an important lemma in the proof of the dynamic gradual guarantee. This lemma is parameterized over the PreCastStructWithPrecision structure ( Figure 12).

A half-dozen cast calculi
We begin to reap the benefits of creating the Parameterized Cast Calculus by instantiating it with six different implementations of CastStruct to produce six cast calculi.

Partially Eager "D" casts with Active cross casts (EDA)
The cast calculus defined in this section corresponds to the original one of Siek & Taha (2006a), although their presentation used a big-step semantics instead of a reduction semantics and did not include blame tracking. In the nomenclature of Siek et al. (2009), this calculus is partially eager and uses the "D" blame tracking strategy. As we shall see shortly, this calculus categorizes cross casts as active, so we refer to this cast calculus as the partially eager "D" cast calculus with active cross casts (EDA).
We define the EDA calculus and prove that it is type-safe and blame-safe by instantiating the meta-theory of the Parameterized Cast Calculus. This requires that we define instances of the structures PreCastStruct, CastStruct, PreBlameSafe, and BlameSafe.
We begin by defining the cast representation type ⇒ as a data type with a single constructor also named ⇒ that takes two types, a blame label, and a proof of consistency: The cast constructor is defined trivially as follows: (Recall that the cast constructor is used during compilation in Section 4.)

Reduction semantics and type safety
We categorize the casts whose target type is ? as inert casts.
Inert c We categorize casts between function, pair, and sum types as cross casts.

Cross c
Cross⊗ : Continuing on the topic of cross casts, we define dom, cod, etc. as follows. (The second parameter x is the evidence that the first parameter is a cross cast.) We check that a cast to a base type is not inert.

Lemma 24. A cast c : A ⇒ b is not inert.
Proof. This is easy to verify because b = ?.

Proposition 25. The EDA calculus is an instance of the PreCastStruct structure.
We import and instantiate the definitions and lemmas from Section 3.2 (values and frames) and Section 3.3 (eta-like reduction rules).
Next we define the applyCast function by cases on the proof that the cast is active. The case below for ActProj relies on Lemma 3 to know that the term is of the form Proposition 26. The EDA calculus is an instance of the CastStruct structure.
We instantiate from CC(⇒) the reduction semantics (Section 3.6) and proof of type safety (Section 3.8) to obtain the following definition and results. Let EDA be the variant of EDA obtained by instantiating CC instead of CC.

Blame-subtyping
Because the EDA calculus uses the "D" blame-tracking strategy, the subtyping relation that corresponds to safe casts is the one in Figure 16 where the unknown type ? is the top of the subtyping order.
We define the CastBlameSafe predicate as follows: Lemma 30 (Blame Safety of Cast Operators). Suppose CastBlameSafe c and c is a cross cast, that is, x : Cross c.

Proposition 31. EDA is an instance of the PreBlameSafe structure.
Lemma 32 (applyCast preserves blame safety in EDA ). If CastBlameSafe c and a : Active c and V safe for and v : Value V , then (applyCast V v c a) safe for .
Proof. The proof is by cases on a (the cast being active and if a cross cast, cases on the three kinds) and then by cases on CastBlameSafe c , except that if the cast is an identity cast, then there is no need for casing on CastBlameSafe c . So there are nine cases to check, but they are all straightforward.

Proposition 33. EDA is an instance of the BlameSafe structure.
We instantiate Theorem 16 with this BlameSafe instance to obtain the Blame-Subtyping Theorem for EDA .

Partially Eager "D" casts with Inert cross casts (EDI)
Many cast calculi (Wadler & Findler, 2007Siek et al., 2009;Siek & Wadler, 2010;Siek et al., 2015a) categorize terms of the form: as values and then define the reduction rule: That is, these calculi categorize cross casts as inert instead of active. In this section, we take this approach for functions, pairs, and sums, but keep everything else the same as in the previous section. We conjecture that this cast calculus is equivalent in observable behavior to EDA. We refer to this calculus as EDI, where the I stands for "inert." So the cast representation type is again made of a source type, target type, blame label, and consistency proof.
The cast constructor is defined as follows:

Reduction semantics and type safety
Again, we categorize casts between function, pair, and sum types as cross casts.

Cross c
Cross⊗ : ⊗ ∈ {→, ×, +} But for the inert casts, we include the cross casts this time.

Inert c
InInj : InCross : Cross c Inert c The active casts include just the identity casts and projections.
Proposition 39. The EDI calculus is an instance of the CastStruct structure.
We instantiate from CC(⇒) the reduction semantics (Section 3.6) and proof of type safety (Section 3.8) for EDI to obtain the following definition and results.

Corollary 41 (Preservation for EDI). If M :
A Let EDI be the variant of EDI obtained by instantiating CC instead of CC.

Blame-subtyping
We define the CastBlameSafe predicate for EDI exactly the same way as for EDA , using the subtyping relation of Figure 16. Because the cast operators for EDI are defined exactly the same as for EDA, Lemma 30 also applies to EDI, that is, the cast operators such as dom preserve blame safety.

Proposition 43. EDI is an instance of the PreBlameSafe structure.
Lemma 44 (applyCast preserves blame safety in EDI ). If CastBlameSafe c and a : Active c and V safe for and v : Value V , then (applyCast V v c a) safe for .
Proof. The proof of this lemma is much shorter than the corresponding one for EDA because there are fewer casts categorized as active. So there are just three straightforward cases to check.

Proposition 45. EDI is an instance of the BlameSafe structure.
We instantiate Theorem 16 with this BlameSafe instance to obtain the Blame-Subtyping Theorem for EDI .

The λB Blame Calculus
This section generates the λB variant (Siek et al., 2015a) of the Blame Calculus (Wadler & Findler, 2009) as an instance of the Parameterized Cast Calculus. It also extends λB in Siek et al. (2015a) with product types and sum types. We explore treating cross casts on products and sums both as active and inert casts in Section 5.3.1. Compared to the previous sections, the main difference in λB is that all injections and projections factor through ground types, defined as follows: Ground Types G, H ::= b | ? → ? | ? × ? | ? + ?
The cast representation type consists of a source type, target type, blame label, and consistency proof.
The cast constructor is defined as follows:

Reduction semantics and type safety
We categorize casts between function, pair, and sum types as cross casts.

Cross c
Cross⊗ : Regarding inert casts in λB, an injection from ground type is inert and a cast between function types is inert. As for casts between product types and sum types, there are two choices, either to make them inert or to make them active. This yields two variants of λB: Variant 1 where all cross casts are inert and Variant 2 where only function casts are inert.

Inert c. Variant 1
InInj : The active casts in both λB variations include injections from non-ground type, projections, and identity casts. In Variant 2, we categorize only function casts as active, so compared to Variant 1 we have an additional rule Act⊗ for casts between product types and sum types being active.

Lemma 49. A cast c : A ⇒ b is not inert.
Proposition 50. Both variants of λB are instances of the PreCastStruct structure.
We define the following partial function named gnd (short for "ground"). It is defined on all types except for ?. gnd Also, we use the following shorthand for a sequence of two casts: The following is the definition of applyCast for λB Variant 1: The definition of applyCast for λB Variant 2 has two additional cases, for Act× and Act+, respectively: Proposition 51. Both variants of λB are instances of the CastStruct structure.
We import and instantiate the reduction semantics and proof of type safety from Sections 3.6 and 3.8 to obtain the following definition and results.

Corollary 53 (Preservation for λB). If M :
A Let λB be the variant of λB obtained by instantiating CC instead of CC.

Blame-subtyping
λB uses the "UD" blame-tracking strategy (Wadler & Findler, 2009;Siek et al., 2009), so the subtyping relation that corresponds to safe casts is the one in Figure 17. In this subtyping relation, a type A is a subtype of ? only if it is a subtype of a ground type. For example, Int → Int <: ? because Int → Int <: ? → ? because ? <: Int.
The CastBlameSafe predicate is defined as it was for EDA in Section 5.1.2, except using the subtyping relation of Figure 17.
The cast operators for λB are defined exactly the same as for EDA, so Lemma 30 also applies to λB , that is, the cast operators such as dom preserve blame safety. We instantiate Theorem 16 with this BlameSafe instance to obtain the Blame-Subtyping Theorem for both variants of λB .

Dynamic gradual guarantee
We now turn to proving the dynamic gradual guarantee for both variants of λB. This involving defining precision on casts and then proving the lemmas required to show that λB is an instance of CastStructWithPrecision. We then instantiate the generic Lemma 19 to obtain the required simulation result and prove the main theorem. We define the precision relations between two casts and between a cast and a type, corresponding to the first three fields in PreCastStructWithPrecision (Figure 12).
The precision relations for Variant 2 have fewer cases compared to Variant 1, since the casts between product types and sum types are active: Then, we instantiate and prove the four lemmas that correspond to the last four fields in PreCastStructWithPrecision (Figure 12). They are forward direction and inversion lemmas about the precision relations defined above between casts and between a cast and a type.

Lemma 59 (Type Precision Implies Cast-Type Precision). Suppose c : A ⇒ B and i : Inert c. If A A , B A , then i
A .
Proof sketch. We case on the premise Inert c :

InInj. In this case, by inversion on A A and B B , A can be either ? or ground. If
A is ?, an identity cast ? ⇒ ? is active so we use rule cast and proceed; otherwise, the cast is inert so we use rule wrap and proceed. InCross. Similar to the InInj case, by inversion on the two type precision relations. We prove the dynamic gradual guarantee for λB following the reasoning of Siek et al. (2015b). We give the full proof here.   (Corollaries 53 and 54), we have the following cases.

Lemma 68 (Simulation
• Case C N −→ * W for some W . Then by Part 1 of this theorem and because reduction is deterministic, V C W . • Case C N −→ * blame for some . We immediately conclude. • Case C N diverges. Then by Part 1, C M also diverges, but that contradicts the assumption that C M −→ * V . 4. Again because λB is type safe (Corollaries 53 and 54), we have the following cases.
• Case C N −→ * W for some W . Then by Part 1, C M −→ * V for some V but that contradicts the assumption that C M diverges. • Case C N −→ * blame for some . We immediately conclude.
• Case C N diverges. We immediately conclude.

Partially Eager "D" Coercions (EDC)
The next three cast calculi use cast representations based on the Coercion Calculus of Henglein (1994). We start with one that provides the same behavior as the cast calculus of Siek & Taha (2006a), that is, partially eager casts with active cross casts (Section 5.1). We use the abbreviation EDC for this cast calculus.
We define coercions as follows, omitting sequence coercions because they are not necessary in this calculus.
The cast constructor is defined by applying the coerce function (defined later in this section) to the implicit proof of consistency between A and B and the blame label :

Proposition 76 The EDC calculus is instance of the PreCastStruct structure.
To help define the applyCast function, we define an auxiliary function named coerce for converting two consistent types and a blame label into a coercion. (The coerce function is also necessary for compiling from the GTLC to this calculus.) The structure of coercions is quite similar to that of the active casts but a bit more convenient to work with, so we define the applyCast function by cases on the coercion. We omit the case for injection because that coercion is not active.
Proposition 77. The EDC calculus is an instance of the CastStruct structure.
We import and instantiate the reduction semantics and proof of type safety from Sections 3.6 and 3.8 to obtain the following definition and results.

Corollary 79 (Preservation for EDC). If M :
A Let EDC be the variant of EDC obtained by instantiating CC instead of CC.

Blame-subtyping
The CastBlameSafe predicate for EDC is defined in Figure 18.

Lemma 81 (Blame Safety of Cast Operators). Suppose
CastBlameSafe c and c is a cross cast, that is, x : Cross c.
Proof. By inversion on Cross c and the CastBlameSafe predicate.
Proposition 82. EDC is an instance of the PreBlameSafe structure.
Lemma 84 (applyCast preserves blame safety in EDC ). If CastBlameSafe c and a : Active c and V safe for and v : Value V , then (applyCast V v c a) safe for .
Proof. The proof is by cases on Active c and inversion on CastBlameSafe c , using Lemma 83 in the case for projection.
Proposition 85. EDC is an instance of the BlameSafe structure.
We instantiate Theorem 16 with this BlameSafe instance to obtain the Blame-Subtyping Theorem for EDC .

Lazy "D" Coercions (LDC)
The Lazy "D" Coercions sometimes catch inconsistencies later in the execution of a program compared to the partially eager and eager calculi. For example, the following term does not immediately trigger a cast failure: It would instead trigger an error if/when it is applied to an argument. In this respect, Lazy "D" Coercions are similar to λB (Section 5.3) and λC (Section 5.6). The lazy behavior of the Lazy "D" Coercions is acheived by changing the definition of applyCast to use shallow consistency instead of consistency (via the definition of the cast constructor A ⇒ B ), which only inspects the head constructors of the source and target type. However, Lazy "D" Coercions differs from λB and λC with respect to how it performs blame tracking. In particular, a cast from any type to the unknown type ? is a safe cast with the "D" blame-tracking strategy.
The Lazy "D" Coercions (Siek et al., 2009) are syntactically similar to the coercions of Section 5.4 except that include a failure coercion, written ⊥ .
The cast constructor is defined as follows, using the coerce function defined later in this section:

Reduction semantics and type safety
Injections are categorized as inert casts.
Inert c Inert A! The coercions between function, pair, and sum types are categorized as cross casts. The definition of cast operators such as dom are the same as in Section 5.4.

Lemma 89. A cast c : A ⇒ b is not inert.
Proposition 90. The LDC calculus is an instance of the PreCastStruct structure.
We define shallow consistency, written A B, as follows: The coerce function differs from that of the EDC calculus (Section 5.4) in that we only require the source and target types to be shallowly consistent. The coerce function is mutually defined with the function A ⇒ B that was presented earlier in this section: The definition of applyCast is similar to the one for EDC (Section 5.4) except that there is an additional case for ⊥ and the projection case checks for shallow consistency instead of consistency, using A ⇒ B : Proposition 91. The LDC calculus is an instance of the CastStruct structure.
We import and instantiate the reduction semantics and proof of type safety from Sections 3.6 and 3.8 to obtain the following definition and results.

Corollary 93 (Preservation for LDC). If M :
A Let LDC be the variant of LDC obtained by instantiating CC instead of CC.

Blame-subtyping
The CastBlameSafe predicate for LDC is the same as for EDC (Section 5.4) except that there is an additional rule for failure coercions: = CastBlameSafe ⊥ Lemma 95 (Blame Safety of Cast Operators). Suppose CastBlameSafe c and c is a cross cast, that is, x : Cross c.
• If x = Cross+, then CastBlameSafe (inl c x) and CastBlameSafe (inr c x) . Proof. The proof is by cases on Active c and inversion on CastBlameSafe c , using Lemma 97 in the case for projection.

Proposition 99. LDC is an instance of the BlameSafe structure.
We instantiate Theorem 16 with this BlameSafe instance to obtain the Blame-Subtyping Theorem for LDC .

The λC Coercion Calculus
This section instantiates the Parametric Cast Calculus to obtain the λC calculus of Siek et al. (2015a). Again we represent casts as coercions, but this time we must include the notion of sequencing of two coercions, written c ; d, to enable the factoring of casts through the ground type. As part of this factoring, injections and projections are restricted to ground types. We omit the failure coercion because it is not necessary for λC.
The cast constructor is defined as follows:

Reduction semantics and type safety
The coercions between function, pair, and sum types are categorized as cross casts. We do not categorize sequence coercions as cross casts, which, for example, simplifies the definition of the dom and cod functions.

Injections and function coercions are categorized as inert casts.
Inert c The active casts in λC include identity casts, projections, and sequences. The λC calculus did not include pairs and sums (Siek et al., 2015a), but here we choose to categorize casts between pairs and sums as active casts, as we did for Variant 2 of λB in Section 5.3. The definition of the functions such as dom are the usual ones, but note that the x parameter plays an important role in this definition. We did not categorize sequence casts as cross casts, so the following functions can omit the cases for (c ; d): Proposition 104. The λC Calculus is an instance of the PreCastStruct structure.
We define the applyCast function for λC as follows: Proposition 105. The λC calculus is an instance of the CastStruct structure.
We import and instantiate the reduction semantics and proof of type safety from Sections 3.6 and 3.8 to obtain the following definition and results. Let λC be the variant of λC obtained by instantiating CC instead of CC.

Blame-subtyping
The CastBlameSafe predicate for λC is the same as the one for EDC ( Figure 18) except for the additional rule for sequence coercions: CastBlameSafe c CastBlameSafe d CastBlameSafe (c; d)

Space-Efficient Parameterized Cast Calculus
The cast calculi in Section 5 all suffer from a space efficiency problem (Herman et al., 2010). When a cast is applied to a higher-order value such as a function, these calculi either wrap it inside another function or wrap the cast itself around the value. Either way, the value grows larger. If a value goes through many casts, it can grow larger in an unbounded fashion. This phenomenon causes significant space and time overheads in real programs, for example, changing the worse-cast time complexity of quicksort from O(n 2 ) to O(n 3 ) Kuhlenschmidt et al., 2019). Herman et al. (2010) proposed solving this problem by replacing casts with the coercions of Henglein (1994). Any sequence of coercions can normalize to just three coercions, thereby providing a space-efficient representation. Siek et al. (2015a) define an algorithm for efficiently normalizing coercions and use it to define the λS calculus.
Siek & Wadler (2010) propose another approach that compresses a sequence of casts into two casts where the middle type is the least upper bound with respect to precision. The AGT methodology uses a similar representation (Garcia et al., 2016) and was proved space-efficient (Toro & Tanter, 2020;Bañados Schwerter et al., 2021).
In this section, we develop a space-efficient version of the Parameterized Cast Calculus, which we name SC(⇒). As a sanity check, prove that SC(⇒) is type-safe, but more importantly, we prove that SC(⇒) is indeed space-efficient provided that the cast representation is an instance of the structures defined later in this section. In Section 7, we instantiate SC(⇒) two different ways to reproduce the λS calculus and to define a new calculus that more directly maps to a compact bit-level encoding. We then instantiate the meta-theory for SC(⇒) to produce proofs of type safety and space efficiency for both of these calculi.

Space-efficient values
This subsection is parameterized over the PreCastStruct structure.
To prepare for the definition of space-efficient cast calculi, we define a notion of value that may be wrapped in at most one cast. We accomplish this by stratifying the non-cast values, that is the simple values S, from the values V that may be wrapped in a cast.

The ComposableCasts structure
The ComposableCasts structure extends PreCastStruct with two more fields, one for applying a cast to a value (like CastStruct) and one for composing two casts into a single, equivalent cast, for the purposes of achieving space efficiency. 3 It would seem reasonable to have this structure extend CastStruct instead of PreCastStruct, but the problem is that the notion of value is different. Here, we use the definition of Value from Section 6.1. The two fields of the ComposableCasts are

Reduction semantics of SC(⇒)
This section is parameterized by ComposableCasts and defines the Space-Efficient Parameterized Cast Calculus, written SC(⇒). The syntax is the same as that of the Parameterized Cast Calculus ( Figure 5).
The frames of SC(⇒) and the plug function are defined in Figure 19. The definitions are quite similar to those of the Parameterized Cast Calculus (Figure 6), with the notable omission of a frame for casts, which are handled by special congruence rules.
A space-efficient reduction semantics must include a reduction rule for compressing adjacent casts to prevent the growth of long sequences of them. In particular, the (compose) rule compresses two adjacent casts into a single cast: Furthermore, the semantics must ensure that this reduction rule is triggered frequently enough to prevent long sequences from forming. To date, the way to accomplish this in a reduction semantics has been to define evaluation contexts in a subtle way, with two mutual definitions (Herman et al., 2007(Herman et al., , 2010Siek & Wadler, 2010;Siek et al., 2015a). Here, we take a different approach that we believe is simpler to understand and that fits into using frames to control evaluation order. The idea is to parameterize the reduction relation according to whether a reduction rule can fire in any context or only in non-cast contexts, that is, when the immediately enclosing term is not a cast. We define reduction context RedCtx as follows. (It is isomorphic to the Booleans but with more specific names.) ctx : RedCtx So the reduction relation takes the form: To prevent reducing under a sequence of two or more casts, the congruence rule for casts (ξ -cast) requires a non-cast context. Further, the inner reduction must be OK with a cast context (i.e. Any context). The congruence rule (ξ ) for all other language features can fire in any context, and the inner reduction can require either any context or non-cast contexts. The rule for composing two casts can only fire in a non-cast context, which enforces an outside-in strategy for compressing sequences of casts. For the same reason, the rule for applying a cast to a value can only fire in a non-cast context. All other reduction rules can fire in any context. The reduction semantics for SC(⇒) is defined in Figure 20.

Type safety of SC(⇒)
Our terms are intrinsically typed, so the fact that Agda checked the definition in Figure 20 gives us Preservation.

Theorem 117 (Preservation). If M :
A and M −→ M , then M : A.
Next, we prove Progress. First we define the following predicate for identifying when a term is a cast and prove a lemma about switching from NonCast to Any when the redex is not a cast.

Space efficiency of SC(⇒)
We follow the space efficiency proof of Herman et al. (2010) but refactor it into two parts: (1) generic lemmas about the reduction of SC(⇒) that appear in this section and (2) lemmas about specific casts and coercions, which appear in Section 7. We clarify a misleading statement by Herman et al. (2010) and fill in details needed to mechanize the proof in Agda. The theorem we aim to prove is that, during execution, the program's size is bounded above by the program's idealized size multiplied by a constant. The idealized size does not include any of the casts. The following are excerpts from the definitions of real-size and ideal-size : · · · real-size(M c ) = size(c) + real-size(M) We shall prove that the size of every cast is bounded above by a constant, so we can simplify some of the technical development by using the following alternative definition of size that uses 1 as the size of each cast: Regarding reduction of SC(⇒), the key property is that the reduction rules prevent the accumulation of long sequences of adjacent casts. In their proof, Herman et al. (2010) state that there is never a coercion adjacent to another coercion.
"During evaluation, the [E-CCAST] rule prevents nesting of adjacent coercions in any term in the evaluation context, redex, or store. Thus the number of coercions in the program state is proportional to the size of the program state." Of course, for the rule [E-CCAST] to apply in the first place, there must be two adjacent coercions. So perhaps we could amend the statement of Herman et al. (2010) to instead say that there are never more than two. However, even that would be technically incorrect. Consider the following example that begins with three separated coercions but a β-reduction brings them together: This turns out to be the worst-case scenario. In the following, we prove that the [E-CCAST] rule, that is, the (compose) rule in this article, together with rules about the order of evaluation, prevent nesting of more than three adjacent coercions.
We define the Size Predicate on terms in Figure 21 which only includes terms with no more than three adjacent coercions. The judgment is written n | b M ok where M is a term, n counts the number of cast application expressions at the top of the term, and b indicates with true or false whether this term is in a delayed context, that is, inside a λ-abstraction or a branch of a conditional expression. The above example with three coercions satisfies the predicate when outside of a delayed context: The rule SCast1 for cast application expressions adds one to the count of adjacent casts and makes sure that the count does not exceed three.
For terms in a delayed context, the rule (SCAST2) restricts the number of adjacent casts to two instead of three. To see why, consider the next example in which there are three adjacent casts inside the λ-abstraction and a β-reduction yields a term with four adjacent casts: ((λ $8 Int! Int? 1 Int! ) 1) Int? 2 −→ $8 Int! Int? 1 Int! Int? 2 The rule SVar starts the count at one even though a variable is obviously not a cast application. The reason is that a value substituted for a variable may have one cast at the top. If we did not count variables as one, then a variable could be surrounded by two casts inside of a λ-abstraction which could reduce to a term with four adjacent casts as in the following example: ((λ '0 Int? 1 Int! ) (1 Int! )) Int? 2 −→ 1 Int! Int? 1 Int! Int? 2 We turn to the proof of the space consumption theorem, starting with the necessary lemmas.
The Size Predicate guarantees that the number of adjacent casts is less than or equal to 3.
The Size Predicate guarantees that a term's size is bounded above by its ideal size multiplied by a constant, plus 3.
The compilation of source programs (GTCL) to the cast calculus produces terms that satisfy the Size Predicate. The next piece needed for the space efficiency theorem is to place a bound on the size of the casts. We require their size to be bounded by their height, as in Herman et al. (2010), so it remains to show that the heights of all the coercions does not grow during execution. To prove this, we must place further requirements on the specific cast calculi, which we formulate as a structure named CastHeight in Figure 22 that extends the ComposableCasts structure in Section 6.
We define the cast height of a term, written c-height(M), to be the maximum of the heights of the all the casts in term M. The cast height of a term monotonically decreases under reduction.  The size of any cast c is bounded above by k 2 · 2 height(c) (according to the size-height member of CastHeight), so the real-size of a term is bounded above by k 2 · 2 c-height(M) multiplied by its size.
With the above lemmas in place, we proceed with the main theorem. The size of the program (including the casts) is bounded above by its ideal size (not including casts) multiplied by a constant.

Theorem 126 (Space Consumption). If M :
A, then there exists c such that for any M : A where ctx C M −→ * M , we have real-size(M ) ≤ c · ideal-size(M ).
Proof. (The formal proof of this theorem is in the Agda development. Here we give a proof that is less formal but easier to read.) By Lemma 122, there is an n such that We establish the conclusion of the theorem by the following inequational reasoning: real-size(M ) ≤ k 2 · 2 c-height(M ) · size(M ) by Lemma 125 ≤ (k 2 · 2 c-height(M ) ) · (3 + 10 · ideal-size(M )) by Lemma 121 So we choose 13k 2 · 2 c-height(C M ) as the witness for c.
We observe that the space consumption theorem of Herman et al. (2010) (as well as the above theorem) has a limitation in that it does not prevent a cast calculus that uses the eta cast reduction rules, as discussed in Section 3.3, from consuming an unbounded amount of space. To capture the space used by the eta cast rules, one would need to mark the terms introduced by the eta cast rules so that they can be excluded from the ideal-size of a term. We do not pursue that direction at this time, but note that the calculi that we introduce in the next section do not use eta cast rules.

Space-efficient cast calculi
We instantiate the Efficient Parameterized Calculus SC(⇒) with two different instances of the ComposableCasts and CastHeight to obtain definitions and proofs of type safety and space efficiency for two cast calculi: λS of Siek et al. (2015a) and the hypercoercions of Lu et al. (2020).

λS
The cast representation in λS are coercions in a particular canonical form, with a three-part grammar consisting of top-level coercions, intermediate coercions, and ground coercions, defined in Figure 23. A top-level coercion is an identity cast, a projection followed by an intermediate coercion, or just an intermediate coercion. An intermediate coercion is a ground coercion followed by an injection, just a ground coercion, or a failure coercion. A ground coercion is an identity on base type or a cross cast between function, pair, or sum types.
The cast constructor is also defined in Figure 23.

Reduction semantics and type safety
Casts between function, pair, and sum types are categorized as cross casts.

Inert c
Inert c → d Inert g; G! Inert ⊥ There are five kinds of active coercions: the identity on ?, projections, failures, cross casts on pairs and sums, and identity on base types. To support space efficiency, we define a composition operator for the coercions of λS. The operator uses two auxiliary versions of the operator for intermediate and ground coercions. The operator that composes an intermediate coercion with a coercion always yields an intermediate coercion. The operator that composes two ground coercions always returns a ground coercion. Agda does not automatically prove termination for this set of mutually recursive functions, so we manually prove termination, using the sum of the sizes of the two coercions as the measure. (c 1 × d 1 ) (c 2 × d 2 ) = (c 1 c 2 ) × (d 1 d 2 ) (c 1 + d 1 ) (c 2 + d 2 ) = (c 1 c 2 ) + (d 1 d 2 ) We define applyCast for λS by cases on the coercion.
We import and instantiate the reduction semantics and proof of type safety from Section 6 to obtain the following definitions and results for λS.

Space efficiency
Next, we establish that λS is an instance of the CastHeight structure so that we can apply Theorem 126 (Space Consumption) to obtain space efficiency for λS. We define the height of a coercion as follows: height ( The cast height of the result of applyCast applied to a simple value S and coercion c is less than the max of the cast height of S and the height of c. Lemma 135. c-height(applyCast S c) ≤ max(height(S), height(c)) The dom, cod, fst, snd, inl, and inr operators on coercions all return coercions of equal or lesser height than their input. There are four kinds of active hypercoercions. The identity hypercoercion is active, as is a hypercoercion that begins with a projection from ? or ends with a failure coercion. Furthermore, if the hypercoercion begins and ends with identity, and the middle is either the identity or a cast between pair or sum types, then it is active.  To support space efficiency, we define the composition operator on hypercoercions in Figure 25. For the most part, this operator compares the end of the first coercion with the beginning of the second. For example, if the end of the first coercion is an injection G! and the beginning of the second is the corresponding projection G? , then the injection and projection are discarded and the resulting hypercoercion is formed using the beginning of the first, the composition of the middles, and the end of the second. The composition operator handles identity coercions and failure coercions as special cases. An identity coercion composed on the left or right is discarded. A failure coercion on the left causes the coercion on the right to be discarded. The composition operator is a mutually recursive with the definition of composition on the middle parts. Thankfully, Agda's termination checker approves of this definition even though the contravariance in function coercions means that it is not technically structurally recursive.
We define the applyCast function for hypercoercions by cases onActive c.
Hypercoercions are an instance of the ComposableCasts structure.
We import and instantiate the reduction semantics and proof of type safety from Section 6 to obtain the following definition and results.

Space efficiency
Next, we establish that λH is an instance of the CastHeight structure so that we can apply Theorem 126 (Space Consumption) to obtain space efficiency for λH. We define the height of a hypercoercion to be the height of its middle part: The cast height of the result of applyCast applied to a simple value S and coercion c is less than the max of the cast height of S and the height of c.
Lemma 148. c-height(applyCast S c) ≤ max(height(S), height(c)) The dom, cod, fst, snd, inl, and inr operators on coercions all return coercions of equal or lesser height than their input.