Soundly Handling Linearity

We propose a novel approach to soundly combining linear types with multi-shot effect handlers. circear type systems statically ensure that resources such as file handles and communication channels are used exactly once. Effect handlers provide a rich modular programming abstraction for implementing features ranging from exceptions to concurrency to backtracking. Whereas conventional linear type systems bake in the assumption that continuations are invoked exactly once, effect handlers allow continuations to be discarded (e.g. for exceptions) or invoked more than once (e.g. for backtracking). This mismatch leads to soundness bugs in existing systems such as the programming language Links, which combines linearity (for session types) with effect handlers. We introduce control-flow linearity as a means to ensure that continuations are used in accordance with the linearity of any resources they capture, ruling out such soundness bugs. We formalise the notion of control-flow linearity in a System F-style core calculus Feff∘ equipped with linear types, an effect type system, and effect handlers. We define a linearity-aware semantics in order to formally prove that Feff∘ preserves the integrity of linear values in the sense that no linear value is discarded or duplicated. In order to show that control-flow linearity can be made practical, we adapt circks based on the design of Feff∘, in doing so fixing a long-standing soundness bug. Finally, to better expose the potential of control-flow linearity, we define an ML-style core calculus Qeff∘, based on qualified types, which requires no programmer provided annotations, and instead relies entirely on type inference to infer control-flow linearity. Both linearity and effects are captured by qualified types. Qeff∘ overcomes a number of practical limitations of Feff∘, supporting abstraction over linearity, linearity dependencies between type variables, and a much more fine-grained notion of control-flow linearity.


INTRODUCTION
Many programming languages support linear resources such as file handles, communication channels, network connections, and so forth.Special care must be taken to preserve the integrity of linear resources in the presence of first-class continuations that may be invoked multiple times [Friedman and Haynes 1985], as a linear resource may be inadvertently be accessed more than once.Java [Pressler 2018] and OCaml [Sivaramakrishnan et al. 2021] have each recently been retrofitted with facilities for programming with first-class continuations that must be invoked exactly once, partly in order to avoid such pitfalls.Nonetheless, multi-shot continuations are a compelling feature, supporting applications such as backtracking search [Friedman et al. 1984] and probabilistic programming [Kiselyov and Shan 2009].In this paper we explore how to soundly handle linearity in the presence of multi-shot effect handlers [Plotkin and Pretnar 2013].
We first illustrate the issues with combining linearity with multi-shot effect handlers by exhibiting a soundness bug in the programming language L [Cooper et al. 2006], which is equipped with linear session-typed channels [Lindley and Morris 2017] and effect handlers with multi-shot continuations [Hillerström et al. 2020a].We begin by defining a function outch that forks a child process and returns an output channel for communicating with it.The idea is that we will use a combination of exceptions and multi-shot continuations to send two integers, rather than an integer followed by a string, along the endpoint (with session type !Int.!String.End) returned by the function outch.
Authors' addresses: Wenhao Tang, The University of Edinburgh, United Kingdom, wenhao.tang@ed.ac.uk;Daniel Hillerström, Huawei Zurich Research Center, Switzerland, daniel.hillerstrom@ed.ac.uk;Sam Lindley, The University of Edinburgh, United Kingdom, sam.lindley@ed.ac.uk;J. Garrett Morris, University of Iowa, USA, garrett-morris@uiowa.edu.The primitive fork creates a child process and two endpoints of a session-typed channel.One endpoint is passed to the child process and the other endpoint is returned to the caller.Here the function returns an output endpoint of type !Int.!String.End and the child process is supplied with an input endpoint of type ?Int.?String.End.The child receives an integer and a string on the input endpoint, then prints them out before closing the endpoint.Now we invoke outch in a context in which we exploit the power of multi-shot continuations to return twice and the power of exceptions to abort the current computation.We handle a computation that performs two operations: 1) Choose : () => Bool; and 2) Fail : forall a. () => a.The handled computation invokes outch, forking a child process and binding the output endpoint of the resulting channel to oc.Next, it invokes the operation Choose to select between two possible integer messages, which is sent on the channel.Then, it performs the Fail operation, before sending a string along the channel and closing it.This is all very well and satisfies the type-checker; however, the described control flow is not actually what happens, because in fact the continuation of Choose is invoked twice and the continuation of Fail is never invoked.The behaviours of Fail and Choose are defined by the corresponding operation clauses of the handler.For Fail the captured continuation is discarded (it must be: it is never bound); for Choose the continuation is bound to resume and invoked twice: first with true and then with false.
Running the program causes a segmentation fault when printing the received values, as it erroneously attempts to concatenate a string with an integer.To see why, follow the control flow of the parent process.It performs Choose, which initially selects 42 and sends it over the channel.The child process receives this integer and subsequently expects to receive a string.Back on the parent process execution is aborted via Fail, which causes the initial invocation of resume to return, leading to the second invocation of resume, which restores the aborted context at the point of selecting an integer.Now Choose selects 84 and sends it over the channel.The child process receives this second integer, mistakenly treating it as a string.
In this paper we rule out such soundness bugs by tracking control flow linearity: a means to statically assure how often a continuation may be invoked, mediating between linear resources and effectful operations to ensure that effect handlers cannot violate linearity constraints on resources.
The main contributions of this paper are: • We give high-level overview of the main ideas of the paper through a series of worked examples that illustrate the difficulties of combining effect handlers with linearity, how they can be resolved by tracking control flow linearity, and how the approach can be refined using qualified types [Jones 1994] (Section 2).• We introduce F • eff (pronounced "F-eff-pop"), a System F-style core calculus equipped with linear types, an effect type system, and effect handlers (Section 3).We prove syntactic type soundness and a semantic linear safety property.
• Inspired by F • eff we implement control flow linearity in L , fixing a long-standing typesoundness bug (Section 4).
• Motivated by expressiveness limitations of F • eff we introduce Q • eff (pronounced "Q-eff-pop"), an ML-style core calculus inspired by [Morris 2016] and R [Morris and McKinna 2019], based on qualified types (Section 5).We prove soundness and completeness of type inference for Q • eff .Along the way, we identify a semantic soundness bug in and conjecture a fix.
Section 6 outlines how control flow linearity applies to shallow handlers [Hillerström and Lindley 2018].Section 7 discusses related work and Section 8 conclude and discusses future work.

OVERVIEW
In this section we give a high-level overview of the main ideas of the paper by way of a series of examples.We first compare standard value linearity with non-standard control flow linearity, illustrating how the latter may be tracked in an explicit calculus F • eff (Section 3).For readability we omit uninteresting syntactic artifacts from our examples.We show how control flow linearity allows linear resources and multi-shot continuations to coexist peacefully.We then highlight two limitations of F • eff : linear types require syntactic overhead which harms modularity, and row-polymorphism based effect types lead to coarse tracking of control flow linearity.We exploit qualified types to relax both limitations in an ML-style calculus Q • eff (Section 5).

Value Linearity
Value linearity classifies the use of values: linear values must be used exactly once whereas unlimited values can be used zero, one, or multiple times.1 Equivalently, value linearity characterises whether values contain linear resources: linear values can contain linear resources whereas unlimited values cannot.Conventional linear type systems track value linearity.F • eff adapts the subkinding-based linear type system of F • [Mazurak et al. 2010].The linearity of a value type is part of its kind Type and can be either linear • or unlimited •.For example, file handles are linear resources (File : Type • ) and integers are unlimited resources (Int : Type • ).
A linearity annotation on a -abstraction defines the linearity of the function itself.Consider the following function faithfulWrite which takes a file handle and returns another function that takes a string , faithfully writes to and then closes the file handle.
The outer unlimited function (→ • ) yields a linear function (→ • ) expecting a string.The linear type system dictates that the inner function is linear as it captures the linear file handle .
One important property of value linearity is that unlimited value types can be treated as linear value types, as it is always safe to use unlimited values (which contain no linear resources) just once.This property is embodied by the subkinding relation ⊢ Type • ≤ Type • in F • eff .For instance, consider the polymorphic identity function.
The return type of the function is a computation type !{ } where is the linear type of values returned ( is used exactly once) and is the row of effects performed by the function.(We chose to omit the corresponding effect annotations in the signature of faithfulWrite because they are empty, but henceforth we will write them explicitly.)Subkinding allows the identity function to be applied to both linear and unlimited values.It is always sound to use an unlimited value exactly once.Thus, we have both ⊢ Int : Type • and ⊢ File : Type • , and if is an effect row type:

Control Flow Linearity
Control flow linearity tracks how many times control may enter a local context: a control-flowlinear context must be entered exactly once; a control-flow-unlimited context may be entered zero, one, or multiple times.Equivalently, control flow linearity characterises whether a local context captures linear resources: a control-flow-linear context can capture linear resources; a controlflow-unlimited context cannot.
To better explain control flow linearity, we first reprise the soundness problem due to the interaction of linear resources and multi-shot continuations of Section 1 via a simpler example in F • eff .Consider the following function dubiousWrite ✗ , which takes a file handle and non-deterministically writes "A" or "B" to it depending on the result of Choose.We ignore control flow linearity for now.
The do Choose () expression invokes operation Choose with a unit argument.F • eff adapts an effect system based on Rémy-style row polymorphism [Hillerström and Lindley 2016;Lindley and Cheney 2012].Effect types in F • eff are rows containing operation labels with their signatures and ended with potential row variables.The effect type {Choose : () ։ Bool} denotes that dubiousWrite ✗ may invoke the operation Choose, which takes a unit and returns a boolean value as indicated by its signature () ։ Bool.The problem arises when we handle Choose using multi-shot continuations.
The file "C.txt" is opened and the file handle is bound to before dubiousWrite ✗ is handled by an effect handler that handles the Choose operation.In the handler clause, binds the continuation of Choose, which expects a parameter of type Bool.As is invoked twice (first with true and then with false), the file handle is written and closed twice, which leads to a runtime error because it is closed before the second write.The essential problem is that the continuation of ℎ should be used linearly as it captures the linear file handle , but it is invoked twice by the effect handler.Conventional linear type systems cannot detect this kind of error as they only track value linearity.Motivated by the observation that only a local context, reified as the continuation of an operation, may be captured by a multi-shot handler, we track control flow linearity at the granularity of operations.We use the control flow linearity of an operation to represent the control flow linearity of the continuation of the operation.Control-flow-linear operations can be used in contexts which may contain linear resources, whereas control-flow-unlimited operations cannot.An operation signature ։ is annotated with a linearity to denote its control flow linearity.The dubiousWrite ✗ function can now be rewritten to correctly track control flow linearity as follows.
The type of dubiousWrite ✓ now tracks that the operation Choose : () ։ • Bool invoked by it is control flow linear.We also annotate let-bindings with linearity information.In let ← in , the term has control flow linearity , and in particular the • annotations on the let-bindings in dubiousWrite ✓ permit the use of the linear file handle throughout.The linear type system of F • eff uses the control flow linearity of operations to restrict the use of continuations in handlers, which ensures that control-flow-linear contexts are entered only once.For instance, consider the handling of dubiousWrite ✓ with the same multi-shot handler.let ← open "C.txt" in handle (dubiousWrite ✓ ) with {Choose _ ↦ → true ; false} This is ill-typed: as Choose is control flow linear, the resumption is given a linear function type and cannot be invoked twice.
We lift the control flow linearity of operations to effect row types and reflect it in their kinds Row .Similar to value linearity, we also have a subkinding relation for control flow linearity.Recall that the control flow linearity of (the operations in) effect row types is actually the control flow linearity of their contexts, not themselves.This induces a duality between value linearity and control flow linearity paralleling the duality between positive values and negative continuations.As a consequence, the subkinding relation for control flow linearity is ⊢ Row • ≤ Row • , the reverse of that for value linearity.Intuitively, this says that control-flow-linear operations can be treated as control-flow-unlimited operations, because it is safe to use control-flow-linear operations in unlimited contexts.For example, consider the following function tossCoin which takes a function that returns a boolean and tosses a coin using this function.
As no linear resource is used, the effect type of tossCoin and its parameter is given by a controlflow-unlimited row variable : Row • .Via subkinding, we can instantiate with operations with either control flow linearity.For instance, suppose ⊢ 1 : Row • and ⊢ 2 : The subkinding relation of control flow linearity only influences how operations are used, not how they are handled.We can use control-flow-linear operations as control-flow-unlimited operations (i.e., use them in unlimited contexts), but this does not imply that we can handle control-flowlinear operations as control-flow-unlimited operations (i.e., handle them by resuming any number of times).Our linear type system does not allow control-flow-linear operations to be handled by multi-shot handlers despite the subkinding relation Row • ≤ Row • .This is because when handling, we directly look at the control flow linearity on operation signatures instead of their kinds, where no ։ • can be upcast to ։ • .This can be seen more clearly from the typing rules in Section 3.2.We formally state the soundness of F • eff in Sections 3.4 and 3.5.

alified Linear Types
As we have seen from the examples so far, F • eff requires linearity annotations on -abstractions and let-bindings.Though this can suffice for an explicit calculus, it can prove cumbersome for practical programming languages and curtail the modularity of programs.Unfortunately, we cannot entirely overcome these limitations by introducing subsumption relations between types, or using Hindley-Milner type inference to infer them.The reason is that there are inner dependencies on the linearity.For instance, consider the following function verboseId which is almost the same as the function id in Section 2.1 but outputs the log message "id is called" using the operation Print : String ։ () before returning.
Depending on different choices of 1 , 2 , 3 and 4 , we can give ten well typed variations of verboseId.Their types are shown as follows, omitting primary kinds and signatures for readability.
The key observation is that the control flow linearity of the operation Print (as well as the row variable ) depends on the value linearity of the parameter type , because the parameter is used in the continuation of Print.To express this kind of dependency, we use a linear type system based on qualified types inspired by [Morris 2016].In the ML-style calculus Q • eff with qualified linear types, verboseId can be written and ascribed a principal type as follows.
The linearity variables and ′ quantify over • and •.We do not use kinds to represent linearity of types variables; instead, all linearity information is represented using predicates of the form ′ , where is a value type, row type or linearity type (•, • or a linearity variable).The type scheme of verboseId is extended with the predicate , meaning that the value linearity of is less than that of , which is the control flow linearity of Print.This type scheme succinctly expresses all ten possibilities listed above.The type inference algorithm of Q • eff (Section 5.4) infers all such linearity dependency constraints without the need for any type, effect, or linearity annotations.

2.4
alified Effect Types In addition to the syntactic overhead of linear types, the row-based effect system of F • eff is also not entirely satisfying when tracking control flow linearity.Row-based effect systems have demonstrated their practicality in research languages such as L [Hillerström and Lindley 2016], K [Leijen 2017], and F [Lindley et al. 2017].In such effect systems, sequenced computations must have the same effect type, which can be smoothly realised by unification in systems based on Hindley-Milner type inference.However, though fixing effect types between sequenced computations is often acceptable, it does introduce some imprecision, and this can become more pronounced when control flow linearity is brought into the mix.
To see the problem concretely in F • eff , consider the following function verboseClose which takes a file handle, reads a string using the operation Get : () ։ String, closes the file handle, and outputs the string using the operation Print : String ։ ().
Note that the second let-binding does not need to be annotated as linear, because the linear resource does not appear after it.The linear resource also does not appear in the continuation of Print.Since 1 , 2 , and should be equal in the row-based effect system of F • eff , omitting the full operation signatures for simplicity, we could write = 1 = 2 = {Get : •, Print : •} in the ideal case.However, this is actually ill-typed because all operations in 1 should be control flow linear, as the linear resource is used in their continuations.
An intuitive way to relax this limitation of F • eff is to introduce a trivial subtyping relation on concrete effect row types.We say 1 is a subtype of 2 , if all operation labels in 1 are also in 2 with the same signatures, and when 1 ends with a row variable, 2 must end with the same row variable.Then, in the verboseClose example, we can write 1 = {Get : •}, 2 = {Print : •}, and = {Get : •, Print : •}, which are safe given that 1 and 2 are both subtypes of .We call the subtyping relation trivial because it does not allow subtyping between row variables; an open row 1 is a subtype of 2 only if 2 contains the same row variable as 1 .For the above verboseClose example this works, but for other functions which make greater use of polymorphism, it can still seem overly-restrictive.For instance, consider the following function sandwichClose which takes two functions and a file handle, and makes a sandwich using them.
Using our trivial-subtyping workaround, we require both 1 and 2 to be subtypes of .The problem appears when we try to be polymorphic over 1 and 2 .Because they are subtypes of the same row type , their row variables must be the same, i.e., we can only write 1 = 2 = in F • eff .To support non-trivial subtyping relations between row variables, we may again use qualified types, this time to express row subtyping constraints.In addition to qualified linear types, Q • eff also supports qualified effect types inspired by R [Morris and McKinna 2019].In Q • eff , the function sandwichClose can be given the following type.Note that here we still choose to fix functions to be unlimited for readability.
The constraints 1 and 2 express that rows 1 and 2 are contained in , and the constraint File 1 expresses that the value linearity of File is less than the control flow linearity of 1 , which essentially means that 1 is control flow linear.As in Section 2.3, the type inference algorithm of Q • eff infers these row subtyping constraints without the need for any annotation.The qualified linear types and qualified effect types of Q • eff are decidable.We give a constraint solving algorithm which checks the satisfiability of both linearity constraints and row constraints in Section 5.6.

AN EXPLICIT HANDLER CALCULUS WITH LINEAR TYPES
In this section, we present the syntax, type-and-effect system, operational semantics and metatheory of F • eff , a System F-style fine-grain call-by-value calculus with linear types and effect handlers.
eff is based on the core language of L which adapts the subkinding-based linear type system of F • [Mazurak et al. 2010] and a row-based effect system [Hillerström and Lindley 2016;Lindley and Cheney 2012].The linear type system and effect system of F • eff are extended to track control flow linearity, which addresses the soundness problem arising from the interference of linear resources and multi-shot continuations.We show that F • eff is truly linearity safe by defining a linearity-aware semantics and proving that no linear resource is discarded or duplicated during evaluation in the presence of multi-shot effect handlers.

Syntax and Kinding Rules
Figure 1 shows the syntax of types, kinds, contexts, values, and computations of F • eff .We introduce a syntactic category for linearity consisting of • and •, which intuitively means unlimited and linear, respectively.The meaning of linearity varies for values and effects; value types track value linearity, and effect types track control flow linearity.Everything relevant to linearity is highlighted in the figure.The remaining part is a relatively standard fine-grain call-by-value calculus with effect handlers and row-based effect system [Hillerström et al. 2020a  In examples we will freely make use of base types and algebraic data types whose treatment is quite standard.We elect to allow polymorphic computation types rather than applying the value restriction.
A computation type !comprises a result value type and an effect type specifying the operations that the computation might perform.Effect types { } are represented by row types .Each operation label in rows is annotated with a presence type , which indicates that the label is either absent Abs, present with signature ։ , or polymorphic in its presence.An operation signature ։ describes an operation with parameter of type that returns a result of type and whose control flow linearity is .Row types are either open (ending with a row variable ) or closed (ending with •, which we often omit).We identify rows up to reordering of labels and ignore absent labels in closed row types [Rémy 1994].Handler types ⇒ represent handlers transforming computations of type to computations of type .By convention, we let range over value type variables, over row type variables, and over presence type variables, but we also let range over all over them (e.g. when binding quantifiers of unspecified kind).
Function application and type application are standard.A computation (return ) returns the value .An operation invocation (do ℓ ) invokes the operation ℓ with parameter .They are both annotated with their effect types for deterministic typing.Sequencing let ← in evaluates and binds its result to in .The linearity basically indicates the control flow linearity of .Handling handle with handles computation with handler .Handlers are given by a return clause return ↦ → , which binds the returned value as in , and a list of operation clauses ℓ ↦ → , which bind the operation parameter to and continuation to in .We have six kinds , one for each syntactic category of types.Kinds are parameterised by linearity .The kinds of value types Type denote value linearity, and the kinds of presence types Presence and row types Row L denote control flow linearity.The label set L tracks the labels that should not appear in a row, which is used to avoid duplicated labels in rows.The kinds of effect, computation, and handler types are not annotated with any linearity information.Type contexts Γ associate value variables with types, and kind contexts Δ associate type variables with kinds.
Figure 2 gives the kinding rules.Linearity-relevant parts are highlighted.The kinding relation Δ ⊢ : states that type has kind in context Δ.The subkinding relation ⊢ ≤ ′ states that is a subkind of ′ .We sometimes write simply Δ ⊢ : for value, row and presence types when the underlying kind is clear.The kinding rules for effect, computation, and handler types are standard [Hillerström et al. 2020a] and irrelevant to linearity (K E , K C , and K H ).The kind context maintains kinds for variables (K T V ).The value linearity of function and polymorphic types comes from their annotations (K F and K F ). Base types have their own value linearity, e.g., ⊢ File : • and ⊢ Int : •.The value linearity of (omitted) algebraic datatypes like pair types ( , ) is lifted from their components; ⊢ ( , ) : As shown in Section 2.1, for value linearity, we have a subkinding relation ⊢ Type • ≤ Type • given by subkinding rules S L and S T .This allows us to use unlimited value types as linear value types since it is always safe to use unlimited values linearly (e.g., the function id in Section 2.1).
We track control flow linearity at the granularity of operations, and lift it to the kinds of presence types and row types.Absent labels and empty rows can be given any control flow linearity (K A and K E R ).The control flow linearity of present labels comes directly from operation signatures (K P ).The control flow linearity of row extensions are given by the labels and remaining rows (K E R ).As shown in Section 2.2, control flow linearity is dual to value linearity in some sense: we have given by subkinding rules S L , S P , and S R .This allows linear effect rows to be used as unlimited effect rows as it is always safe to use control-flow-linear operations in unlimited contexts (e.g., the function tossCoin in Section 2.2).

Typing Rules
We define two auxiliary relations in Figure 3 for typing rules.The judgement Δ ⊢ Γ : states that under kind context Δ all types in Γ have linearity .As the subkinding relation for value linearity holds that Type • ≤ Type • , the relation Δ ⊢ Γ : • guarantees that all variables in Γ are unlimited and the relation Δ ⊢ Γ : • is a tautology.Dually, as the subkinding relation for control flow linearity holds that Row • ≤ Row • , the relation Δ ⊢ : • guarantees that all operations in are control flow linear and the relation Δ ⊢ : • is a tautology.The context splitting judgement Δ ⊢ Γ = Γ 1 + Γ 2 states that under kind context Δ the type context Γ is well formed and can be split into two contexts Γ 1 and Γ 2 such that each linear variable only appears in one of them.We write Δ ⊢ Γ 1 + Γ 2 when we only care about splitting results, and write Γ 1 + Γ 2 in typing rules when the kind context Δ is clear.
The typing rules for values, computations, and handlers are given in Figure 4. Linearity-relevant parts are highlighted.The relations Δ; Γ ⊢ : , Δ; Γ ⊢ : , and Δ; Γ ⊢ : ⇒ , state respectively that: value has type , computation has type and handler has type ⇒ in contexts Δ and Γ.As usual, the type contexts and types are well formed under the kind contexts.Δ; Γ ⊢ : The T V rule requires the remaining context to be unlimited.The T A and T TA rules check the value linearity of functions and polymorphic computations against that of the context via the premise Δ ⊢ Γ : .The typing rules for function application and type application are standard (T A and T TA ).Note that we need to split the context in the T A rule to avoid duplicating linear variables.The T R rule does not constrain the effects.The T D rule ensures that the operation ℓ and its parameter agree with the effect signature .The T H rule uses a handler of type ⇒ to handle a computation of type .
The T H rule checks that (deep) handlers must not use any linear variables via the premise Δ ⊢ Γ : • because they are recursively applied during evaluation.More importantly, it connects the control flow linearity of operations with the value linearity of resumption functions.In the typing judgement of each operation clause ℓ : ։ , the continuation is given the value linearity , which is exactly the control flow linearity of ℓ that restricts the use of ℓ 's continuation.Concretely, when = •, the continuation of ℓ may use some linear resources.Making linear guarantees that they are used exactly once.When = •, the continuation of ℓ must not use any linear resources and is unlimited.Note that the subkinding relation Row • ≤ Row • does not influence the handling behaviour, because the T H rule uses the linearity annotations on operation signatures.
The T S rule for sequencing is the most important rule for tracking control flow linearity, because this is the primary source of sequential control flow in a fine-grain call-by-value calculus.Though handling is another source of sequential control flow, deep handlers are unlimited and cannot influence control flow linearity.We will discuss the extension of shallow handlers which may capture linear resources and influence control flow linearity in Section 6.
Remember that for let ← in , the linearity annotation indicates the control flow linearity of which determines how many times the control can enter .Concretely, when = •, may use some linear variables bound outside (Δ ⊢ Γ 2 : •), and all operations in should be control flow linear (Γ ⊢ : •); when = •, cannot use any linear variables from the context (Δ ⊢ Γ 2 : •), and operations in have no restriction on their control flow linearity (Δ ⊢ : •).The dubiousWrite ✓ in Section 2.2 is an example.Note that technically, the third sequencing let • ′ ← write ( , ) in close ′ can be changed to let • because no linear variable bound outside is used by the context let ′ ← _ in close ′ .
As we observed by the function verboseClose in Section 2.4, the fact that the T S rule requires the and to have the same effect type is too restrictive for tracking control flow linearity.We can improve it by defining a trivial subtyping relation on effect row types as shown in Figure 5.

The subtyping relation Δ ⊢
′ : makes it explicit that and ′ are well kinded and can be given kind under kind context Δ.It simply requires that all operation labels with their signatures and row variable in must also appear in ′ .The slightly more precise typing rule for sequencing is shown as follows.
This subtyping relation does not allow non-trivial subtyping between row variables.We consider a more expressive alternative using qualified types in Section 5.

Operational Semantics
where Figure 6 gives a standard small-step operational semantics for F • eff [Hillerström et al. 2020a].It is clear from the definition of evaluation contexts that let-binding and handling are indeed the only two constructs that influence the control flow.

Metatheory
We now prove a type soundness result for F • eff .First we define normal forms of computations.Definition 3.1 (Computation normal forms).We say a computation is in a normal form with respect to , if it is either of the form Syntactic type soundness of F • eff relies on progress and subject reduction.The proofs can be found in Appendices A.2 and A.3.

T 3.2 (P ).
If ⊢ : !, then either there exists such that or is in a normal form with respect to .We now show that our tracking of value linearity and control flow linearity in the type system is sound, by proving that linear variables never appear in terms that are claimed to be unlimited.In F • eff , a term is claimed to be unlimited if it appears in an unlimited value, a control-flow-unlimited context, or a deep handler.The following theorem covers all three of these cases.

Unlimited continuations are unlimited: if
The proof can be found in Appendix A.1.However, Theorem 3.4 only cares about the static tracking of linear variables.It says nothing about the use of linear values during evaluation directly.In the next section, we prove that in F • eff no linear value is ever discarded or duplicated during evaluation, by defining a linearity-aware semantics inspired by Walker [2005], Mazurak et al. [2010], and Morris [2016].

Linearity Safety of Evaluation
In this section, we design a linearity-aware semantics of F • eff , extending the small-step operational semantics to track the introduction and elimination of linear values, and prove that all linear values are used exactly once during evaluation.
We first extend the syntax of values with values marked with linear tags • to indicate linear values during evaluation.The typing rules simply ignore the linear tags.

Values
: We restrict attention to closed computations and define two auxiliary functions lin( ) and tag( ) for closed values as follows.
The predicate lin( ) holds when is a genuine linear value as opposed to an unlimited value that has been upcast to be linear by subkinding.The operation tag( ) tags a value as linear if it is and has not been tagged, and yields a pair of the possibly tagged and a multiset containing the value if it is newly tagged and nothing otherwise.The linearity-aware semantics is given in Figure 7.We augment the previous reduction relation with two multi-sets

S T
, where S contains the linear values introduced by this reduction step, and T contains the linear values eliminated by this reduction step.Note that in F • eff , we cannot duplicate or discard a value before we bind it.We introduce linear values at the first time they are bound to variables (L A , L S , L R and L O ).Take L A for example.When is a non-tagged real linear value (the first case of tag( )), we tag it and add it to the multiset of introduced linear values.Otherwise, is either not really linear or has been tagged already (which implies that we have already introduced it).We do not need to update the multisets.We eliminate linear values when they are destructed (L R ).As we only have term abstraction and type abstraction as value constructors, the tag-removing contexts F capture the elimination of these two cases.It is easy to extend the linearity-aware semantics with other value constructors.
We write ℒ( ), ℒ( ), ℒ(E) and ℒ(F ) for the multisets of tagged linear values within , , E, and F , respectively.They are given by the homomorphic extension of the following equation.
We define the notion of linear safety similarly to Theorem 3.4.A term is linear safe if there are no tagged linear values in terms that are claimed to be unlimited.Definition 3.5 (Linear safety).A well-typed computation or value is linear safe if and only if: Evaluation contexts (1) For every value subterm of the form (3) For every handler subterm , ℒ( ) = ∅.
Finally, the following theorem states that linear safety is preserved by evaluation, and tagged linear values are not duplicated or discarded during evaluation.
The proof can be found in Appendix A.4.

CONTROL FLOW LINEARITY IN LINKS
In this section we describe our implementation of control flow linearity tracking in L .The implementation fixes a long-standing type soundness bug in L arising from the interaction between session types and effect handlers, as we described in the introduction.
L is an ML-style language with type inference, linearly typed session types (based on F • [Lindley and Morris 2017]), and a row-based effect type system [Hillerström and Lindley 2016].In L we write Unl for • and Any for •.The latter is Any as any value can be soundly used once.The subkinding relation ⊢ Type • ≤ Type • (Unl ≤ Any) allows type variables of kind Any to be unified with types of either kind.This allows us to write functions that may accept both linear and nonlinear values, e.g. the identity function fun id(x){x} : (a::Any) -> (a::Any).Here, we can instantiate the type variable a to a linear type, such as !Int.End, or an unlimited type, such as Int.
To make type inference deterministic, L makes use of two different keywords for defining unlimited functions and linear functions, which are fun and linfun respectively.For instance, we can define a channel version of the function faithfulWrite in Section 2.1 as follows.The inferred type is (!(a::Any).End) -> (a::Any) ~@ ().The faithfulSend function takes a polymorphic channel c and returns a linear function (indicated by ~@ instead of the usual arrow ~>) that sends a polymorphic value over the channel c.If we wanted to we could restrict the inferred type of the channel c and the input by supplying a type annotation to either.
To track control flow linearity we repurpose the existing effect system and add two new control flow kinds Any (for •) and Lin (for •) to signify whether a given context allows control flow to be unlimited or linear.We further add a new effectful operation space for control-flow-linear operations, which is syntactically denoted by the arrow =@, in addition to the existing operation space denoted by =>.The subkinding relation ⊢ Row • ≤ Row • (Lin ≤ Any) is implemented by allowing row variables of kind Any to be unified with both control-flow-linear and unlimited operations and other row variables of arbitrary kinds.In contrast, row variables of kind Lin can only be unified with control-flow-linear operations and row variables of kind Lin.The change from Unl to Lin is consistent with the duality between value linearity and control flow linearity.
Since L is a practical programming language, sequencing is often implicit.Instead of writing linearity annotations on all sequencing, we assume that control flow linearity is unlimited by default, and introduce the keyword xlin to switch the control flow linearity to linear.We also add the construct lindo to invoke control-flow-linear operations in addition to the existing do for control-flow-unlimited operations.To illustrate the use of these extensions, let us consider a channel version of the function dubiousWrite ✓ from Section 2.2.sig dubiousSend : (!String.End) {Choose:() =@ Bool| _ ::Lin}~> () The dubiousSend takes a channel c, non-deterministically sends "A" or "B" through it depending on the result of the operation Choose, and closes the remaining channel.We use xlin to switch the control flow linearity to linear so that we can use the linear channel c and must use the controlflow-linear operation Choose:() =@ Bool with the keyword lindo.If we replace lindo with do then L correctly rejects the code as the continuation captures the linear endpoint c.The example from the introduction will be rejected for the same reason.For linear effect handlers, we use the linear arrow syntax =@ to bind linear continuations of control-flow-linear operations.fun(c) {handle ({xlin; dubiousSend(c)}) {case <Choose =@ r> -> xlin; r(true)} } Here, we interpret the operation Choose as true.The use of xlin in the Choose-clause is necessary because the reified continuation is linear.As the continuation is used linearly, L correctly accepts this program.
Our implementation works well with previous programs using the effect handler feature in L and fixes the type soundness bug.However, being based on F • , L suffers from the limitations outlined in Section 2. In the next section, we present a considerably more expressive calculus, Q • eff , which uses qualified types for both linearity and effects, enabling a much more fine-grained analysis of control flow linearity, and avoiding the need to distinguish between linear and nonlinear variants of term syntax.

AN IMPLICIT CALCULUS WITH QUALIFIED TYPES
In this section, we propose Q • eff , an ML-style calculus which enhances F • eff (and its implementation in L ) in two directions: minimising syntactic overheads and improving accuracy of control flow linearity tracking.The core idea is to use qualified types for both linear types and effect types.The qualified linear type system is inspired by [Morris 2016], which eliminates the linearity annotations on terms and supports principal types.The qualified effect system is inspired by the row containment predicate of R [Morris and McKinna 2019] and the subtyping-based effect system of E [Karachalias et al. 2020;Pretnar 2014], which allows non-trivial subtyping constraints between row variables.

Syntax
Figure 8 shows the syntax of qualified types of Q • eff .We name some syntactic categories for defining meta functions.The remaining syntax is given in full in Appendix B.1, which is mostly identical to that of F • eff , except that we introduce generalising let-bindings let = in to replace explicit type abstraction and implicit instantiation in place of type application and remove all type annotations and linearity annotations.Linearity.In addition to concrete linearities • and •, Q • eff has linearity variables .This is essential to have principal types and more expressive constraints.For example, the identity function .returncan be given the principal type ∀ .→ !{ }, which can be instantiated to either a linear function (by instantiating to •) or an unlimited function (by instantiating to •).
Qualified types.The syntactic category includes value types, row types, and linearity types.Qualified types restrict value types by predicates.The linearity predicate 1 2 means the linearity of 1 is less than 2 (e.g., • •).Note that we allow directly using value types and row types in the linearity predicates, since every value type has its value linearity, and every effect row type has its control flow linearity.The row predicates 1 2 means 1 is a sub-row of 2 , and ⊥ L means does not contain labels in L.
Kinding.For conciseness we omit kinds and infer the kind of a type variable from its name.As usual, we let range over value types, range over row types, and range over linearity types.We also let range over all of them in the definition of type schemes ∀ . .All rows are assumed to be well-formed (no duplicated labels).To simplify type inference, the predicate ⊥ L will be used in place of kinds Row L to track labels that may not occur in rows.This is just a convenience, though, as the corresponding kinds of row type variables can be computed from the inferred types.

Typing
Figure 9 gives representative syntax-directed typing rules for Q • eff ; the remaining rules are given in full in Appendix B.2.The judgement | Γ ⊢ : states that, under predicate assumptions and typing assumptions Γ, the term has type , and similarly for the judgements for values and handlers.As usual for qualified type systems, the typing rules depend on an entailment relation ⊢ (and an auxiliary relation ⊢ Γ ), discussed in the following section.Rule Q L demonstrates the treatment of linearity in Q • eff .We divide the context in three: Γ 1 is used exclusive in the bound term , Γ 2 is used exclusively in the body , and Γ is used in both (and so its types must be unlimited).
Rule Q D demonstrates the use of constraints in Q • eff to generalise subtyping between effect rows.It states that if is a value of type ℓ , then do ℓ has result type ℓ and effect row .We assume that the parameter and result types of operations are given by an implicit global context We again rely on entailment: the constraints must be sufficient to show that the singleton row {ℓ : ℓ ։ ℓ } is contained within .Rule Q S demonstrates the remaining novelty of qualified types in Q • eff .Several of its uses of entailment follow the previous patterns.The bindings in Γ are available in both and , so ⊢ Γ • requires that their types be unlimited.We want flexibility in combining the effects in and , so the conditions ⊢ assure that the effects of each are included in the effects of the entire computation.This allows us to avoid having to unify row types in examples like sandwichClose (Section 2.4) which causes inaccuracy for tracking control flow linearity.Finally, is in the continuation of all operations in , so the value linearity of types in Γ 2 must be less than the control flow linearity of operations in 1 .Note that the two kinding judgements in T S in Figure 4 are now combined into one entailment judgement ⊢ Γ 2 1 .The duality we have identified between value linearity and control flow linearity is reflected by the fact that value types appear on the left of and effect row types appear on the right.
Rule Q H uses the lacking predicate ⊢ 1 ⊥ {ℓ } to ensure that the handled operations are not in the remaining part of the input effect row 1 , and requires 1 to be a sub-row of the output effect row 2 .This is used to allow the handled operations ℓ to appear in 2 .

Entailment
Figure 10 defines the entailment relations between predicates ⊢ .It also defines an auxiliary entailment relation ⊢ Γ which compares the linearity of all variables in Γ and .These two entailment relations are both defined as the conjunction of sub-relations as indicated by P P S and P C .For ⊢ , we only need to use entailment relations of the form ⊢ .The P S is standard.The linearity predicate is reflexive (P R ), with • as top (P L ) and • as bottom (P U ) elements.The two-way rules P F and P R define the linearity of functions and rows.We make use of the fact that in the linearity predicates generated by typing rules, functions only appear on the left, and rows only appear on the right.Here we do not include entailment rules for base types, but in practice we would have axioms like ⊢ • File.For row predicates, we write set( ) for the set of all elements (comprising operation labels with their signatures and row variables) of , and dom( ) for the set of all labels of .We define the row predicates directly by set operations (P S and P L ).The entailment relation ⊢ Γ is defined using ⊢ which compares the linearity of a type scheme and a type .Our treatment of the linearity of type schemes is novel, and addresses a soundness bug in .The rule P which characterises the linearity of polymorphic types may be surprising.It states that the linearity of a polymorphic type ∀ . is less than if there exists an instantiation of it whose linearity is less than .This is because the linearity of a polymorphic type should capture the linearity of values that inhabit that type.A value of a polymorphic type can be understood as the intersection of values of all possible instantiations of the type.If one of these instantiation gives a type that is less linear than , then the value itself must be less linear than no matter what other instantiations are.For example, consider the identity function id = .returnwhich is obviously unlimited.We give id a polymorphic type ∀ . → !{ } to make it possible to use it as both a linear function (by instantiating to •) and an unlimited function (by instantiating to •).Thus, we have expressive principal types for id without adding subtyping between linearity types to the type system.
The rule P may also be surprising.To compare the linearity of a qualified type ⇒ with , we require the predicate to hold and then compare the linearity of the remaining part with .At first glance, the condition ⊢ may seem unnecessary: if must hold in instantiations of this type, surely we can assume it in checking the type's linearity.However, particularly in local definitions, predicates may mention type variables not quantified in those schemes.We do not want to assume anything about the instantiation of those variables.Consider the following function.
.let = (). in return ( , ) The polymorphic function can be given the principal type = ∀ .( ) ⇒ () → !{ } where is the type of .Note that the constraint mentions , which is bound outside this type scheme.Then, since is duplicated in return ( , ), the typing of it collects the constraint •.

Obviously, we want to know from
• that should be unlimited since is also duplicated.One possible derivation of ⊢ • is shown as follows.

P
In P we instantiate and with variables ′ and ′ .In order to prove • from , we must then prove ′ and ′ •.Note that ′ and ′ are not fresh, but should instead appear in , e.g., we might have = { ′ , ′ •}.If we instead assumed , or removed the condition entirely from P , then would not need to restrict at all.We could later instantiate with a linear type, say File, and use this term to unsoundly copy file handles.
Readers may worry that the P rule is as general as it could be, because it always requires ⊢ .For example, consider let = in where : does not appear freely in .We collect the constraint •. Constraints of that are captured in do not necessarily need to be satisfied, because is not used.However, we believe that binding unsatisfiable values has little benefits and can hide potential bugs in practice.
Note that these entailment rules are intentionally made as simple as possible.For example, we do not include any transitivity rules.The entailment rules also do not check potentially conflicted predicates in predicate sets since the rule P S allows collecting any predicates.We say that predicate set is satisfiable if there exists a substitution such that • ⊢ , and define the solutions of it as Transitivity of is admissible when considering the solutions of predicates, e.g., In Section 5.6, we will give an algorithm to check the satisfiability of constraint sets.

Type Inference
Figure 11 shows representative type inference rules for Q • eff ; the remainder are given in full in Appendix B.3.Our type inference algorithm is based on Algorithm W [Damas and Milner 1982] extended for qualified types [Jones 1994].In Γ ⊢ : ⊣ , , Σ, the input includes the current context Γ and value , and the output includes the inferred type , substitution , predicate set , and variable set Σ of used term variables.Note that the predicates are an output of inference, not an input; rather than checking entailment, as the syntax-directed type rules do, we will emit a constraint set sufficient to guarantee typing.In the next section, we discuss our algorithm to guarantee that inferred constraint sets are not unsatisfiable.As usual, the substitution has been already applied to and .
Rule Q L W demonstrates the treatment of linearity.We write Γ| Σ for the type context generated by restricting Γ to variables in Σ.We begin by inferring types for and .Variable sets Σ 1 and Σ 2 capture those variables used in each; any variable in Σ 1 ∪ Σ 2 must be unlimited.We also account for the possibility that the variable may not be used in -that is to say, that it may appear in Σ c 2 , the complement of the used variables Σ 2 .We generate the corresponding unlimitedness constraints using the auxiliary function factorise, discussed next.Rule Q D W emits the constraint that the singleton effect row be included in the output row.Rule Q S W combines these techniques.
We prove soundness and completeness of type inference with respect to the syntax-directed type system.We write | Γ for the substitution generated by restricting the domain of to the free variables in Γ and . The same applies to computation and handler typing.The proofs can be found in Appendix C.3 and depend on the correctness of factorise, discussed next.Note that we do not need to incorporate the subtyping relation into the statement of the completeness theorem because we only have subtyping between row types and do not allow implicit subsumption (unlike traditional subtyping systems).

Factoring Predicates
) for some fresh factorise The factorise function is defined in Figure 12; it factors constraints into simpler predicates following the entailment rules in Figure 10.We use to represent rows consisting of only operation labels.
The only surprising case is for (∀ .) . Rule P requires that we find some instance such that [ ′ / ] . Rather than search for such an instance, we simply pick a fresh type variable .As a result, our type inference algorithm is likely to produce ambiguous type schemes, in which quantified type variables appear only in predicates.Such type schemes are typically rejected [Jones 1994], as the meaning of ambiguously typed terms is undefined.However, as our linearity predicates do not have any intrinsic semantics, but only constrain the use of terms, we do not believe these constraints lead to semantic ambiguity.One interesting property of factorise is that the linearity predicates in its results are only between value type variables , row type variables , and linearity types .
We prove the correctness of factorise with respect to the entailment rules in Figure 10.The proof can be found in Appendix C.1.

Constraint Solving
Finally, we must check that inferred constraint sets are satisfiable; we do not want to conclude that a program is well-typed, but only under the assumption that a linear type is unlimited.We define a constraint solving algorithm solve( ) for checking the satisfiability of the predicate set , inspired by solving algorithms for general subtyping constraints [Pottier 1998[Pottier , 2001;;Pretnar 2014].The tricky part compared to solving usual subtyping constraints is that we need to carefully deal with the interaction between row subtyping constraints and linearity constraints.For instance, 1 2 and 2 actually implies 1 .To resolve the interaction, the algorithm proceeds by first transforming row subtyping constraints to those of the forms , so that we can always simply instantiate on the left to the empty row • for which • always holds.Then, the algorithm computes the transitive closure of linearity constraints and rejects • •.The full algorithm is given in Appendix B.4.We have the following theorem on the correctness of the constraint solving algorithm, in which we write for the substitution set { ′ | ′ ∈ }.
T 5.4 (C ).For any constraint set generated by the type inference of Q • eff , solve( ) always terminates.• If it fails, then is not satisfiable.
The proof can be found in Appendix C.4, whose main idea is to show that every step of the algorithm preserves solutions, and the output predicate set has one solution.
We leave the design of constraint simplification algorithms as practice concerns.Some existing algorithms on simplifying general subtyping constraints [Pottier 1998[Pottier , 2001] ] are promising.

SHALLOW HANDLERS
Up to now we have concentrated on deep effect handlers, which wrap the original handler around the body of captured continuations.Given this automatic reuse of the handler, the handler itself cannot capture any linear resources.In contrast, shallow handlers [Hillerström and Lindley 2018;Kammar et al. 2013] do not wrap the original handler around the body of captured continuations, which means shallow handlers can capture linear resources and thus influence control flow linearity.

Let us first consider shallow handlers in F •
eff .We write † for a shallow handler.The only difference in the operational semantics is the new E O † rule for handling with shallow handlers.
Unlike in E O , the body of the continuation is not handled by † .Whereas deep handlers perform a fold over a computation trees shallow handlers perform a case-split.As such, we know that exactly one operation clause or the return clause will be invoked, and providing all allowed operations are linear each clause may capture the same linear resources.The typing rule is as follows.
T S H Instead of requiring value linearity of Γ to be unlimited as in the deep handler rule T H , we require the value linearity of Γ to coincide with the control flow linearity of , the effect row of the unhandled operations.The is because the shallow handler may be captured as part of the continuations of these unhandled operations in outer handlers.Concretely, when = •, the shallow handler may use linear variables from the context, and unhandled operations are control flow linear; when = •, the shallow handler cannot use any linear variables from the context, and we have no restriction on the control flow linearity of unhandled operations.
We can also easily extend Q • eff with shallow handlers.
, we have ⊢ Γ 1 , which restricts the value linearity of the type context to be less than the control flow linearity of unhandled operations in 1 .
Shallow handlers are typically used together with recursive functions to implement more general recursive behaviours than the structural recursion of deep handlers.It is straightforward to extend F • eff and Q • eff with recursive functions.Obviously recursive functions are themselves unlimited so cannot capture linear resources, but that does not preclude explicitly threading a linear resource through a recursive function that installs a shallow handler.In addition to our two new typing rules, shallow handlers can actually introduce more challenges to tracking control flow linearity, especially because shallow handlers do not handle all invocations of the same operation uniformly.As a result, we can give different control flow linearity to them.We defer a full analysis of the interaction between shallow handlers and control flow linearity to future work.

RELATED WORK
In this section, we discuss related work on linear types, effect types, multi-shot continuations, and effect handlers.
Linear resources and control effects.Exception handlers with finally clauses are a common way of managing linear resources.Exception handlers provide a form of unwind protection, which enables the programmer to supply the logic to release acquired resources in the finally clause, which gets executed irrespective of whether a fault occurs.Similarly, the defer statement in Go [Donovan and Kernighan 2015] defers the execution of its operand until the defining function returns either successfully or via a fault.Thus the programmer can conveniently acquire a particular resource and include the deferred logic for releasing it on the next line of code.Another variation on is automatic resource block management as in the C++ RAII idiom [Combette and Munch-Maccagnoni 2018] and Java's try-with-resource [Gosling et al. 2023], both of which offer a means for automatically acquiring and releasing resources in the static scope.In Scheme the fundamental resource protection mechanism is the procedure dynamic-wind [Friedman and Haynes 1985].It is a generalisation of unwind protection intended to be used in the presence of first-class control, where control may enter and leave the same computation multiple times.It takes three functional arguments: the first is the resource acquisition procedure, which gets applied when control enters dynamic-wind; the second is the main computation, which may use the acquired resources; and the third is the resource release procedure, which is applied when control is about to leave dynamic-wind.Leijen and Brachthäuser [2018] present a constraint system based on qualified types for programming with multi-shot effect handlers and linear resources in K . They use these constraints to mark some effects as linear.However, they do not include a linear type system and instead rely on predeclaring the linearity of operations and a syntactic check to ensure that resumptions are not invoked more than once.
Structural types and control effects.Tov and Pucella [2011b] propose a calculus URAL ( ) which extends the substructural -calculus URAL [Ahmed et al. 2005] with abstract control effects given by a set of effects, a pure effect, and an effect-sequencing operator.They show how to instantiate URAL ( ) with concrete control effects including exceptions and shift/reset [Danvy and Filinski 1990] separately.Similar to F • eff and Q • eff , the URAL ( ) calculus also uses type-and-effect system to check that control effects do not violate the substructural usage guarantees for values.It includes a judgement on effect types to determine whether control effects may discard or duplicate their continuations, which roughly corresponds to our notion of control flow linearity.The main difference between our work and URAL ( ) is that we consider the tracking of control flow linearity in the presence of algebraic effects and effect handlers, which are more involved than exceptions and shift/reset both statically and dynamically.While it is theoretically possible to instantiate URAL ( ) to effect handlers, this task is itself highly non-trivial due to the richer effect systems of effect handlers.Conversely, we can also easily encode exceptions and shift/reset as user-defined effects in F • eff and Q • eff using effect handlers [Forster et al. 2019;Piróg et al. 2019].Linear type systems.Type inference with linear types is a well-studied area.Mazurak et al. [2010] propose using kinds to track linearity, using subkinding to enable polymorphism over linearities.Tov and Pucella [2011a] develop an expanded approach to tracking structural restrictions in kinds; among other differences they introduce subtyping for function types and require fewer explicit linearity annotations than Mazurak et al.. Gan et al. [2014] use qualified types to characterise types that admit structural rules in a substructural type system: for example, in a linear type system, unlimited types are exactly types that support operations dup : → ( , ) and drop : → ().Morris [2016] extends the approach of Tov and Pucella to generalise the treatment of function types, introducing the linearity ordering constraint ; he also generalises their description of unlimited types to type schemes, but does so unsoundly.In contrast, the current work does not interpret unlimited types via operations like dup and drop; we also avoid Morris's unsoundness in the treatment of type schemes.An alternative approach tracks linearity exclusively in function types, rather than in kinds.This approach is developed by Ghica and Smith [2014], McBride [2016], and Atkey [2018], and has been implemented in Idris [Brady 2021] and an extension to the GHC Haskell compiler [Bernardy et al. 2018].
Row-based effect types.Row types and row polymorphism are a popular way of implementing effect systems in programming languages.L [Hillerström and Lindley 2016] adopts Rémy style row polymorphism [Rémy 1994], where the row types are able to represent the absence of labels and each label is restricted to appear at most once.K [Leijen 2017] and F [Lindley et al. 2017] use row polymorphism based on scoped labels [Leijen 2005] which allows duplicated labels.We believe the idea of tracking control flow linearity in F • eff should work well with all kinds of different row-based effect systems.
Subtyping-based effect types.Some versions of E [Bauer and Pretnar 2013;Pretnar 2014] use an effect system based on subtyping.Karachalias et al. [2020] describe an explicit target calculus E E with a subtyping-based effect system and a type inference algorithm that elaborates E source code into it.E uses a row-like representation of effect types and defines a subtyping relation for effect types similar to the that of Q • eff .One difference is that E incorporates full subtyping relations between all types and implicit subsumption, whereas we only introduce subtyping between row types and allow explicit subsumption in necessary positions (like Q S and Q H ).In this respect our qualified effect system is more lightweight.Algebraic subtyping [Dolan 2016;Dolan and Mycroft 2017] combines subtyping and parametric polymorphism with elegant principal types.It would be interesting to explore the possibility of combining linear types and effect types based on algebraic subtyping with control flow linearity.
One-shot effect handlers.OCaml 5 [Sivaramakrishnan et al. 2021], the C++-effects library [Ghica et al. 2022], and the typed continuations proposal for adding effect handlers to WebAssembly [Hillerström et al. 2022] all implement dynamically-checked one-shot effect handlers.Continuations captured by such effect handlers can be thought of as linear resources themselves, and thus play nicely with other linear resources.Any attempt to invoke a continuation more than once throws a runtime error.In contrast, our type systems can be used to statically ensure that handlers are one-shot.In fact, its considerably easier to build a system that ensures that all handlers are uniformly oneshot than a system like ours that supports both one-shot and multi-shot handlers, as in the former case there is no need to track the use of linear resources specially.Another advantage of one-shot continuations is that they admit efficient implementations which are compatible with linear resources, as a one-shot continuation need not copy its underlying stack [Bruggeman et al. 1996].Hillerström et al. [2023] present a substructural type system for a calculus with effect handlers based on dual intuitionistic linear logic [Barber 1996] which restricts all effect handlers to be oneshot (actually one-or zero-shot).They use it to show an asymptotic performance gap between one-shot and multi-shot effect handlers, but are not concerned with linear resources other than continuations.[Biernacki et al. 2019] are research programming languages with multi-shot handlers.In contrast to one-shot handlers, multi-shot handlers can invoke the captured continuations an arbitrary number of times.This enables a range of interesting applications.For instance, asymptotic efficient backtracking search [Hillerström et al. 2020b], nondeterminism [Kammar et al. 2013], and UNIX fork-style concurrency [Hillerström 2022] can all be given a direct semantics in terms of multi-shot handlers.However, one obstacle is that the aforementioned languages cannot statically optimise uses of one-shot continuations, as they must conservatively expect the ambient context to have nonlinear control flow, and as a consequence they must copy the continuation a priori [Hillerström et al. 2016].Our type systems can enable static optimisation of one-shot continuations as they statically distinguish linear from nonlinear contexts.

CONCLUSION AND FUTURE WORK
We have explored the interplay between effect handlers and linear types.We have demonstrated that in order to soundly combine potentially non-linear effect handlers with linear types, it is necessary to add a mechanism for tracking control flow linearity too.We incorporated control flow linearity into two quite different core languages as well as realising control flow linearity in L .Directions for future work include: implementing a programming language based on Q • eff ; developing more precise type systems for combining control flow linearity with shallow handlers; combining control flow linearity with other forms of effect type systems, such as those that support generative effects, duplicate effects, capabilities, and modal effect types; adapting the constraints of Q • eff to algebraic subtyping [Dolan and Mycroft 2017]; and adapting control flow linearity for uniqueness types and for quantitive type theory [Atkey 2018;McBride 2016].

A PROOFS OF F • eff
In this section, we prove the theorems in Section 3.

P
. Directly follows from the typing rules.
T 3.2 (P ).If ⊢ : !, then either there exists such that or is in a normal form with respect to .

P
. By induction on the typing derivation ⊢ : ! . .In a normal form with respect to .Case T D .In a normal form with respect to .Case By a case analysis on .Subcase = (return ) .Reduced by E S .Subcase Otherwise.By the IH, if , then the original term is reduced by E L .Otherwise, is in a normal form with respect to , which implies the original term is also in a normal form with respect to .Case By a case analysis on .Subcase = (return ) ′ .Reduced by E R .
, then the original term is reduced by E L .Otherwise, is in a normal form with respect to ′ .By Definition 3.1, = E [(do ℓ ) ′′ ] for ℓ ∈ ′ and ℓ ∉ bl(E).By the last subcase, ℓ is also not handled by .Thus, the original term is also in a normal form with respect to .

P
. We apply various structural lemmas like weakening, permutation of contexts, and properties of context splitting in the following proofs.1. Preservation of kinds under type substitution.Straightforward induction on the kinding derivations.2. Preservation of types under type substitution.By Lemma A.2.1 and straightforward mutual induction on the typing derivations.3. Preservation of types under value substitution.By mutual induction on the typing derivations.

P
. By the definition of the context splitting relation and straightforward induction on typing derivations.

P
. Case analysis on the linearity of .Case ⊢ : •.We have ′ = .By the linear safety of , we have ℒ( ) = ∅.The linear safety of [ ′ / ] follows from the linear safety of .Case : •.By Theorem 3.4, does not appear in unlimited values, continuations and handlers of .Thus, ′ does not appear in unlimited values, continuations and handlers of [ ′ / ].The linear safety of [ ′ / ] then directly follows from the linear safety of and .

P
. We proceed by induction on the linearity-aware reduction rules defined in Figure 7.To avoid name conflicts, we consider ˆ S T ˆ .
The linear safety of ˆ directly follows from the linear safety of ˆ .We have The linear safety of ˆ gives the linear safety of and .The linear safety of ˆ follows from Lemma A.4. Suppose ⊢ : .Our goal follows from a case analysis on the linearity of similar to the L A case. • for some 1 , we have • = ( , ∅).By a case analysis on the linearity of → .

B.1 Full Syntax
The full syntax of Q • eff is given in Figure 13.Note that we introduce the syntactic category of concrete rows to simplify the presentation of the constraint solving algorithm.

B.2 Full Typing Rules
The full syntax-directed typing rules for Q • eff is given in Figure 14.Note that in the qualified effect system of Q • eff , we only have subtyping between row types and use them in Q D , Q S , Q H , and Q H .This is different from other type systems with general subtyping, where the subtyping relation is used everywhere.For example, in the Q A rule, we require the argument type to be equal to the parameter type of the function, instead of requiring a subtyping relation.Having a full subtyping relation between any types does not help improve the accuracy of tracking control flow linearity; subtyping between effect rows is enough.

B.3 Type Inference Algorithm
The full type inference of Q • eff is given in Figure 16.It uses the unification relations ∼ ′ : which states that is the principal unifier of types and ′ , and ∼ ′ : which states that is the principal unifier for computation types and ′ .The unification relations are directly defined by the unification function.
Figure 15 gives unification function unify( ) which takes a set of unification predicates and returns the principal unifiers of them.It is relatively standard [Martelli and Montanari 1982].The arrow ⇀ indicates a meta function that might fail.Following Leijen [2008] we explicitly indicate the successful return of a result by return.The auxiliary functions urow and ulin are given and explained in .The unification predicates and predicate sets are defined as follows.
Unification predicates UPred ∋ ::= ∼ ′ | ∼ ′ Unification sets USet ∋ ::= , Note that it is possible to postpone the solving of unification constraints to the constraint solving algorithm.We opt for this mixed style presentation for Q • eff in order to keep close to the original presentation of qualified types [Jones 1994], and to keep the constraint set cleaner.

B.4 Constraint Solving Algorithm
The constraint solving algorithm of Q • eff is given in Figure 17.The function ulin unifies two linearity types.The function ulab unifies the signatures of shared labels of two concrete rows.The function urow wraps ulab.The function trlin computes the transitive closure of linearity constraints.
The function srow( , , ) solves row constraints.It takes the current substitution and the currently solved predicate set , and solves the predicates in .The basic idea is to transform the row subtyping predicates to forms of and row lacking predicates to forms of ⊥ L, which we call solved forms.It does a case analysis on the first predicate in .For instance, consider the most complicated case 1 ; 1 2 ; 2 .It first unifies the common labels of 1 and 2 .When 1 is a subset of 2 , we can directly transform it to the solved form; otherwise, we allocate a fresh row variable to substitute 2 and transform it to the solved form.Note that we also need to move all previously solved predicates to the unsolved predicate set, because the row variable 2 is substituted, which might turn some predicates in solved forms to unsolved forms.
The main function solve sequentially solves row constraints using srow and linearity constraints using trlin.Note that we use factorise to factorise the output predicate set to transform the linearity constraints into the simplest form (i.e., only between value type variables, row variables, and linearity), which is suitable for computing the transitive closure using trlin.
In this section, we prove the theorems in Section 5.

C.1 Correctness of Factorisation
We first prove some useful properties of the entailment relations.

P
. By straightforward induction on the typing derivations.
Our goal follows from the IH on (1) and Q R .Case Our goal follows from the IH on (1), Q D , Theorem 5.3, and Lemma C.5.
By a similar proof to the Q A case, our goal follows from the IHs on (1) and (2), Theorem 5.3, Lemma C.5 and Lemma C.6.

P
. By straightforward induction on the typing derivation.

P
. By Lemma C.3, the substitution returned by urow( 1 , 2 ) is the principal unifier that unifies the linearity types of the same labels in 1 and 2 , which is a necessary condition for any solution of 1 2 .
T 5.4 (C ).For any constraint set generated by the type inference of Q • eff , solve( ) always terminates.
• Transitivity.If 1 ⊢ 2 and 2 ⊢ 3 , then 1 ⊢ 3 ., we only need to prove that if 1 ⊢ 2 and 2 ⊢ , then 1 ⊢ .By straightforward induction on 2 ⊢ .Closure property.By P P S , we only need to prove that if ⊢ then ⊢ .By straightforward induction on ⊢ .Weakening.By P P S , we only need to prove that if ⊢ then , ′ ⊢ .By straightforward induction on ⊢ .