Effectful Software Contracts

Software contracts empower programmers to describe functional properties of components. When it comes to constraining effects, though, the literature offers only one-off solutions for various effects. It lacks a universal principle. This paper presents the design of an effectful contract system in the context of effect handlers. A key metatheorem shows that contracts cannot unduly interfere with a program's execution. An implementation of this design, along with an evaluation of its generality, demonstrates that the theory can guide practice.

1 CONTRACTS AND EFFECTS: UBIQUITOUS YET IGNORED For many years, functional programming languages have included constructs for expressing and checking higher-order behavioral contracts [Findler and Felleisen 2002;Keil and Thiemann 2015b;Xu 2014;Xu et al. 2009].With such contracts, programmers state function specifications and the language's runtime checks them. 1 Concretely, a contract describes the promises a library makes about exported values, and the expectations it imposes on uses [Meyer 1988[Meyer , 1992]].Put differently, contracts represent agreements between modules about values that flow from one to the other.
Although these contract systems deal with a wide range of functional properties, none can systematically express properties concerning effects.For example, a library that parallelizes map computations [Dean and Ghemawat 2008] should enforce-but often does not-that the function argument to map is pure.Similarly, when a module exports a function that mutates a hash table, its interface should promise client modules-but often cannot-that it modifies only the given table .The literature is teeming with ad hoc solutions: affine contracts [Tov and Pucella 2010] to interoperate with substructural type systems; framing contracts [Shinnar 2011] to limit mutation; temporal contracts [Disney et al. 2011] to monitor protocols; authorization contracts [Moore et al. 2016] to enforce access control; size-change contracts [Nguyễn et al. 2019] to guarantee termination; trace contracts [Moy and Felleisen 2023] to check properties across multiple calls.All of these systems use effects in contracts to constrain effects in code.No existing work supplies a unified approach for doing so, however.This paper presents effect-handler contracts, a universal mechanism for expressing and monitoring properties of effectful code (Section 2).Its central contribution is a formal semantics of effectful software contracts (Section 3).The model consists of a language where effectful operations are expressed in terms of effect requests and handlers [Plotkin and Pretnar 2009], not as primitive operations; in the context of such a language, effect-handler contracts suffice to check a broad class of constraints.An extension to the model (Section 4) formalizes dependent variants of these contract forms.The model is carefully constructed to satisfy an erasure property (Section 5), meaning that contracts cannot interfere with a program's computation, other than signaling an error and stopping the world.It also satisfies blame correctness, meaning contracts correctly identify components serving values that break the contract assertion.
A secondary contribution is an implementation based on this design.The implementation is a standalone language within the Racket ecosystem [Felleisen et al. 2018] that has both effect handlers and effect-handler contracts (Section 6).A thorough literature survey confirms that effecthandler contracts subsume many existing constructs from prior work (Section 7).

EFFECT-HANDLER CONTRACTS, INFORMALLY
This section presents an effect-handler language that extends a functional core with constructs for requesting effects, interpreting effects, and contracts governing effects.While adding ordinary higher-order functional contracts to such a language is straightforward, extending it with contracts on effects requires careful language design.
The first subsection presents the syntax of the model, while the second subsection illustrates the semantics informally, using a series of code snippets that add up to a complete example.

Syntax and Informal Semantics
The model's syntax will be presented in three steps: the untyped by-value -calculus [Plotkin 1975]; an extension with functional contracts [Dimoulas and Felleisen 2011]; and an extension with effect handlers [Plotkin and Pretnar 2009] that also includes syntax for contracts governing effects.coRe The functional coRe language comes with three built-in data types: Booleans, functions, and pairs.They are eliminated by conditionals, application, and projections, respectively.contRacts extends coRe The contRacts extension reinterprets the base data types as contracts.As a contract, true and false accept and reject all values, respectively.Functions, when used as a contract, are predicates that describe flat [Findler and Felleisen 2002] first-order constraints.A contract pair ⟨ 1 , 2 ⟩ checks the first component of a value pair with 1 and the second component with 2 .A function contract 1 −→ 2 protects functions by checking that arguments satisfy 1 and results satisfy 2 .A monitor mon , 1 2 attaches the contract 1 to the value of 2 (the carrier).Labels , , and name the Proc.ACM Program.Lang., Vol. 8, No. POPL, Article 88.Publication date: January 2024.contract-defining, carrier-providing, and carrier-consuming parties, respectively.These labels are used in error messages to blame the party responsible for a violation [Findler and Felleisen 2002].effects extends contRacts The effects extension introduces two new pieces of syntax related to effect handlers: handle and do.Evaluating do requests the effect described by .The evaluation of a request proceeds by searching for the matching handler in the enclosing evaluation context and supplying it with .Handlers come in two flavors: • handle ▷ with ℎ is a main-effect handler.It interprets only effects performed by ordinary code in the body expression using the handler ℎ .• handle ♢ with ℎ is a contract-effect handler.It interprets only effects performed by contractchecking code in the body expression using the handler ℎ .
Note.Two handler forms are needed to eliminate effect interference.If handle ▷ were to interpret effects at the contract level, a contract could use this channel of communication to change the outcome of a program.By interpreting effects at different levels with different handlers, contract code cannot affect the result of a program.Thus, if a flat contract requests an effect via do , it is not interpreted by a handle ▷ form, even if it is the nearest enclosing handler.
Symmetrically, effect-handler contracts also demand two constructs, one per level.Both of these forms monitor a function that may request effects: • 1 ▷ 2 is a main-effect contract.It ensures that effects performed during the application of satisfy 1 and values received from the handler satisfy 2 .• ♢ is a contract-handler contract.It handles, using , effect requests during the application of that occur during the dynamic extent of a contract check.

Examples, Informally
The model suffices to establish essential metaproperties, but illustrating the ideas with such a spartan syntax is too cumbersome.Hence, this section uses ML-like syntactic sugar to present simple examples that illustrate the informal semantics of contracts, effect handlers, and effecthandler contracts.For interesting examples, rather than synthetic ones, see Section 7.1.
Higher-Order Contracts.The RSA cryptographic algorithm is widely used for secure communication [Rivest et al. 1978].Crucially, RSA relies on the difficulty of factoring prime numbers.Here is the sketch of an RSA-key-generating function, using first-class contracts on a higher-order function to describe the primality constraint: The contract on rsa, attached with a colon, tells the reader that rsa is a function that accepts a pair-of-primes-generating thunk and returns a key-pair-generating thunk.Contracts are first-class values and can be defined using let.The • −→ • combinator protects functions by composing an argument contract and a result contract.Furthermore, unlike a type for such a function, contracts can employ user-defined predicates, e.g., is_prime, to check the validity of arguments and results.
If the runtime discovers a contract violation-possibly in a distant client module-an error is signaled identifying the violated contract and blaming the responsible party.Given an invalid p_gen function-say, one that does not generate primes-the contract system identifies the source of the violation like this: > rsa bad_p_gen () rsa: contract violation expected: is_prime given: 4 blaming: bad_p_gen (assuming the contract is correct) Note.Contracts on their own can enforce only safety properties; they do not suffice to establish security properties.Abstractions that enforce security can be built on top of contracts though, as shown in Section 6.3.

Main-Effect Handlers.
A key requirement of RSA is that the generated prime numbers are random.To generate random primes, there must be some way to generate ordinary random numbers.A pseudorandom number generator (PRNG) is a deterministic algorithm for generating numbers with properties similar to truly random numbers.The interface to most PRNGs is effectful: generating a random number causes the PRNG's internal state to change.
In a language with effect handlers, a PRNG function collaborates with an effect handler via effect-request messages to realize state changes [Pretnar 2015 Consider the run_with_prng function.Given a thunk and a random seed, it runs the thunk in a context that makes random-number generation available.To provide this service, run_with_prng applies the thunk inside handle ▷ with prng_h, an effect handler that interprets requests for generating a random number.This handler function takes two arguments: the requested effect and a continuation that reifies the computation between the origin of the effect request and the handler. 2 When thk needs a random number, it applies rand, which in turn, issues a do Gen effect request.The handler of a request packages up the request (Gen) and the delimited continuation to give the handler function.Once the handler function receives these values, it constructs a that, when given the PRNG state, invokes the continuation (kont) with the next random number.The context then applies this function to the PRNG state.If some other effect is requested, the handler propagates the request to an outer handler.Propagation works by applying the continuation to a renewed request.Since do req is not a value, the call-by-value semantics ensures that the request is handled before the continuation is resumed.
As a reminder, effect composition is a key benefit of using an effect-handler-based language instead of a language with primitive effects.Since an effect-handler language expresses effects uniformly, it is straightforward to reinterpret them, too.In particular, a programmer can replace or supplement the default PRNG provided by run_with_prng, without changing the computation (thk) at all.For example, here is a handler that biases random numbers toward extreme values by squaring them: let bias_h req kont = let bias x = if req = Gen then x * x else x in kont (bias (do req)) let run_with_bias thk = handle ▷ thk () with bias_h Assuming the original generator produces reals in [0, 1], this new handler can be composed with the original PRNG to yield a biased generator: run_with_prng ( _.run_with_bias ( _.rand ())) 0 Main-Effect Contracts.In the presence of I/O effects, the contract for rsa does not suffice.A program may accidentally (or intentionally) use a prime-generating function that reveals more information than desired: let bad_p_gen_v2 () = let ⟨p, q⟩ = elided in do (Write "secret.txt"p); ⟨p, q⟩ In this snippet, the prime-generating function writes the secret prime to a file and thus compromises the RSA key.A contract for rsa should prohibit the use of effectful arguments such as bad_p_gen_v2.
With main-effect contracts, expressing this restriction is straightforward: This revised contract is a conjunction; the ⊓ combinator applies each of the two conjuncts, one after another.Consequently, the prime-generating function must satisfy both.While the first conjunct is the original p_gen_c contract, the second one describes a constraint on effects.In this example, is_gen ▷ is_real ensures that effect requests satisfy is_gen, and that the handler passes only values to the continuation if they satisfy is_real.Since is_gen returns true only for Gen, but not Write, a use of bad_p_gen_v2 signals the desired contract violation. 3ain-effect contracts are active only during the dynamic extent of the protected function, and not at any other point.Consider the following handler: This handler intercepts all random-number requests and writes them to the filesystem.In the same way as bad_p_gen_v2, this handler can be used to expose information: run_with_prng ( _.run_with_printer ( _.rsa p_gen)) 0 However, this program does not result in a contract violation even when the contract of the primegenerating function is p_gen_c_v2.When the prime-generating function requests a random number, evaluation moves to the body of the handler printer_h, which is outside the prime generator's dynamic extent.Therefore is_gen ▷ is_real is no longer active when printer_h writes to the filesystem.The above behavior is by design; it is critical for assigning correct blame.Recall that a contract establishes an agreement between a client and server module.According to p_gen_c_v2, the p_gen function is responsible only for ensuring that its code does not perform forbidden effects directly, or indirectly by calling other functions.Client code, including the code that calls p_gen and the code that handles the legitimate effects p_gen performs, is not restricted by this part of the contract.In other words, blaming p_gen_c_v2 would be wrong even though printer_h writes to the filesystem; it would violate the blame correctness property (Section 5.3).
Contract-Effect Handlers.The p_gen_c_v2 contract guarantees that the thunk always receives a real number from the PRNG handler in response to its requests, but gives no assurance that these real numbers are even somewhat random.A PRNG function that always returns 1 2 does not cause an error, but yields a useless prime generator.Statistical tests exist to detect faulty PRNGs [Bassham et al. 2010 Note.This restriction is similar to that of a runner [Ahman and Bauer 2020] where, informally, a handler may invoke the continuation at most once in tail position.Here, the handler must invoke the continuation exactly once in tail position.
Direct access to the delimited continuation would permit tampering with the program's result and would thus allow interference between program code and contract code.For example, a handler could ignore the continuation completely and return an arbitrary value.
Despite its limitations, handle ♢ is still quite useful.Adapting p_gen_c_v2 yields a contract with the desired test: Here, diff_real requests an effect whose purpose is to check whether the latest argument to a function differs from the most recent one.With this contract, and its corresponding effect handler installed, a PRNG that always returns 1 2 signals a contract error.The p_gen_c_v3 example illustrates why contracts themselves may need to perform effects.Moreover, these effects cannot be locally encapsulated within the contract.In this example, state should persist across multiple calls to the prime-generating function.If the state was locally contained to p_gen_c_v3, then subsequent invocations of the prime-generating function would reset the state.This approach would allow more faulty PRNGs to pass the contract.More broadly, locally encapsulated effects do not suffice to express many of the systems described in Section 7.
Contract-Handler Contracts.Suppose, for unit testing, the author of rsa wants to use a predetermined pool of numbers for random generation, instead of a PRNG.As such, it is important that the number of times a program requests a random number does not exceed the length of the pool.Thus, the contract needs to keep track of this information: Like diff_h in the previous example, rem_h is a contract-effect-handler function.It stores the number of values remaining in the random number pool.Instead of being installed directly using handle ♢ , it is installed by pool_c.Specifically, the contract-handler contract ♢ (rem_h k) installs the function rem_h k using handle ♢ .As such, run_with_pool executes thk in a context where the Remaining effect is interpreted by rem_h initialized with the size of the pool.On its own, a contract-handler contract cannot signal a violation.Rather, it supports other contracts that can.Here, that check happens in the ▷ conjunct of pool_c.When the thk requests a random number, has_rem checks if there are still numbers left in the pool.If so, the request is forwarded.Otherwise, an error is raised.
Note.The order of conjuncts in pool_c is relevant.Since has_rem requires that the rem_h handler is installed, it must come earlier in the list of conjuncts than ♢ (rem_h k).The ⊓ combinator applies contracts left-to-right.Thus, the right-most conjunct creates the outermost wrapper.

A FORMAL MODEL OF EFFECT HANDLER CONTRACTS
Defining a semantics amounts to defining an evaluation function that maps programs to answers.Specifying such a function with a reduction relation provides an easy way to prove metatheorems.Following tradition, this section starts with an extension of the model's syntax to an evaluation syntax (Section 3.1).Next, the reduction relation is defined piecemeal across four subsections (Sections 3.2 to 3.5).The reduction rules for contracts differ a bit from conventional definitionsnamely, flat contracts have cascading behavior where, instead of just a Boolean, they can return any arbitrary contract that is then applied.This behavior, and its purpose in the context of effectful contracts, is examined in the last subsection.

Evaluation Syntax
The evaluation syntax extends the grammar of expressions and defines the set of values: effects (eval) extends effects Expressions include marks and errors, which can arise during evaluation but cannot be expressed in written programs.The expression mark , states that effects requested by , and their fulfillment, must satisfy contract .In other words, effect requests "passing through" the mark must satisfy the contract.These marks are installed by main-effect contracts.
Next comes the grammar of evaluation contexts.The reduction relation requires three different kinds of evaluation context, each with a different role: • ▷ is the set of main-executing contexts containing regular code that is handled with handle ▷ .• ♢ is the set of contract-executing contexts describing the dynamic extent of contract code that is handled with handle ♢ .• is the set of unrestricted contexts, which is the union of the (disjoint) sets ▷ and ♢ .Here are the elements of the grammar that are shared between each kind of evaluation context: effects (eval) extends effects the above mutatis mutandis ♢ ∈ Ctx ♢ = the above mutatis mutandis And here are the elements that differ between the evaluation contexts: effects (eval) extends effects Contract code executes in two syntactic positions: in mon , , and ℎ in handle ♢ with ℎ .While the former is clear, the latter might be a surprise.Recall the purpose of handle ♢ : it interprets effect requests that originate in contract code.By implication, ℎ may receive and execute higherorder values originating from contract code.Therefore, it must be considered contract code.
The definition of evaluation contexts reflects this reasoning.In particular, ▷ omits productions of the shape mon , ▷ and handle ♢ with ▷ , while ♢ omits the production for □.This restriction on ♢ ensures that fully formed ♢ contexts contain either mon , or handle ♢ with .Finally, formulating the reduction rules and the evaluator requires two more definitions: effects (eval) extends effects An unhandled evaluation context lacks a handler for any effect requests that may originate from an expression plugged into the hole.The set of stuck expressions describes those to which no reduction rule applies, i.e., they are not in the domain of the reduction relation.Examples are the application of non-functions to values or an effect request in the hole of an unhandled context.Instead of dealing with stuck expressions in the reduction relation, the evaluator is defined to produce a sensible error when the reduction relation (transitively) reduces to a stuck expression.
The next four subsections present the one-step reduction relation for complete programs using the evaluation contexts [Felleisen et al. 2009;Felleisen and Hieb 1992].The evaluator is defined by the reflexive-transitive closure of the union of these relations.expressions is specified via two related rules, prefixed with mon and gRd, respectively.A mon rule checks a first-order property of the carrier.In this model, checking first-order properties amounts to checking whether the top-level shape of the carrier value is the expected one.For example, mon-fun checks whether the carrier is a function.A gRd rule assumes the shape check is satisfied and constructs a wrapper to check either higher-order (as in gRd-fun) or nested properties (as in gRd-paiR).

Contract Reduction Rules
The mon-tRue and mon-false rules immediately succeed and fail, respectively.When the contract is a predicate , mon-flat applies the predicate to the carrier and uses the result as a contract.Since true and false double as contracts, this cascading of checks works as expected.It is possible to return non-Boolean contracts as well; Section 3.6 explains why this matters.
The gRd rules cover values that need "deep" checking.A pair of contracts distributes over a pair of values.A function contract yields a wrapper value that checks the argument and result contract when the wrapper is applied.The blame labels on the argument monitor are swapped since the argument position of a function contract is contravariant [Findler and Felleisen 2002].

handle
[handle Figure 3 presents the reduction rules for effect handlers.When the body of any handler is a value, the effect computation has run its course and the handler is eliminated.Otherwise, one of the do rules may apply.The unhandled side condition in all of these rules ensures that only the innermost handler is matched with an effect request.Both do ▷ and do ♢ use the special evaluation contexts from Section 3.1 to ensure that the requested effect (do ) originates from either main-program code or contract code.The do ▷ rule specifies main-program handlers as deep.Concretely, the handler is applied to the effect request and a delimited continuation that includes the handler itself.The evaluation context ▷ may contain marks deposited by main-effect contracts.Two metafunctions, ↑ and ↓, collect the contracts for main-effect requests and their fulfillment, respectively.Plugging the raw effect request into the context created by ↑ produces an expression that performs all of the necessary contract checks.The same goes for and ↓.
Note that the blame labels flip for ↓.The return value, given to the continuation, comes from a handler, which exists in the context of an effect request.As such, swapping the labels is necessary so that blame assignment points to the party that violated the contract [Dimoulas et al. 2011].
By contrast, the do-paiR ♢ and do-fun ♢ rules specify handlers that have no control over the continuation.Furthermore, two rules are needed to distinguish the two contract cases, analogous to the rules for Boolean contracts and predicate contracts.Specifically, the expression ℎ in handle ♢ with ℎ can reduce to either a function or a pair: • In do-paiR ♢ , the first component is plugged into the evaluation context, which is the continuation of the effect request, and the second component becomes the next handler.• In do-fun ♢ , the function is applied to the effect request with the expectation that this new contract expression eventually reduces to a pair.Like mon-flat, this rule ensures that contract code is always executed in one syntactic position.Finally, Figure 4 presents the reduction rules governing both kinds of effect-handler contract.The mon rules ensure that the carriers are functions; if not, they signal a violation.If the carriers are functions, the contracts act in a higher-order manner via gRd-handle ▷ and gRd-handle ♢ .The gRd-handle ▷ rule simply installs a mark that constrains effects performed in .Actually checking these contracts is delegated to do ▷ .Once the dynamic extent of a mark expression is over, the mark itself can be eliminated via the maRK rule.

Effect-Handler Contract Reduction Rules
The gRd-handle ♢ rule wraps the carrier in a contract-effect handler, where ℎ becomes the handler function.As such, ℎ also becomes contract-checking code.

On the Importance of Cascading Contracts
Flat contracts in this model generalize the ones from the literature to allow cascading.In particular, a flat contract can return any contract, not just a Boolean.Generalizing flat contracts in this manner is highly useful.Take affine contracts [Tov and Pucella 2010] as an example.An affine contract guarantees that a function is called at most once by keeping track of how many times the function has previously been called.It does so with mutable state.Figure 5 shows the code for a contract that allows a function to be called at most times.The run_with_mut_refs function grants contract code the ability to create, read from, and write to mutable references.Accordingly, the −⊸ contract specifies a function −→ that can be called at most times.This property is maintained by allocating a reference containing the remaining number of calls permitted.Each time the function is applied, the number contained inside this reference is decremented.
The −⊸ contract itself is flat; it is not a function contract.When applied to a function, −⊸ ignores its argument (the function itself) and allocates a cell initialized with ; then it returns a function contract.Due to the cascading behavior, this allocation happens exactly once for each carrier whose monitor enforces the "call at most times" constraint.Without cascading, this kind of contract is not expressible [Felleisen 1991] in terms of existing contract forms.

DEPENDENT CONTRACTS
The contract forms considered thus far cannot deal with dependencies.For example, the result part of a function contract might have to depend on the actual argument.
This section extends the model with dependency: both traditional dependent function contracts, written as 1 =⇒ 2 , and new dependent main-effect contracts, written as 1 ▶ 2 .Formally, the syntax is extended as follows: dependent (eval) extends effects (eval) the above mutatis mutandis Dependent contracts have an extensive history in the literature [Blume and McAllester 2006;Findler and Blume 2006;Greenberg et al. 2010].The "indy" semantics, due to Dimoulas et al. [2011], is now accepted as standard: Instead of being a result contract, as in a normal function contract, 2 is a function that produces a result contract when given the argument.In the contractum, 2 is applied not directly to the argument .Doing so would be the "lax" semantics [Findler and Felleisen 2002].For indy, 2 is applied to protected by the argument contract.This is because 2 itself may violate the contract.
To reflect this possiblity, the client blame label on is , the contract-defining party.Otherwise, this rule is the same as gRd-fun.
Note.Moy and Felleisen [2023] observe that, under certain circumstances, dependent function contracts can duplicate effects.They present a solution to this problem that stages contract effects.Since the purpose of this section is to convey the essence of dependent contracts, the model here does not include the complexity of staged contract effects.However, the solution is orthogonal to this formalism and could be readily adopted.

Dependent Main-Effect Contracts
Dependency can also arise in main-effect contracts.Consider a random-number-generating effect Gen k that yields a random integer between 0 and inclusive.Guaranteeing that the random number is within bounds requires dependency: → _.true let rand_c = is_any ▶ is_in_range In this example, is_in_range matches on the effect request itself to determine the greatest valid random number.This number, given the name upper, is used to construct a predicate that ensures the generated number is within bounds.
Formalizing dependent main-effect contracts requires a few adjustments to the original semantics.First, two additional rules are needed to reduce monitors containing dependent main-effect contracts.These are analogous to the ones for ordinary main-effect contracts: Second, the ↓ metafunction must be extended to permit dependencies: With this revision, ↓ has access to the raw effect request .When a mark contains a dependent contract, it must generate the wrapper needed for the effect response.To do so, it applies 2 to , where is the protected effect request.In a lax semantics, = (↑ ) [ ].For indy, must also protect with 1 where the client label is the contract-defining party .
Finally, the do ▷ rule must be adjusted to use the newly adapted metafunction: Here, uses the updated metafunction (highlighted) with the raw effect request supplied.

SEMANTIC PROPERTIES
At this point, defining a partial evaluation function, also known as an evaluator, is straightforward: dependent (pRoof) extends dependent (eval) Programs, i.e. closed expressions, are the input to the evaluator.Answers are the output of the evaluator.If a program reduces to a Boolean, the answer is the same Boolean.All other values yield the opaque token. 4This behavior matches that of most REPLs where function values are printed as an opaque symbol.Two kinds of error can occur during execution: contract errors, which produce err , and language errors,5 which produce err • • .

Well-Definedness
Following convention, the first theorem states two properties that ensure the sanity of the reduction relation.Specifically, eval is a partial function because the reduction relation relates each program to at most one answer.Programs where eval is undefined are exactly those with unbounded reduction sequences.
Theorem 5.1 (Functional Evaluation).Two facts about the evaluator hold: (1) The eval relation is a partial function.
(2) If is a program, then either (i) eval( ) is defined or (ii) the reduction sequence starting with is unbounded.
PRoof.See Appendix B. □

Erasure
The key property of interest for the model is contract erasure.Contracts serve one purpose, namely, to detect violations of specifications.Therefore, the output of a correct program should not depend on the presence or absence of contracts.In short, contracts must not interfere with program execution-other than possibly signaling an error.Non-interference in the presence of effects is critical for modular reasoning [Oliveira et al. 2012].
Stating the erasure theorem requires defining an erasure function ℰ for contract monitors: PRoof.The proof of erasure proceeds by a simulation argument with the following simulation: . ∼ By convention, a metavariable with a tilde such as is in simulation with its plain counterpart .See Appendix C for details.□ Technically, non-termination is the one contract effect that can affect a program's behavior.So long as contracts contain code in a Turing-complete language, this effect is unavoidable.As stated, Theorem 5.2 holds because the antecedent rules out non-terminating contracts.
While the syntax design already clarifies that there are two separate, disjoint levels of effect handling, the proof for Theorem 5.2 confirms this claim: main code cannot be serviced by contracteffect handlers.A small adjustment to the erasure function, defined above as ℰ + , makes it possible to state the claim formally.PRoof.Follows directly from the proof of Theorem 5.2.□ Establishing the erasure theorem is straightforward in a pure setting, yet difficult to achieve in a language with effects.Ensuring erasure means contract code must not interfere with the main program directly or indirectly via effects.A language with effect-handler contracts poses the additional problem of having to grant contract code the right to interact with effects, while also imposing constrains on such interactions.
A physicist may describe the model as being in an "unstable equilibrium;" a programminglanguages researcher may use the word "brittle" and compare the design to Hindley-Milner type inference.Directly put, designing a language semantics that satisfies contract erasure demands balancing expressive power with preventing interference.The model presented here achieves this delicate balance, as the theorem and Section 7 show.Limiting the expressive power any further makes programming inconvenient and would neglect existing use cases.However, experiments adding more power to the model show that many extensions violate erasure.
For example, consider a naive design where the reduction relation for handlers merges the two levels of effect handling: Instead of restricting the evaluation context in the body of the handler, this rule uses the unrestricted context .Such a rule violates contract erasure as the following program demonstrates: handle (mon , (do false) true) with . .
The original program evaluates to false, but erasing the contract yields a variant whose value is true.Similarly, modifying do ▷ to use , or modifying do-fun ♢ to give direct access to the continuation, both result in erasure violations as they produce a rule equivalent to the one above.
Again, the original program evaluates to false, while its erased variant yields true.In short, gRdhandle ♢ cannot be generalized.

Blame Correctness
The final property to consider is blame correctness, that is, whether a failing monitor assigns blame to the component that serves a faulty value.In the context of the model, the do ▷ reduction deserves particular attention.Like the rule for monitoring first-class functions, the reduction for main-effect handling switches the order of blame labels as it pushes the relevant contracts down the handler's continuation ( ↓ ▷ ).The question is-as it was for the original work on higher-order (dependent) function contracts [Findler and Felleisen 2002]-whether this switch is correct.As Dimoulas et al. [2011] show, the answer is a blame correctness theorem.By now, the strategy for proving blame correctness is reasonably standard.The first step is to introduce ownership labels on expressions, values, and evaluation contexts.Intuitively, an expression | | denotes that the owner of is the component labeled .
The second step is to adjust the reduction relation so that ownership changes when a value crosses from one component to another.Crossing may either add or drop a label from a value.The reduction drops a label when the crossing involves a contract check, meaning the value is vetted and "absorbed" by a new host component.A blame label is added when the crossing does not involve a check, meaning the value becomes co-owned by several distinct components.It is critical that the ownership labels do not affect the semantics proper.
The third and final step is to show that when a monitor is about to check a value, the latest ownership label of the value is the same one that the monitor uses to assign blame.PRoof.The proof uses the standard subject-reduction technique [Curry and Feys 1958;Wright and Felleisen 1994] and a consistency judgment for the ownership annotations.The judgment ; Γ ⊢ says that is well-formed if its owner is , given an environment Γ that maps variables to their owners.Importantly, if a program is well-formed under the default owner , then for any monitors it contains, the owner of the carrier matches the server label of the monitor.Subject reduction shows that this consistency is preserved across reduction sequences, and hence, if a monitor check fails, blame is assigned to the correct component.See Appendix D for the full proof.□ The labeled reduction semantics is indeed equivalent to the unlabeled one after erasing ownership labels ( (•)) from the first one.
Note.The stronger complete monitoring property states that all channels of communication between components can be monitored using contracts [Dimoulas et al. 2012].The presented model does not satisfy complete monitoring.As Section 7 explains, the intent of effect-handler contracts is to be a low-level mechanism for implementing other constructs.Complete monitoring is more relevant to prove for these higher-level contract systems, not the low-level target.

EFFECT RACKET
Rapidly moving from a model to a full-fledged programming language calls for (1) a programmable production-level language with (2) linguistic constructs for realizing effect handlers easily and (3) a well-developed higher-order contract system.Racket is such a language [Felleisen et al. 2018;Findler and Felleisen 2002;Flatt and PLT 2010;Flatt et al. 2007].This section presents effect/racket, a language with effect handlers and a full contract system (Section 6.1).Following the precedent of typed/racket, the language is implemented as a library [Tobin-Hochstadt et al. 2011] (Section 6.2).The language implementation validates that the model can be realized.Therefore, it may help guide implementers of other effect-handler languages.

The Language, By Example
This section is organized like Section 2.2, but uses different examples to keep things interesting.
Main-Effect Handlers.As an introductory example, consider implementing ML's first-class mutable references using effect handlers.References come with a ref constructor and two elimination forms: ref-get and ref-set.In effect/racket, each form demands the declaration of a corresponding effect: one for allocating a reference cell, one for getting its value, and yet another for assigning to a cell.Declaring an effect makes the effect name available both for requesting the effect and, within a handler, interpreting the effect.
Figure 6a displays the code for both the effect declarations and the effect handler.The handler, dubbed a service for references, comes with three clauses, one per declared effect; all other effects are propagated automatically.Furthermore, the handler form binds two identifiers to delimited continuations: continue, for resuming in a deep manner; and continue*, for resuming in a shallow manner.Otherwise, the handler uses standard techniques for implementing a store in this setting [Cartwright and Felleisen 1994;Pretnar 2015].
Any language in the Racket ecosystem, including effect/racket, is easily equipped with a readeval-print loop (REPL).By running the effect/racket program, the definitions of effects and services become available for interactive experimentation.Figure 6b shows how to install the handler function using the with form.In the context of this with expression, it is now possible to allocate a numeric reference cell, to increase its value by 1, and then to retrieve this value.
Main-Effect Contracts.Suppose a programmer wishes to write a library function that guarantees a frame condition.To make this concrete, the function guarantees that it manipulates only a specific, given reference cell during the dynamic extent of any call.A good name for this contract would be mutates-only/c, and here is how the library's interface would state that guarantee: The function contract is a standard indy dependent contract [Dimoulas et al. 2011] that governs two arguments-r and f-and promises nothing about its result.The new part is the contract for f, which says that (1) f is a function from a reference cell to any value and (2) it may mutate only r.
The frame contract is a rather straightforward instance of a main-effect contract: (define (mutates-only/c r-ok) (define (effect-ok?e) (match e [(ref-set r _) (equal?r r-ok)] [_ true])) (->e effect-ok?any/c)) The mutates-only/c function takes a reference cell as an argument and returns a main-effect contract that permits only writing to the given cell and no other one.The two-part ->e contract (i.e., • ▷ •) tells a reader that requested effects must satisfy the effect-ok?predicate and that values returned by the handler can be anything.According to effect-ok?, any write effect must be to a reference cell that is equal to r-ok.All other effects are permitted.
Contract-Effect Handlers.Equipped with reference cells, it is now possible to transliterate the affine-function contract from Section 3.6 into running code.Figure 7 shows the implementation of −⊸ as a contract exported from a library.
Since the contract relies on reference cells at the contract level, it is mandatory to lift the service from the main level to the contract level; see Figure 7 (lines 26-34).The contract-handler form does not make the delimited continuation available; instead, each arm must return a pair of values: the value to be supplied to the delimited continuation, and a new handler to be installed around the continuation.
Using ref-contract-service, both −⊸ and unused?can be defined using Racket's existing contract library, with reference effects performed as needed; see Figure 7 (lines 12-24).As in Section 3.6, −⊸ must use cascading to allocate a reference for affine functions at the right time; the presented code realizes this constraint using the self/c combinator, which when protecting a carrier v, applies a function to v and uses the result to protect v-just like flat contracts in the model.Here, the function given to self/c returns the expected function contract.For −⊸, the value v is not needed and is discarded.this lack of abstraction safety and have proposed solutions, especially in typed settings [Biernacki et al. 2017;Brachthäuser et al. 2022;Leijen 2013;Xie et al. 2020;Zhang and Myers 2019].
An alternative design can rectify this problem easily.Racket gives programmers the ability to attach metadata to continuations via continuation marks [Clements and Felleisen 2004;Flatt and Dybvig 2020].To prevent other parties from arbitrarily tampering with this information, the language only permits access to continuation marks via keys.Racket also uses this mechanism to limit how much of the continuation a program can capture and abort [Felleisen 1988;Flatt et al. 2007;Sitaram and Felleisen 1990].These keys are first-class unforgeable values.If a module does not export its key, then no other party can view or update the information associated with that key.To prevent interception, a module can just keep a key internal.Effectively, a key is a capability [Dennis and Van Horn 1966].
Instead of an arbitrary match pattern, a handler could be restricted to explicitly provide a set of "effect keys" that it can interpret.Similarly, main-effect contracts would have to include a set of effect keys instead of an arbitrary predicate over all effect requests.
There is a downside to this approach; it eliminates a useful class of contracts such as those for purity.A contract that guarantees purity must, by definition, be able to interpose on all effects.This includes effects that are kept hidden.Unsurprisingly, there is a trade-off between security and expressiveness.
If desired, though, this kind of restriction can be built on top of effect/racket; the language is flexible and can serve as a foundation upon which other abstractions can be constructed.Thus, language implementors can choose the design that fits their situation.

EVALUATION AND RELATED WORK
The introduction of this paper claims that effect-handler contracts are a universal mechanism.An evaluation of such a claim must show that the model and its full-scale implementation cover all existing work.6Additionally, such related research must be analyzed and systematically compared.As such, this section consists of two pieces: (1) an evaluation of effect-handler contracts with respect to a survey of existing literature; and (2) a summary of each piece of related research and how it compares to this paper.

Analysis
Table 1 presents an overview of the existing literature.It explicates the many overlapping problems that various papers address.Rows correspond to existing pieces of literature, and columns correspond to properties that at least one system can express.
A concise description of these properties follows: allow call A function may be called only during the dynamic extent of another function.exceptions Only specified exceptions may be raised during a function call.This property is the dynamic analogue to Java's checked exceptions.fRaming Mutations are restricted to specified memory locations.ghost state Values are associated with a mutable reference that is used to check conformance with a protocol.must call A function must be called during the dynamic extent of a call to another function.non-ReentRant A function must not call itself recursively.puRe No effects-other than non-termination and error signals-are permitted.
RestRicted effect Effects are restricted at a fine-grained level.teRmination A function call must terminate.Specifically, a call graph keeps track of changes to the size of arguments.union contRacts Given a set of contracts, the protected value satisfies at least one of the given contracts.Checking the union of flat contracts is easy, but checking the union of higherorder contracts relies on state to keep track of violations and assign blame.
Most papers illustrate these properties with a plethora of examples, all of which can be implemented in effect/racket.The cells of the table have the following rough meaning.A • indicates that the presented contract framework supports this property.Note that the number of • entries in a row does not indicate anything about the "power" of the presented system.It merely means that a paper with fewer • entries may focus on a narrower set of properties.Also, these papers differ in other, significant ways that are not communicated by the • markings.
The effect/racket language can faithfully express all but one existing contract.This exception is the computational contract [Scholliers et al. 2015] that prohibits a function from being called during the dynamic extent of a call to another function.Effect-handler contracts can achieve this behavior as long as the excluded function comes with a contract that enables the system to monitor it; if not, this contract is impossible to realize without invasive monitoring techniques.For details, see the next subsection.

Related Work
The Java Modeling Language (JML) [Chalin et al. 2006] is a specification language for stating and verifying properties of objects in Java.It encompasses a broad range of features including assertions, class invariant statements, frame conditions, purity constraints, termination constraints, and ghost state declarations-just to name a few.Property checking takes place in one of two modes: static deductive verification (DV) or dynamic runtime-assertion checking (RAC).Some properties, such as termination, can be checked only using DV.JML differs from higher-order contract systems in three major ways.First, properties are described using a restrictive set of "well-defined" terms, a limitation compared to contracts written with ordinary constructs.Second, JML supports only first-order properties.Finally, JML lacks a blame assignment component, meaning developers are on their own when a contract check fails.Tov and Pucella [2010]'s research on interoperability between a language with a substructural type system and one with a plain structural type system relies on an affinity check for function arguments.Specifically, the boundary employs a run-time check to ensure that a function argument is affine, meaning it can be applied at most once.This check uses a mutable Boolean field associated with each function value, i.e. ghost state, which indicates whether a function has been applied.Dimoulas et al. [2016] also use ghost state.They define a general-purpose DSL that uses ghost state to check protocol conformance.As described in Section 3.6, contract-effect handlers can easily introduce and manipulate ghost state.
For interoperability, a language with a sound gradual type-and-effect system relies on a runtime enforcement mechanism to restrict the effects performed by untyped code.The contracts for such a language are formulated in terms of two operations [Bañados Schwerter et al. 2014]: has (for checking the privileges granted by the current context) and restrict (for restricting the privileges of an expression).In the effect-handler language, these primitives are just main-effect contracts.
Shinnar [2011] takes some of the constructs from JML, in particular framing contracts, and adapts them to Haskell.The implementation uses delimited checkpointing to keep track of state.A delimited checkpoint is a snapshot of memory captured using software transactional memory (STM).Framing contracts can detect and restrict writes to transactional references by comparing memory snapshots.Shinnar proves erasure for a limited model of Haskell with delimited checkpoints.This work is similar to those pieces of research [Findler and Felleisen 2001;Strickland et al. 2012] that consider erasure for only a few restricted effects.Disney et al. [2011]'s higher-order temporal (HOT) contracts and Moy and Felleisen [2023]'s trace contracts check properties of sequences of argument and return values for functions and methods.While the two differ in many respects, from the perspective of effectful software contracts they fall into the same class of extended higher-order contracts.Describing constraints over sequences amounts to a writing a predicate that "folds over" the sequence incrementally, storing intermediate state in a mutable reference.As such, contract-effect handlers can supply the needed mutable references to such contracts.Indeed, Disney et al. [2011] present some examples that are more directly expressed using effect-handler contracts than HOT contracts.For example, their HOT contract for non-reentrancy does not suffice in the presence of control effects, whereas an effect-handler-contract implementation of the same property is robust.Scholliers et al. [2015]'s computational contracts instantiate aspect-oriented programming for the contract world.Critically, such contracts can prohibit or enforce that a function is called in a particular dynamic extent.Due to the intrusiveness of aspect-oriented programming, computational contracts do not require that is aware of the contract.Indeed, without aspect-oriented programming or a similarly invasive mechanism, there is no way to interpose on function applications in a dynamic extent, which is why the effect-handler language cannot fully realize this form of checking.Moore et al. [2016]'s authorization contracts enforce access control with contracts about granted privileges.Specifically, authorization contracts can capture, check, and restore access privileges via an authority environment that records such privileges.Moore et al. [2016]'s model is essentially a variant of contract-handler contracts topped off with a DSL for authorization management.
Effectful contracts alone do not implement any of the security aspects of the system.However, authorization contracts could be built on top of contract-handler contracts given the secure design described in Section 6. 3. Nguyễn et al. [2019] provide a run-time check for termination by monitoring the size-change property (SCP) of functions dynamically.Any diverging function must exhibit an SCP violation, causing a contract violation.They turn this run-time check into a static one, using existing contract verification techniques [Nguyễn et al. 2018].To guarantee termination, they use continuation marks to store size-change information on the stack.Contract-handler contracts can be used to store the same information.
While the literature on higher-order contracts tends to mention intersection and union contracts, implementing these in general is a serious challenge.Indeed, Racket rejects or/c contracts if the disjuncts are not "first-order distinguishable." Several researchers [Freund et al. 2021;Keil and Thiemann 2015a;Williams et al. 2018] have studied this problem, and all come to the conclusion that effects are needed.For example, Williams et al. [2018] use a mutable blame state to keep track of contract violations.A contract-effect handler can be used to implement this blame state.Moreover, erasure guarantees that such an implementation does not have adverse effects on a program's result.This property is critically important because even benign-looking contract effects can have unintended consequences.Such a phenomenon has been observed in practice [Lazarek et al. 2020, Section 6.1].

IGNORED NO LONGER
In the real world, developers use contracts with effects; in papers, researchers study how to employ effects in contracts.What has been lacking is a general framework for combining contracts and effects.As a result, existing extensions solve specific problems and do not generalize.
This paper offers the first general model of effectful software contracts.As such, it synthesizes a model of effect handlers with a model of contracts.In this combination, contracts can check effects, contracts can request effects, and contracts can handle contract-requested effects.Yet, since contracts should not affect the main program-other than signaling violations-the model is designed to avoid interference between contract-level effects and main-level code.Hence, in addition to well-definedness and blame correctness, the model satisfies an erasure theorem.
Beyond theoretical explorations, the formalism also provides guidance for implementation efforts.A fully faithful implementation, effect/racket, exists as a standalone language within the Racket ecosystem.This language demonstrates that the design can be realized.It is an open question how to modify an existing contract system to support all of the model's expressive power in a backwards compatible manner.Still, the theory can serve as a roadmap for others who wish to combine effects and contracts in a principled way.And, as effect handlers go mainstream [Chandrasekaran et al. 2018], the theory may find many more practical uses.
Fig. 6.Mutable References with effect/racket (f r), restores the content of r, and ;; returns the value of r that f stores there.(->i([r reference?][f (r) (and/c (mutates-only/c r) (-> reference?any/c))]) [result any/c])])) Fig. 7. Affine-Function Contracts with effect/racket ]; a contract can employ such tests to detect obviously bad PRNG implementations.Consider the simple-minded test that ensures two consecutive random numbers are different:

Table 1 .
Detailed Comparison Matrix