Gradual session types

Abstract Session types are a rich type discipline, based on linear types, that lifts the sort of safety claims that come with type systems to communications. However, web-based applications and microservices are often written in a mix of languages, with type disciplines in a spectrum between static and dynamic typing. Gradual session types address this mixed setting by providing a framework which grants seamless transition between statically typed handling of sessions and any required degree of dynamic typing. We propose Gradual GV as a gradually typed extension of the functional session type system GV. Following a standard framework of gradual typing, Gradual GV consists of an external language, which relaxes the type system of GV using dynamic types; an internal language with casts, for which operational semantics is given; and a cast-insertion translation from the former to the latter. We demonstrate type and communication safety as well as blame safety, thus extending previous results to functional languages with session-based communication. The interplay of linearity and dynamic types requires a novel approach to specifying the dynamics of the language.


Introduction
It was the best of types, it was the worst of types.
A survey of the top-20 programming languages to learn for open source projects 1 lists eight dynamically typed languages (JavaScript, Python, Ruby, R, PHP, Perl, Scheme, and Gradual session types 3 (Honda et al., 1998;Yoshida & Vasconcelos, 2007). Gay and Hole (2005) introduced subtyping for session types, and session types were embedded into a functional language with linear types, similar to the one used in this paper, by . Caires, Pfenning, Toninho, and Wadler introduced propositions-as-types interpretations of session types in linear logic (Caires & Pfenning, 2010;Wadler, 2012Wadler, , 2014Caires et al., 2014). One important line of research is multiparty session types (Honda et al., , 2016 but we confine our attention here to dyadic session types. Session types have been adapted to a variety of languages, either statically or dynamically checked, and using either libraries or additions to the toolchain; implementations include C, Erlang, Go, Haskell, Java, Python, Rust, and Scala. New languages incorporating session types include C0 (Willsey et al., 2017), Links (Cooper et al., 2007), SePi (Franco & Vasconcelos, 2013), SILL (Pfenning & Griffith, 2015), and Singularity (Fähndrich et al., 2006). Industrial uses of session types include Red Hat's support of the Scribble specification language , which has been used as a common interface for several systems based on session types; Estafet's use of session types to manage microservices 3 ; and the Ocean Observatories Initiative's use of dynamically checked session types in Python (Demangeon et al., 2015). Session types inspired an entire line of research on what has come to be called behavioral types, the subject of EU COST action BETTY, a recent Shonan meeting, and a recent Dagstuhl seminar.
Here is a simple session type encoding of a protocol to purchase an online video: S video = !string. ?int. ⊕ {buy : !CC. ?URL. end ? , quit : end ! } It describes a channel endpoint along which a client sends the name of a video as a string, receives its cost as an integer, and then selects either to buy the video, in which case one sends a credit card number, receives a URL from which the video may be downloaded, and waits for an indication that the channel has been closed, or selects to quit and closes the channel. There is a dual session type for server at the other end of the channel, where ! (write) is swapped with ? (read), ⊕ (select from a choice) is swapped with & (offer a choice), and end ! (close a channel) is swapped with end ? (wait for a channel to close). Session types are necessarily linear. Let x be bound to a string and let c be bound to a channel endpoint of type S video . Performing let d = send x c in . . . binds d to a channel endpoint of type R, where S video = !string.R. To avoid sending a string to the same channel twice, it is essential that c must be bound to the only reference to the channel endpoint before the operation, and for similar reasons d must be bound to the only reference to the channel endpoint after. Such restrictions can easily be enforced in a statically typed language with an affine type discipline. Linearity is required to guarantee that channels are not abandoned before they are closed.
But how is one to ensure linearity in a dynamically typed language? Following Tov & Pucella (2010), we require that each dynamically typed reference to a channel endpoint is equipped with a lock. That reference is locked after the channel is used once to ensure it cannot be used again. To ensure that each channel is appropriately terminated, with either a

A compute service
The compute service is a simplified version of one of the protocols in Sy and Rob's project. The service involves two peers, a server and a client, connected via a communication link. The server runs a protocol that first offers a choice of two arithmetic operations, negation or addition, then reads one or two numbers depending on the operation, outputs the result of applying the selected operation to its operand(s), and finally closes the connection. The client chooses an operation by sending the server a label, which is either neg or add indicating the choice of negation or addition, respectively. In session-type notation, the server's view of the compute protocol reads as follows: Compute = &{neg : ?int.!int.end ! , add : ?int.?int.!int.end ! } Sy chooses to implement the server in the language GV that is inspired by previous work  and that we will describe formally in Section 3.  The parameter c of type Compute is the server's endpoint of the communication link to the client (when unambiguous, we often just say endpoint or channel). The case c of ... expression receives the client's choice on channel c in the form of a label neg or add and branches accordingly. The notation "c. " in each branch (re-)binds the variable c to the channel in the state after the transmission has happened. The type of c is updated to the session type corresponding to the respective branch in the Compute type. The receive c operation receives a value on channel c and returns a pair of the received value and the depleted channel with a correspondingly depleted session type. Analogously, the send v c operation sends value v on channel c and returns the depleted channel. The final close c disconnects the communication link by closing the channel.

The view from the client side
A client of the Compute protocol communicates on a channel with the protocol ComputeD defined in the following. This protocol is dual to Compute: sending and receiving operations are swapped. ComputeD = ⊕{neg : !int.?int.end ? , add : !int.!int.?int.end ? } A client of the compute service may always select the same operation and then proceed linearly according the corresponding branch. Such a client can use a simpler supertype of ComputeD with a unary internal choice. For example, a client that only ever asks for negation can implement ComputeDneg. ComputeDneg = ⊕{neg : !int.?int.end ? } Here is Sy's implementation of a typed client for ComputeDneg. n e g a t i o n C l i e n t : i n t → ComputeDneg → i n t n e g a t i o n C l i e n t v c = l e t c = s e l e c t neg c i n l e t c = send v c i n l e t y , c = receive c i n l e t _ = wait c i n y There are two new operations in the client code. The select neg c operation selects the neg branch in the protocol by sending the neg label to the server. It returns a channel to run the Gradual session types 7 selected branch of the protocol with type !int.?int.end ? . The wait c operation matches the close operation on the server and disconnects the client.

A unityped server
To test some new features, Rob also implements the Compute protocol, but does so in the unityped language Uni GV, which is safe but does not impose a static typing discipline.
Here is Rob's implementation of the server. The main function dynServer takes a channel c on which it receives the client's selection. It delegates to an auxiliary function serveOp that takes the arity of a function, the function itself, and the channel end on which to receive the arguments and to send the result. The serveOp function counts down the number of remaining function applications in the first argument, accumulates partial function applications in the second argument, and propagates the channel end in the third argument.
It is easy to see that the dynServer function implements the Compute protocol. Rob chose this style of implementation because it is amenable to experimentation with protocol extensions: the function dynServer is trivially extensible to new operations and types by adding new lines to the case dispatch.

The gradual way
How can we embed Rob's server with other program fragments in the typed language (e.g., Sy's client) while retaining as many typing guarantees as possible?
One answer would be to use a dependently typed system that can describe the type of the serveOp function adequately. In an extension of a recently proposed system (Toninho & Yoshida, 2018) with iteration on natural numbers and large elimination, we might write that code as follows: Op : n a t → Type Op 0 = i n t Op ( n+1) = i n t → Op n Ch : n a t → S e s s i o n Ch 0 = ! i n t . end ! Ch ( n+1) = ? i n t . Ch n 8 A. Igarashi et al. serveOpDep : ( n : n a t ) ( op : Op n ) ( c : Ch n ) → u n i t serveOpDep 0 op c = close ( send op c ) serveOpDep ( n+1) op c = l e t v , c = receive c i n serveOpDep n ( op v ) c However, we are not aware of a fully developed theory of a session-type system that would be able to process this definition. An alternative that is immediately available is to resort to gradual typing. For this particular program, it will insert casts to make the program type check, but all those casts are semantically guaranteed to succeed because it would have a dependent type. To this end, we rewrite the function dynServer in a gradually typed external language analogous to the gradually typed lambda calculus GTLC (Siek et al., 2015b), but extended with GV's communication operations.
In our example, the rewrite to the external language boils down to providing suitable type signatures for dynServer and serveOp: d y n S e r v e r : Compute → u n i t serveOp : i n t → → → u n i t The first argument n of dynServer is consistently handled as an integer, so its type is int. The second argument op is invoked with values of type int → int → int, int → int, and int: these types are subsumed to the dynamic type . Similarly to other gradual type systems, an expression of type can be used in any context, e.g., addition, function application, or even communication, and any value can be passed where is expected. The third argument c is invoked with channels of different types: ?int .? int .! int .end ! , ?int .! int .end ! , and ! int . end ! . These types are subsumed to a type that is novel to this work, the dynamic session type, , a linear type which subsumes all session types. It is important to see that the channel c is handled linearly in functions dynServer and serveOp. For that reason, the role and handling of the linear dynamic session type with respect to the set of session types is analogous to the role and handling of with respect to general types, as shown in earlier work (Fennell & Thiemann, 2012;Thiemann, 2014). Aside from the type annotation, the code remains exactly the same as in the unityped case. The external language comes with a translation into a blame calculus with explicit casts. This translation inserts just the casts that are necessary to make typing of the code go through. Here is the output of this translation (suffix Cast is appended to the names of the functions to distinguish different versions): Casts of the form e : T 1 p ⇒ T 2 -meaning that e of type T 1 is cast to T 2 -are inserted where values are converted from/to or , similarly to the translation from GTLC. The blame labels 1 , 2 , . . . (ranged over by p and q) on the arrow identify casts, when they fail. The resulting casts in dynServerCast and serveOpCast look fairly involved, but we should keep in mind that the programmer does not have to write them as they result from the translation. In practice, blame labels may contain information on program locations to help identify how a program fails. For example, if Rob made the following mistake in writing his dynServer neg : c . s e r v e O p 2 ( λ x .− x ) c ; −

− The f i r s t argument t o s e r v e O p s h o u l d be 1 !
then a call to negationClient would fail after the server receives the first integer from the client. More specifically, the failure would identify the cast labeled 7 failed because a channel endpoint whose session type is !int.end ! had been flown from 2 .

Dynamic linearity
The refined criteria for gradual typing (Siek et al., 2015b) postulate that a gradual type system should come with a full embedding of a unityped calculus. This embedding (which we indicate by ceiling brackets . . . ) extends the embedding given for the simply typed lambda calculus (Wadler & Findler, 2009) to handle the operations on sessions (see Figure 13 for its definition). For example, (the unityped version of) the dynServer as written by Rob is compiled and embedded into the gradually typed language as a value dynServer : . To directly incorporate Rob's code, the gradual type checker enables Sy to write a function callDynServer that accepts a channel of type Compute and returns a value of type unit, but internally just calls dynServer. c a l l D y n S e r v e r : Compute → u n i t c a l l D y n S e r v e r c = d y n S e r v e r c The gradual type checker translates the definition of callDynServer by inserting the appropriate casts: it casts the embedded dynServer (of type ) to the function type → , it casts the channel argument to this function to , and it casts the result to unit.
c a l l D y n S e r v e r : Compute → u n i t c a l l D y n S e r v e r c = ( ( d y n S e r v e r : The casts inserted in this code make Sy's expectations completely obvious: dynServer must be a function and it is expected to use c as a channel of type Compute. Any misuse will allocate blame to the respective cast in dynServer. One kind of misuse that we have not discussed, yet, is compromising linearity: Sy has no guarantee that Rob's code does not accidentally duplicate or drop the communication channel. Both actions can lead to protocol violations, which should be detected at runtime. Gradual GV takes care of linearity by factoring the cast (c : Compute 9 ⇒ ) through the dynamic session type : The first part is a cast among linear (session) types and it can be handled as outlined in Section 2.4. The second part is a cast from a linear type (which could be a session type, a linear function type, or a linear product) to the unrestricted dynamic type . A cast from a linear type to unrestricted is a novelty of Gradual GV. Operationally, the cast introduces an indirection through a store: it takes a linear value as an argument, allocates a new cell in the store, moves the linear value along with a representation of its type into the cell, and returns a handle a to the cell as an unrestricted value of type . Gradual GV represents the cell by a process and creates handles by introducing an appropriate binder so that a process of the form E[v : Here, (νa)P represents the scope of a fresh reference to a linear value and the process a → v : p ⇒ represents the cell storing v at a. Linear use of this cell is controlled at runtime using ideas for runtime monitoring of affine types (Tov & Pucella, 2010;.
Any access to a cell comes in the guise of a cast a : p ⇒ T from to another type applied to a handle a. If the first access to the cell is a cast from to a linear type consistent with the type representation stored in the cell, then the cast returns the linear value and empties the cell. Any subsequent access to the same cell results in a linearity violation which allocates blame to the label on the cast from . If the first cast attempts to convert to an inconsistent type, then blame is allocated to that cast. In addition, there is a garbage collection rule that fires when the handle of a full cell is no longer reachable from any process. It allocates blame to the context of the cast to because that cast violated the linearity protocol by dismissing the handle.

End-to-end dynamicity
The examples so far tacitly assume that channels are created with a fully specified session type that provides a "ground truth" for the protocol on this channel. Later on, channels may be cast to and on to , but essentially they adhere to the ground truth established at their creation.
Unfortunately, this view cannot be upheld in a calculus that is able to embed a unityped language like Uni GV. When writing new in a unityped program to create a channel, Rob (hopefully) has some session type in mind, but it is not manifest in the code.
In the typed setting, new returns a linear pair of session endpoints of type S × lin S where S is the server session type and S its dual client counterpart (cf. the Compute and ComputeD types in Sections 2.1 and 2.2). When embedding the unityped new, the session type S is unknown. Hence, the embedding needs to create a channel without an inherent ground truth session type. It does so by assigning both channel ends type and casting it to as in new : × lin ⇒ . To make this work, the dynamic session type is considered selfdual, that is = . Gradual GV offers no static guarantees for either end of such a channel.
To see what runtime guarantees Gradual GV can offer for a channel of unknown session type, let's consider the embedding of the dynamic send and receive operations that may be applied to it. The embedded send operation takes two arguments of type , for the value and the channel, and returns the updated channel wrapped in type . The embedded receive operation takes a wrapped channel of type and returns a ( -wrapped) pair of the received value and the updated channel. for some contexts E and F. The channel ends cc : and cs : are the two ends of the channel created in line 6. Fortunately, the two processes use the channel consistently as the cast target ? . on one end is dual to the cast target ! . at the other end. Hence, Gradual GV has a reduction that drops the casts at both ends in this situation, and retypes the ends to cc : ? . and cs : ! . , respectively.
Implementing this reduction requires communication between the two processes to check the cast targets for consistency. While our formal presentation abstracts over this implementation issue, we observe that a single asynchronous message exchange is sufficient: Each cast first sends its target type and then receives the target type of the cast at the other end. Then both processes check locally whether the target types are duals of one another. If they are, then both processes continue; otherwise they allocate blame. As both ends perform the same comparison, the outcome is the same in both processes.

GV
We begin by discussing a language GV with session types but without gradual types. The language is inspired by both the  functional session type calculus and Wadler's (2012Wadler's ( , 2014 'good variant' of the language. A main difference from the former is the introduction of communication primitives and session types to close a session explicitly. Unlike the latter, types are "stratified" into two levels-sessions types and plain types-and deadlock freedom is not guaranteed. Figure 1 summarizes types of GV. Let m, n range over multiplicities for types whose use is either unrestricted, un, or must be linear, lin.

Types and subtyping
Let T, U range over types, which include unit type, unit; unrestricted and linear function types, T → m U; unrestricted and linear product types, T × m U; and session types. One might also wish to include Booleans or base types, but we omit these as they can be dealt with analogously to unit.
Let l range over labels used for selection and case choices. Let S, R range over session types that describe communication protocols for channel endpoints, which include: send !T. S, to send a value of type T and then behave as S; receive ?T. S, to receive a value of type T and then behave as S, select ⊕{l i : S i } i∈I , to send one of the labels l i and then behave as S i ; case &{l i : S i } i∈I to receive any of the labels l i and then behave as S i ; close end ! , to close a channel endpoint; and wait end ? , to wait for the other end of the channel to close. In ⊕{l i : S i } i∈I and &{l i : S i } i∈I , the label set must be non-empty. We will call the session type that describes the behavior after send, receive, select, or case the residual.

13
We define the usual notion of the dual of a session type S, written as S. Send is dual to receive, select is dual to case, and close is dual to wait. Duality is an involution, so that S = S.
Multiplicities are ordered by un <: lin, indicating that an unrestricted value may be used where a linear value is expected, but not conversely. The unit type is unrestricted, session types are linear, while function types T → m U and product types T × m U are unrestricted or linear depending on the multiplicity m that decorates the type constructor. To ensure that linear objects are used exactly once our type system imposes the invariant that unrestricted data structures do not contain linear data structures. As an example, type unit × un end ! cannot be introduced in any derivation. We also write n :> (T) if m(T) holds for some m such that m <: n, thus un :> (T) holds only if un(T), while lin :> (T) holds if either lin(T) or un(T), and hence holds for any type.
We define subtyping as usual for functional-program like systems . Function types are contravariant in their domain, covariant in their range, and covariant in their multiplicity, and send types are contravariant in the value sent and covariant in the residual session type. All other types and session types are covariant in all components. Width subtyping resembles record subtyping for select, and variant subtyping for case. That is, on an endpoint where one may select among labels with an index in I one may instead select among labels with indexes in J , so long as J ⊆ I, while on an endpoint where one must be able to receive any label with an index in I one may instead receive any label with an index in J , so long as I ⊆ J . (Subtyping on endpoints appears sometimes reversed in process-calculus like systems, such as (Carbone et al., 2007(Carbone et al., , 2012Demangeon & Honda, 2011), Wadler's CP (2012; Gay (2016) discusses the situation.) Subtyping is reflexive, transitive, and antisymmetric. Duality inverts subtyping, in that S <: R if and only if R <: S.

Expressions, processes, and typing
Expressions, processes, and typing for GV are summarized in Figure 2. We let x, y range over variables, c, d range over channel endpoints, and z range over names, which are either variables or channel endpoints.
We let e, f range over expressions, which include names, unit value, function abstraction and application, pair creation and destruction, fork a process, create a new pair of channel endpoints, send, receive, select, case, close, and wait. Function abstraction and pair creation are labeled with the multiplicity of the value created. We sometimes abbreviate expressions of the form (λ lin x.e)f to let x = e in f , as usual. A GV program is always given as an expression, but as it executes it may fork new processes.
We let P, Q range over processes, which include expressions, parallel composition, and a binder that introduces a pair of channel endpoints. The initial process will consist of a single expression, corresponding to a given GV program.
The bindings in the language are as follows: variable x is bound in subexpression e of λ m x.e, variables x, y are bound in subexpression f of let x, y = e in f , variables x i are bound in subexpressions e i of case e of {l i : x i .e i } i∈I , channel endpoints c, d are bound in subprocess P of (νc, d)P. We assume that c and d in (νc, d)P are different. The notions of free and bound names/variables as well that substitution are defined accordingly. The set of the free 14 A. Igarashi et al. Fig. 2. Expressions,processes,and typing in GV. names in P is denoted by fn(P). We follow Barendregt's variable convention, whereby all names in binding occurrences in any mathematical context are pairwise distinct and distinct from the free names (Barendregt, 1984).
We let , range over environments, which are used for typing. An environment consists of zero or more associations of names with types. Environment splitting = 1 • 2 is standard. It breaks an environment for an expression or process into environments 1 and 2 for its components; a name of unrestricted type may be used in both environments, while a name of linear type must be used in one environment or the other but not both. We write m( ) if m(T) holds for each T in , and similarly for m :> ( ).
Write e : T if under environment expression e has type T. The typing rules for expressions are standard. In the rules for names, unit, and new the remaining environment must be unrestricted, to enforce the invariant that linear variables are used exactly once. A function abstraction that is unrestricted must have only unrestricted variables bound in its closure, and a pair that is unrestricted may only contain components that are unrestricted. Thus, it is never possible to construct a pair of type, e.g., S × un T, which contains a linear type S under the unrestricted pair type constructor × un , even though such a type is syntactically allowed for simplicity. The rules for send, receive, select, case, close, and wait match the corresponding session types. For example, the following type judgment can be derived. The typing system supports subsumption: if e has type T and T is a subtype of U then e also has type U.
Write P if under environment process P is well typed. The typing rules for processes are also standard. If expression e has unrestricted type T then process e is well typed. If processes P and Q are well typed, then so is process P | Q, where the environment of the latter can be split to yield the environments for the former. And if process P is welltyped under an environment that includes channel endpoints c and d with session types S and S, then process (νc, d)P is well typed under the same environment without c and d.

Reduction
Values, evaluation contexts, reduction for expressions, structural congruence, and reduction for processes for GV are summarized in Figure 3.
Let v, w range over values, which include unit, function abstractions, pairs of values, and channel endpoints. Let E, F range over evaluation contexts, which are standard.

16
A. Igarashi et al. Write e −→ f to indicate that expression e reduces to expression f . Reduction is standard, consisting of beta reduction for functions and pairs.
Write P ≡ Q for structural congruence of processes. It is standard, with composition being commutative and associative. A process returning the unit is the identity of parallel composition, so P | () ≡ P. The order in which the endpoints are written in a ν-binder is irrelevant. Distinct prefixes commute, and satisfy scope extrusion. The Barendregt convention ensures that c, d are not free in Q in the rule for scope extrusion. Similarly for the rule to swap prefixes.
Write P −→ Q if process P reduces to process Q. Evaluating fork e returns () and creates a new process e . Evaluating new introduces a new binder (νc, d) and returns a pair (c, d) lin of channel endpoints. Evaluating send v c on one endpoint of a channel and receive d on the other, causes the send to return c and the receive to return (v, d) lin . Similarly for select on one endpoint of a channel and case on the other, or close on one endpoint of a channel and wait on the other.
Process reduction is a congruence with regard to parallel composition and binding for channel endpoints, it is closed under structural congruence, and supports expression reduction under evaluation contexts.

Gradual GV
We now introduce Gradual GV. Following standard frameworks of gradual typing (Siek & Taha, 2006;Siek et al., 2015b), Gradual GV consists of two sublanguages: an external language GGV e , in which source programs are written, and an internal language GGV i , to which GGV e is elaborated by cast-inserting translation to make necessary runtime checks explicit. The operational semantics of a program is given as reduction of processes in GGV i . We first introduce GGV i by outlining its differences to GV (Sections 3.2.1-3.2.3). Next, we introduce the syntax of GGV e , which has only expressions, because it is the language in which source programs are written, its type system, and cast-inserting translation from GGV e to GGV i (Sections 3.2.4 and 3.2.5). Finally, we discuss how an untyped variant of GV can be embedded into GGV i (Section 3.2.6).

Types and subtyping
Following the usual approach to gradual types, we extend the grammar of types with a dynamic type (sometimes also called the unknown type), written . Similarly, we extend session types with the dynamic session type, written . The extended grammar of types is given in Figure 4, where types carried over from Figure 1 are typeset in gray.
As before, we let T, U range over types and S, R range over session types. We also distinguish a subset of types which we call ground types, ranged over by T, U, and a subset of session types which we call ground session types, ranged over by S, R, consisting of all the type constructors applied only to arguments which are either the dynamic type or the dynamic session type, as appropriate.
We define to be self-dual: = . We define the multiplicity of the new types by setting to be un and to be lin. The remaining definitions of multiplicity of types carries over unchanged from Figure 1. Type is labeled unrestricted although (as we will see in the following) it corresponds to all possible types, both unrestricted and linear, and therefore we will need to take special care when handling values of type that correspond to values of a linear type. Consistent subtyping is defined over types of Gradual GV also in Figure 4. It is identical to the definition of subtyping from Figure 1, with each occurrence of <: replaced by , and with the addition of four rules for the new types: For example, we have (a) ⊕{l 1 : ! . , l 2 : ? . } ⊕{l 1 : } and (b) &{l 1 : } &{l 1 : ! . , l 2 : ? . }. Consistent subtyping is reflexive, but neither symmetric nor transitive. As with subtyping, we have S R iff R S. In Gradual GV, we will be permitted to attempt to cast a value of type T to a value of type U exactly when T U. A cast may fail at runtime: while a cast using (a) will not fail, a cast using (b) may fail because an expression of type &{l 1 : } may evaluate to a value of type, say, &{l 1 : end ! }. Two types are consistent, written T ∼ U, if T U and U T. Consistency is reflexive and symmetric but not transitive. The standard example of the failure of transitivity is that for any function type we have T → m U ∼ and for any product type we have ∼ T × n U , but T → m U ∼ T × n U . In the setting of session types one has for example ?T. S ∼ and ∼ end ! , but ?T. S ∼ end ! . Subtyping T <: U for Gradual GV essentially carries over from GV. Its definition is exactly as in Figure 1, with the addition of two rules that ensure subtyping is reflexive for the dynamic type and the dynamic session type. In contrast to consistent subtyping, subtyping T <: U guarantees that we may always treat a value of the first type as if it belongs to the second type without casting.

Expressions, processes, and typing of GGV i
Expressions, processes, and type rules of GGV i are summarized in Figure 5. The expressions of GGV i are those of GV, plus an additional form for casts. A cast is written where e is an expression of type T, and p, q range over blame labels such as 1 , 2 , . . .. For example, the following term which represents a simplified version of serveOpCast in Section 2, can be given type → un → un unit. Blame labels carry a polarity, which is either positive or negative. The complement operation, p, takes a positive label into a negative one and vice versa; complement is an involution, so that p = p. By convention, we assume that all blame labels in a source program are positive, but negative blame labels may arise during evaluation of casts at a function type or a send type. A cast raises positive blame if the fault lies with the expression contained in the cast (for instance, because it returns an integer where a character is expected), while it raises negative blame if the fault lies with the context containing the cast (for instance, because it passes an argument or sends a value that is an integer where a character is expected).
In a valid cast e : T p ⇒ U, the type T must be a consistent subtype of U (T U), the type of the entire expression. If a cast in a program fails, it evaluates to blame p q X or blame p X (which, as we see later, are treated as processes) where the blame label p and q indicate the root cause of the failure (we will explain X shortly). If the cast in (1)  p ⇒ bool is well typed. However, at runtime it raises blame by reducing to blame q p ∅, which flags the error that int is not a subtype of bool: that is, int <: bool.
Blame is indicated by processes of the form blame p q X or blame p X where p and q are blame labels, and X is a set of variables of linear type. As we will see, most instances that yield blame involve two casts, hence the form with two blame labels, although blame can arise for a single cast, hence the form with one blame label. The set X records all linear variables in scope when blame is raised, and is used to maintain the invariant that as a program executes each variable of linear type appears linearly (only once, or once in each branch of a case). Discarding linear variables when raising blame would break the invariant. Blame corresponds to raising an exception, and the list of linear variables corresponds to cleaning up after linear resources when raising an exception (for instance, closing an open file or channel). In the typing rules, the notation flv( ) refers the set of free variables of linear type that appear in . We also write flv(E) and flv(v) for the free linear variables appearing in an evaluation context E or a value v. In a running program, only free linear variables are channel endpoints, so flv(E) and flv(v) can be defined without type information.
The processes of GGV i are those of GV, plus three additional forms for references to linear values (as well as blame, described above). Recall that a value of type may contain a linear value, in which case dynamic checking must ensure that it is used exactly once. The mechanism for doing so is to allocate a reference to a linear value. We let a, b range over references. A reference is of type , and contains a value w of ground type T, where T is linear (either → lin or × lin or the dynamic session type ). References are allocated by the binding form (νa)P, and the value contained in store a is indicated by a process which is either of the form where w is a value of type T and p is a blame label. Bindings for references initially take the first form, but change to the second form after the reference has been accessed once; any subsequent attempt to access the reference a second time will cause an error.

Reduction
Values, evaluation contexts, reductions for expressions, structural congruence, and reductions for processes for GGV i are summarized in Figures 6 and 7. The values of GGV i are those of GV, plus five additional forms. Values of dynamic type have the form either v : T p ⇒ as in other blame calculi, if T is unrestricted, or a, which is a reference to a linear value, if the dynamic type wraps a linear value. Additionally, there are values of dynamic session type which take the form v : S p ⇒ . Following standard practice for blame calculus, we take a cast of a value between function types to be a value, and for similar reasons a cast from a session type to a session type is a value unless one end of the cast is the dynamic session type:

21
Additional reductions for expressions appear in Figure 6. Typical of blame calculus is the reduction for a cast between function types, often called the wrap rule: The cast on the function decomposes into two casts, one on the domain and one on the range. The fact that subtyping (and consistent subtyping) for function types is contravariant on the domain and covariant on the range is reflected in the fact that the cast on the domain is from T to T and complements the blame label p, while the cast on the range is form U to U and leaves the blame label p unchanged. Casts for products follow a similar pattern, though covariant on all components.
Reductions on session types follow the pattern of the reduction for a cast between send types: The cast on the send decomposes into two casts, one on the value sent and one on the residual session type. The fact that subtyping (and consistent subtyping) for send types is contravariant on the value sent and covariant on the residual session type is reflected in the fact that the cast on the value sent is from T to T and complements the blame label p, while the cast on the residual session type is from S to S and leaves the blame label p unchanged. The casts for the remaining session types follow a similar pattern, though covariant on all components.
Also typical of blame calculus, casts to the dynamic type factor through a ground type, when T = , T = T, and T ∼ T. This factoring is unique because for every type T such that T = there is a unique ground type T such that T ∼ T. The additional condition T = T ensures that the factoring is nontrivial and that reduction does not enter a loop. Casts from the dynamic type, and casts to and from the dynamic session type are handled analogously. Additional structural congruences and reductions for processes appear in Figure 7. Like bindings for channel endpoints, bindings for references to linear values satisfy scope extrusion and reduction is a congruence with respect to them.
The first five reduction rules for processes deal with references to linear values, ensuring that a value cast from a linear type to is accessed exactly once. As the only values of the dynamic type are casts from a ground type, expressions of interest take the form where E is an evaluation context and U is a ground type that may or may not be linear. The second rule implements the first access to a linear value by copying the value v in place 22 A. Igarashi et al. of the reference a, and updating the reference process to a → locked p, indicating that the linear reference has been accessed once. The third rule implements any subsequent attempt to access a linear value, which allocates blame to the two casts involved, negative blame p from locked p, which was a cast v : T p ⇒ before the first access, and positive blame q for the cast to access a, indicating that in both cases blame is allocated to the side of the cast of type . The blame term also contains flv(E), the set of free linear variables that appear in the context E, which as mentioned earlier is required to maintain the invariant on linear variables; all occurrences of blame contain corresponding sets of linear variables, which we will not mention further. The final two rules indicate what happens when all processes containing the reference finish execution. If the linear reference is locked then it was accessed once, and the reference may be deallocated as usual. If the reference is not locked then it was never accessed, and blame should be allocated to the context of the original cast, which discarded the value rather than using it linearly. In practice, these rules would be implemented as part of garbage collection.
The remaining six rules come in three pairs. Typical of blame calculus is the first pair, often called the collapse and collide rules: If the source type is a subtype of the target type, the casts collapse to the original value. Types are preserved by subsumption: since v has type T and T <: U then v also has type U. Conversely, if the source type is not a subtype of the target type, then the casts are in collision and reduce to blame. Blame is allocated to both of the casts involved, negative blame p for the inner cast and positive blame q for the outer cast, indicating that in both cases blame is allocated to the side of the cast of type . Our choice to allocate blame to both casts differs from the usual formulation of blame calculus, which only allocates blame to the outer cast. Allocating blame to only the outer cast is convenient if one wishes to implement blame calculus by erasure to a dynamically typed language, where injection of a value to the dynamic type is represented by the value itself, that is, the erasure of v : T p ⇒ is just taken to be the erasure of v itself. However, this asymmetric implementation is less appropriate in our situation. For session types, a symmetric formulation is more appropriate, as we will see shortly when we look at the interaction between casts and communication.
The next pair of rules transpose collapse and collide from types to session types. The final pair of rules adapt collapse and collide to the case of communication between two channel endpoints. Here is the adapted collapse rule.
ifS <: R Setting S to and R to S, this congruence can reduce the third collapse rule (on channel endpoints) to the second collapse rule (on a nested pair of casts on session types). However, even with this congruence the two collide rules are not quite equivalent. Our chosen formulation, though slightly longer, is more symmetric and easier to implement.  We also show an example of dynamic linearity checking. The following function foo takes an argument of type , cast it to end ! , and closes it: Consider an application of foo to a channel endpoint c of type end ! . It reduces as follows: If the channel endpoint is passed to a function that uses the argument more than once, blame will be raised. Let bar be λ un x.close (x : ⇒ end ! ); close (x : ⇒ end ! ) and observe that bar (c : end ! ⇒ ) reduces as follows: Then, parallel composition with a process waiting at the other end d of the endpoint c will raise blame as follows:

External language GGV e
Having defined the internal language, we introduce the external language GGV e , in which source programs are written. The syntax of expressions of GGV e is presented in Figure 8. For ease of type checking, variable declarations in functions and channel endpoint creations are explicitly typed. There are no processes in GGV e : a program is a well-typed closed expression and it is translated to a GGV i expression before it runs.
The type system of GGV e adheres to standard practice for gradually typed languages (Siek et al., 2015b;Cimini & Siek, 2016), but requires a few adaptations to cater for features not covered in previous work. We first introduce a few auxiliary definitions used in typing rules. Figure 9 defines the matching relation T U (Cimini & Siek, 2016). Roughly speaking, T U means that T can be used, after necessary runtime checking, as U. The second and third columns declare that, if T is or , then it can be used as any type or session type, respectively. Otherwise, the matching relation extracts substructure, i.e., the domain type, the codomain type, the first-element type, and so on, from T. So, we have neither unit nor end ! or end ? . Matching for the internal and external choice types is slightly involved as it has to cater for subtyping. Matching for internal choice is invoked in the type rule for an expression select l e. Thanks to subtyping, the type of e can be any internal choice with a branch for label l. Hence, matching only asks for the presence of this single label and extracts its residual.
Dually, matching for external choice is invoked in the rule for a case e of . . . expression. Again due to subtyping, the case expression can check more labels than provided by the type of e. Hence, matching allows extra branches to be checked with arbitrary residual types (l j : S j in the definition) while extracting the residual types for all branches provided by e.
Obtaining the result type of a case expression from the types of its branches requires a join operation T ∨ U that ensures that its result is (in a certain sense) a supertype of both T and U. Figure 10 contains the definitions of join and its companion meet, which is needed in contravariant positions of the type. Both operations are partial: join or meet is undefined for cases other than those listed in Figure 10.
Join of two ⊕-types can be obtained by taking the joins of the types associated with common labels. Note that labels where the joins S i ∨ R i do not exist will be dropped. On the other hand, the label set of the join of two &-types is the union of the two label sets 26 A. Igarashi et al. from the input. For the common labels in I ∩ J , the joins S k ∨ R k must exist. Join or meet is undefined if the resulting type is ⊕{} or &{} (with the empty set of labels) as they are ill-formed types.
Without the last four clauses, which deal with and , the definitions of the join and meet coincide with those for ordinary subtyping. This is motivated by the static embedding property of the Criteria for Gradual Typing (Siek et al., 2015b), which requires the typability of a GGV e term without (or in our case) to be the same as the typability under the GV typing rules. There are a few choices for the join (and meet) of and other types and we choose ∨ T to be T for any T because, as we prove later, our join then corresponds to the least upper bound with respect to negative subtyping (Wadler & Findler, 2009), which is formally defined later, and we can construct a type-checking algorithm that produces a minimal type with respect to the negative subtyping. (The least upper bound with respect to positive subtyping is not a good choice because int ∨ bool = holds, invalidating the static embedding property.) Typing rules are presented in Figure 11. The matching relation is used in elimination rules. To obtain a syntax-directed inference system, the subsumption is merged into function application, sending, select, and case. Moreover, subtyping is replaced with consistent subtyping. The type of the whole case expression is obtained by joining the types of the branches. Finally, the judgment e prog means that e is a Gradual GV program, which is a closed, well-typed GGV e expression of unrestricted type. Cast insertion discussed in the following translates a program to a GGV i expression e, which runs as a process e . For example, we can derive λ un o : .λ un c : .close (send o c) : → un → un unit.
We also develop a type-checking algorithm for GGV e by following the standard approach (Kobayashi et al., 1999;Walker, 2005). We define an algorithm CHECKEXPR( , e), which takes a type environment and an expression e and returns a type T of e and the set X of linear variables in e. We avoid nondeterminism involved in environment splitting by introducing X , which is used to check whether subexpressions do not use the same (linear) variable more than once. We present the algorithm in full and 28 A. Igarashi et al. Fig. 12. Cast insertion. prove its correctness in Appendix 6. In particular, the algorithm is shown to compute, for given and e, a minimal type with respect to negative subtyping (if a typing exists).

Cast-inserting translation
A well-typed GGV e expression is translated to a GGV i expression by dropping type annotations and inserting casts. Figure 12 presents cast insertion. The judgment e f : T means that "under type environment , a GGV e expression e is translated to a GGV i expression f at type T." Most rules are straightforward: casts are inserted where the matching or consistent subtyping is used. In each rule, blame label p is supposed to be fresh and positive. The notation f : T p ⇒ ? U is used to avoid inserting unnecessary casts.
Thanks to this optimization, we can show that a program that does not use or is translated to a cast-free GGV i expression, whose behavior obviously coincides with GV.
For example, we can derive

Embedding
One desideratum for a gradual typing system-if it is equipped with dynamic typingis that it is possible to embed an untyped (or rather, unityped) language within it (Siek et al., 2015b). An embedding of an untyped variant of GV into GGV i is given in Figure 13. Blame labels are omitted; each cast should receive a unique blame label. The untyped variant has the same syntax as the expressions of GV, but every expression has type and multiplicities are implicitly assumed to be un. The embedding extends that of (Wadler & Findler, 2009) for the untyped lambda calculus into the blame calculus.

Results
We study some of the basic properties (Siek et al., 2015b) of Gradual GV in this section. They include (1) type safety of GGV i and (2) blame safety of GGV i , (3) conservative typing of GGV e over GV, and (4) the gradual guarantee for GGV e . Since GGV i do not guarantee deadlock freedom, type safety is stated as the combination of preservation and absence of runtime errors, rather than progress. We show that (1)-(3) hold with their proof sketches.
For (4), we show that GGV e does not satisfy the gradual guarantee.

Preservation and absence of runtime errors for GGV i
We show preservation and absence of runtime errors for GGV i . The basic structure of the proof follows . In proofs, we often use inversion properties for the typing relation, such as "if x : T, then = , x : S for some S and such that S <: T and un( )," without even stating. They are easy (but tedious) to state and prove because the only rule that makes typing rules not syntax-directed is T-SUB (see, for example, (Pierce, 2002) for details). Similarly, we omit inversion for subtyping, which is syntax-directed. Proof. By induction on P ≡ Q. Use Lemmas 1, 2, and basic properties of context splitting (Vasconcelos, 2012;Walker, 2005) for the scope extrusion rules. Proof. By induction on = 1 • 2 .
Proof. By case analysis on the last rule used to derive v : T.  (2) 11 = 11 , x : U and 12 = 12 and = 11 • 12 , in which case the conclusion is similarly proved.
The following two lemmas are adapted from earlier work .

Lemma 7 (Sub-derivation introduction). If D is a derivation of E[e]
: T, then there exist 1 , 2 and U such that = 1 • 2 and D has a sub-derivation D concluding Proof. By rule induction on the first hypothesis. For β-reduction and let we use the substitution lemma (Lemma 6) and inversion of the typing relation.

Theorem 2 (Preservation for processes).
If P −→ Q and P, then Q.
Proof. By rule induction on the first hypothesis, using basic properties of context splitting (Walker, 2005;Vasconcelos, 2012) and weakening (Lemma 1). Rules that make use of context use sub-derivation introduction (Lemma 7) to build the derivation for the hypothesis, and sub-derivation elimination (Lemma 8) to build the derivation for the conclusion.
Rules for reduction to blame use Lemma 9. Reduction underneath parallel composition and scope restriction follow by induction. The rule for ≡ uses Lemma 3. Closure under evaluation contexts uses Theorem 1.
1. If T = , there is a unique ground type T such that T ∼ T.
2. If S = , there is a unique ground session type S such that S ∼ S. 3. T U iff T <: U.

S R iff S <: R.
Proof.
1. By case analysis on T.
2. By case analysis on S.
3. By case analysis on T and U. 4. By case analysis on S and R.
Lemma 11 (Canonical forms). Suppose that v : T where contains session types and , only.
Proof. By induction on the derivation on v : T.
Theorem 3 (Progress for expressions). Suppose that e : T and that only contains channel endpoints and references. Then exactly one of the following cases holds: 1. e is a value, 2.
The notion of runtime errors helps us state our type safety result. The subject of an expression e, denoted by subj(e), is c when e falls into one of the following cases and undefined in all other cases.
Two expressions e and f agree on a channel with ends in set {c, d} where c = d, denoted agree {c,d} {e, f }, a relation on two two-element sets, in the following cases: A process is an error if it is structurally congruent to some process that contains a subprocess of one of the following forms: The first two cases are typical of functional languages. The third case ensures no two threads hold references to the same channel endpoint. The fourth case ensures channel endpoints agree at all times: if one process is ready to send then the other is ready to receive, and similarly for select and case, close and wait.
For processes, rather than a progress result, we present a type safety result as our type system does not rule out deadlocks, which are formed by a series of processes each waiting for the next in a circular arrangement; these are exactly the deadlocked processes of GV. Our result holds both for GV and Gradual GV alike. The condition on in the statement is to exclude processes getting stuck due to a free variable in an application (xe) or a pair destruction (let a, b = x in e).
Theorem 4 (Absence of runtime errors). Let P where does not contain function or pair types, and let P −→ * Q. Then Q is not an error.
Proof. By induction on the length of reduction steps P −→ * Q. For the base case, where P = Q, we show P is not an error by showing all error processes cannot be well typed.
All cases use Lemma 7 and inversion of the typing relation. The cases for application and let follow from the fact that does not contain function or pair types. The third case follows from the fact that c, being the subject of expressions, is of a linear type, hence cannot occur in two distinct processes. The fourth case follows from the fact that typability implies that c and d are of dual types, which in turn implies agree {c,d} (e, f ).
A cast from T to U with label p may either return a value or may raise blame labeled p (called positive blame) or p (called negative blame). The original subtyping relation T <: U of GGV i characterizes when a cast from T to U never yields blame; relations T <: + U and T <: − U characterize when a cast from T to U cannot yield positive or negative blame, respectively; and relation T U characterizes when type T is more precise (in the sense of being less dynamic) than type U. All four relations are reflexive and transitive, and subtyping, positive subtyping, and naive subtyping are antisymmetric. Wadler & Findler (2009) have an additional rule that makes any subtype of a ground type a subtype of , i.e., T <: if T <: T. This rule is not sound in Gradual GV because our collide rule blames both casts: The four subtyping relations are closely related. In previous work (Wadler & Findler, 2009;Siek et al., 2015a) one has that proper subtyping decomposes into positive and negative subtyping, which-after reversing the order on negative subtyping-recompose into naive subtyping. Here we have three-quarters of the previous result.  Proof. By induction on types.
Here the first and second items are an implication, rather than an equivalence as in the third and fourth items and previous work. In order to get an equivalence, we would need to alter subtyping such that T <: for all T and S <: for all S, which would interfere with our Canonical Forms lemma (Lemma 11). However, implication in all four items is sufficient to ensure the most important result, Corollary 1.
The definitions of negative subtyping and naive subtyping have been changed since the conference version of the paper. Now, negative subtyping supports width subtyping and naive does not. This change is motivated by the type system for the external language, in particular the join operation. (See the discussion on the join in Section 3. 2.4.) The following technical result is used in the proof of Theorem 6.

Proof.
(1) A case analysis on T. Lemma 10 tells us that T is unique. We show the case for functions. Let T be the type U → m V ; we know that T is → m , that <: − U, and V <: + . Conclude with the positive subtyping rule for functions. (2) Similar.
We say that a process P is safe for blame label p, if all occurrences of casts involving p or p correspond to subsumptions in the (positive or negative) blame subtyping relation. Figure 14 defines judgments e safe for p and P safe for p, extended homomorphically to all other forms of expressions and processes. The safe for predicate on well-typed programs is preserved by reduction.
Theorem 6 (Preservation of safe terms). If P with P safe for p and P −→ Q, then Q safe for p.
Proof. It is sufficient to examine all reductions whose contractum involves coercions. We start with the reductions in Figure 6. The four rules starting from the one with reductum v : T p ⇒ follow from Lemma 12. Then, the standard function cast is analogous to previous work (Wadler & Findler, 2009), and the case for pairs is similar. The casts for session types (send, receive, select, case, close, and wait) are new; we concentrate on send. A process P blames label p if P ≡ (Q | R) where Q is blame p q X , blame q p X , or blame p X , for some q and X , and prefix of bindings for channel endpoints and references.
Theorem 7 (Progress of safe terms). If P and P safe for p, then P −→ Q where Q blames p.
Proof. We analyze all reduction rules whose contractum includes blame. From Figure 6 take the rule with reductum (v : T p ⇒ ) : q ⇒ U. It may blame p and q, if T <: U. However, if it is safe for p then T <: − , which cannot hold (because only <: − and T cannot be ), and similar reasoning applies for q and U. The remaining rules are similar.
We are finally in a position to state the main result of this section.
Corollary 1 (Well-typed programs cannot be blamed). Let P be a well-typed process with a subterm of the form e : T p ⇒ U containing the only occurrence of p and p in P. Then the following cases holds:: • If T <: + U then P −→ * Q where Q blames p.
• If T <: − U then P −→ * Q where Q blames p.
• If T <: U then P −→ * Q where Q blames p or p.
For example, the redex (v : T p ⇒ ) : q ⇒ U may fail and blame p and q if T <: U. And indeed we have that T <: − and <: + U, so it is not safe for p or q. However, T <: + and <: − U, and the redex will not blame p or q. Wadler and Findler (2009) explain how casting between terms related by naive subtyping always places the blame (if any) on the less-precisely typed term or context, as appropriate.

Properties of GGV e
Now we turn our attention to GGV e and prove that cast insertion succeeds for well-typed GGV e expressions and preserves typing and that the GGV e typing conservatively extends the GV typing. As we need to relate the judgments of different systems, let e denote the GGV e typing, i denote the GGV i typing, and GV denote the GV typing.
Proposition 1 goes back to an observation by Siek and Taha (2007).
Proposition 1 (Consistent Subtyping). Proof. The left-to-right direction is proved by induction on T 1 T 2 and the right-to-left is by induction on subtyping with case analysis on T 1 , T 2 , and T 2 .
The next lemma clarifies the relation between subtyping, positive and negative subtyping, and consistent subtyping.
Proof. By simultaneous induction on T 1 ∨ T 2 = U (for the first item) and T 1 ∧ T 2 = U (for the second item).
Lemma 15 (Least upper bound and greatest lower bound).
Proof. The two items are simultaneously proved by induction on T 1 <: − U and U <: + T 1 .
Theorem 8 states that cast insertion succeeds for well-typed external language and preserves typing. A few lemmas are required in preparation. Proposition 2 states that the cast-insertion translation does not insert casts for static expressions, which can be seen as expressions of GV if type annotations are removed. The proof is similar to that of Theorem 9 (1).

Proposition 2.
Suppose that and e are both static. If e f : T, then T is static and |e| = f .
Proof. By induction on e f : T. with case analysis on the rule applied last. We show one of the main cases as follows.

(Failure of) The gradual guarantee
In a gradually typed language, changing type annotations in a program should not change the static or dynamic behavior-except for runtime errors caused by casts. Such an expectation is formalized by Siek et al. (2015b) as the gradual guarantee property. It usually consists of two statements concerning the static and dynamic aspects of programs. The static counterpart of the gradual guarantee (simply called the static gradual guarantee) states that less precise type annotations make the type of an expression less precise, whereas the dynamic gradual guarantee states that making type annotations less precise does not change the final outcome of a program.
We will show that, unfortunately, GGV e satisfies neither the static nor dynamic gradual guarantee by constructing counterexamples. We analyze the problem and argue that it is not easy to recover without losing other good properties.
First, to capture the notion of programs with more precise type annotations formally, the precision over types is extended to type environments and expressions. The relation 1 2 is the least relation that satisfies · · and 1 , x : T 1 2 , x : T 2 if 1 2 and T 1 T 2 and the relation e 1 e 2 is the least precongruence that is closed under the following rules:

42
A. Igarashi et al. Using the precision, the static gradual guarantee can be stated as follows.
However, it does not hold.
Although we do not state the dynamic gradual guarantee formally, we expect at least that, if two programs e 1 and e 2 satisfy e 1 e 2 and the execution of e 1 (after cast insertion) terminates normally (at () ), then e 2 also terminates normally. Unfortunately, it would not be very difficult to see such an expectation fail. Let's consider e 1 = (λ lin x:T 1 . e 1 ) (λ lin x 1 :unit. x 1 ) and a more imprecise expression e 2 = (λ lin x: . e 2 ) (λ lin x 1 :unit. x 1 ) The former will return λ lin x 1 :unit. x 1 if l 1 is selected by another process. However, e 2 shows different behavior: the cast-inserting translation puts a cast from to T 2 = unit → un unit on x in the first branch of case in e 2 but x will be bound to (a reference to) a linear function and, if l 1 is selected, the cast will fail and raise blame.
The problem seems to stem from the fact that ∨ has subtle interaction with . For typing case-expressions, we would naturally require precision to be preserved by the join operation, i.e., if T 1 T 1 , then T 1 ∨ T 2 T 1 ∨ T 2 . However, the current definition of ∨ breaks this property as the counterexample to the static gradual guarantee above shows. Also, as we can see from the counterexample to the dynamic gradual guarantee, join with a more precise type can yield a supertype-that is, T 1 and ∨ T 2 <: T 1 ∨ T 2 hold. One possible workaround is to adapt the "lifted join" operation ∨ of the GTFL language  to Gradual GV. Like ∨, unit → lin unit ∨ unit → un unit = unit → lin unit but, unlike ∨, ∨ unit → un unit = . Thus, the lifted join would perhaps recover the gradual guarantee. However, it seems that the lifted join is the least upper bound operation for no known ordering between types and we would lose the minimal type property of GGV e if we used ∨. Also, the lifted join has the following property: T ∨ is T only if T does not have nontrivial supertypes; otherwise T ∨ is . For example, unit ∨ = unit and unit → lin unit ∨ = unit → lin unit but unit → un unit ∨ = (because unit → un unit <: unit → lin unit). It means that the standard narrowing propertyif , x : T 1 e e : T and T 2 <: T 1 , then 1 , x : T 2 e e : T-does not hold. We leave more detailed analysis of the problem and possible remedy for future work. Findler and Felleisen (2002) introduced two seminal ideas: higher-order contracts that dynamically monitor conformance to a type discipline, and blame to indicate whether it is the library or the client which is at fault if the contract is violated. Taha (2006, 2007) introduced gradual types to integrate untyped and typed code, while Flanagan (2006) introduced hybrid types to integrate simple types with refinement types. Both used target languages with explicit casts and similar translations from source to target; both exploit contracts, but neither allocates blame. Motivated by similarities between gradual and hybrid types, Wadler and Findler (2009) introduced blame calculus, which unifies the two by encompassing untyped, simply typed, and refinement-typed code. As the name indicates, it also restores blame, which enables a proof of blame safety: blame for type errors always lays with less-precisely typed code-"well-typed programs can't be blamed."

Gradual typing
While the first investigations of gradual typing were based on simply typed calculi, subsequent work has explored gradual typing for a range of typing features. Polymorphism (Ahmed et al., 2011;Igarashi et al., 2017b;Toro et al., 2019) has proved to be quite tricky, with one important question about the Jack-of-All-Trades Principle (Ahmed et al., 2011) still open. A gradual treatment of record types may be found in the paper on Abstract Gradual Typing (AGT) . Variant types have proved elusive, but union types have been considered (Siek & Tobin-Hochstadt, 2016) along with intersection types and polymorphism as part of a set-theoretical reevaluation of gradual principles (Castagna et al., 2019).
Moving toward session types, systems with gradual typestate have been considered (Wolff et al., 2011;Garcia et al., 2014); they extend an object-oriented language with typestate by a dynamic type and define a suitable translation to an internal language with casts. The additional complication is to track the current typestate at runtime. Thiemann (2014) describes a system with gradual types and session types, but in it only types (and not session types) can be gradual.
Effect systems have been gradualized based on ideas from abstract interpretation by Banados Schwerter and others (2014). While the former work only presented a gradualization of effects themselves a subsequent extension adds a full treatment of types (Schwerter et al., 2016). Related ideas are explored by Thiemann and Fennell (2014), who present an approach to gradualize annotated type systems, like units and security labels. Following an earlier approach for gradual security typing for simply typed lambda calculus (Disney &Flanagan, 2011), Fennell andThiemann (2013) developed gradual security for an ML core language with references and subsequently for a Java core language LJGS with polymorphic security labels (Fennell & Thiemann, 2016). Toro and others (2018) developed a gradual calculus with slightly different features from first principles using the AGT  approach. In each of these approaches, special measures have to be taken to ensure the key property of noninterference. Gradual type systems related to session types also include the runtime enforcement of affine typing of Tov and Pucella Tov and Pucella (2010).
As noted in the introduction, gradual typing may be important as a bridge to type systems that go beyond what is currently available, including dependent, effect, and session types. There is a range of gradual type systems for dependent types. Ou and others (2004) bridge the gap between simply typed lambda calculus and a calculus with indexed types. In Flanagan's hybrid typing (2006), subtyping judgments are either proved or disproved statically by SMT theorem proving or residualized as runtime checks. Greenberg and others (2010) consider different styles of contracts in simply typed and dependently typed settings. Lehmann and Tanter (2017) present an approach that uses the AGT methodology to obtain a gradual system that mediates between simple types and dependent refinement types. This work has been augmented with type inference by Vazou and others (2018) and it has been extended to verification (Bader et al., 2018) where specifications may contains unknown subformulas. Jafery & Dunfield (2017) consider gradualized refinements for sum types with the goal to control errors in pattern matching.
Gradual ownership types (Sergey & Clarke, 2012) is a gradualization of the Owners as Dominators principle of ownership. Its theory is built with similar principles as other gradual languages, but its flavor is different as ownership is not a semantic property, but a structure imposed by the programmer.
Siek and others (2015b) review desirable properties of gradually typed languages, while Wadler Wadler (2015) discusses history of the blame calculus and why blame is important. These papers provide overviews of the field, each with many further citations. Many of the above-cited works strive to fulfill the properties of Siek and others, not all of them are successful, but further discussion of the properties exceeds the scope of this survey of related work.
TypeScript TPD (Williams et al., 2017) applies contracts to monitor the gradual typing of TypeScript, and evaluates the successes and shortcomings of contracts in this context.

Session types
Session types were introduced by Honda (1993) and Honda et al. (1998). The original system addressed binary sessions, whereby types describe the interaction between two partners. Binary sessions were eventually extended to the more general setting of multiparty session types (Honda et al., 2016). Recent years have seen the introduction of session types in programming languages, and software development tools. We review the most important works.
Session types inspired the design of several programming languages. Sing# (Fähndrich et al., 2006) constitutes one of the first attempts to introduce session types in programming languages. An extension of C, Sing# was used to implement Singularity, an operating system based on message passing. Gay and others (2010) propose attaching session types to class definitions, allowing to treat channels as objects for session-based communication in distributed systems. SePi (Franco & Vasconcelos, 2013) is a concurrent, message-passing programming language based on the pi-calculus, featuring a simple form of refinement types. SILL (Toninho et al., 2013;Pfenning & Griffith, 2015) is a higher-order session functional programming language, featuring process expressions as first class objects via a linear contextual monad. Concurrent C0 (Willsey et al., 2017) is a type-safe C-like programming language equipped with channel communication governed by session types. Links (Lindley & Morris, 2017) is a functional programming language designed for tierless web applications that natively supports binary session types.
Proposals have been made to retroactively introduce session types in mainstream programming languages. Session Java (Hu et al., 2008) introduces API-based session primitives in Java, while (Hu et al., 2010) presents a Java language extension and type discipline for session-based event-driven programming. Featherweight Erlang (Mostrous & Vasconcelos, 2011) imposes a session-based type system to discipline message passing in Erlang. Mungo (Kouzapas et al., 2016) is a tool for checking Java code against session types, presented in the form of typestates. Embedding of session types have been proposed for Haskell (Pucella & Tov, 2008;Sackman & Eisenbach, 2008;Polakow, 2015;Orchard & Yoshida, 2016;Lindley & Morris, 2016a), OCaml , Scala (Scalas & Yoshida, 2016), and Rust (Jespersen et al., 2015). Most of these embeddings delegate linearity checks to the runtime system.
Session types can be used in the software development process under different forms, including languages to describe protocols, specialized libraries to invoke session-based communication primitives, provision for runtime monitoring against session types, and extended type checkers. Scribble  is a language-agnostic protocol description formalism used in many different tools. Multiparty Session C (Ng et al., 2012) uses Scribble, a compiler plug-in, and a C library to validate against session types. Hu and Yoshida (2016) generate protocol-specific Java APIs from multiparty session types described in Scribble. SPY (Neykova et al., 2013) generates runtime monitors for endpoint communication from Scribble protocols. Neykova and Yoshida (2014) designed and implemented a session actor library in Python together with a runtime verification mechanism. Bocchi and others (2017) present a theory that incorporates both static typing and dynamic monitoring of session types. Fowler (2016) describes a framework for monitoring Erlang applications against multiparty session types. Neykova and Yoshida (2017) investigate failure handling for Erlang processes in a system that dynamically monitors session types.

Conclusions
We presented the design of Gradual GV, which combines a session-typed language GV along the lines of  with a blame calculus along the lines of Wadler and Findler (2009), and with dynamic enforcement of linearity along the lines of Tov and Pucella (2010). We established expected results for such a language, including type safety and blame safety. The gradual guarantee however does not hold, and it is not clear how it can be recovered without losing other good properties.
Much remains to be done; we consider just one future direction here. The embedding of linear types in the unrestricted dynamic type relies on an indirection through a cell in the store. In our present work, these cells are used once and then discarded. This one-shot policy imposes a certain usage pattern on linear values embedded in the unityped language. In particular, the send and receive operations on a channel need 46 A. Igarashi et al. to be chained as in (close (send v 2 (send v 1 c))). However, one could imagine a unityped language where one may use the channel nonlinearly in an imperative style as in (send v 1 c; send v 2 c; close c), mimicking the style of network programming in conventional languages. This style can also be supported by a variant of Gradual GV with a multi-shot policy that restores an updated channel to the same cell from which it was extracted. We leave the full formalization of this policy to future work.