1 Introduction

In this paper, we study a principled, typeful foundation to represent a relevant class of control effects within a behavioral type system for stateful concurrent programs. Sophisticated structural type systems have shaped mainstream static type checking for a long time now, and are fairly complete tools to discipline and effectively check programs that manipulate pure values. Unfortunately, the same cannot be said for most mainstream programming idioms, which intensively rely on state mutation, sharing, and often concurrency, about which “standard” type systems are quite silent.

Interactive concurrent systems need to manipulate stateful resources, ranging from basic memory references and passive objects (such as files, locks, and communication channels) to dynamic entities (such as threads or web references) typically subject to linearity constraints. To extend type-based verification techniques to this challenging setting, substructural type systems, based on various forms of linearity and affinity, have been increasingly investigated [31, 35, 45,46,47], and start to make their way towards practical adoption. Recent examples include Mozilla’s Rust, but also embeddings of session types in target languages without linear types [32, 41]. Some approaches use types to model states (cf. assertions); examples include typestate and several affine, linear, and stateful type systems, see, e.g., [20, 31, 33]. In other works, types are used to model behaviors (cf. processes); examples in this line include session types and usage types such as, e.g., [27, 28, 34], often referred to as behavioral types [29].

Linear types are in general very expressive, and the fine-grained specifications of usage types that they typically support may simultaneously bring a benefit and a curse. In particular, a still open issue is how to seamlessly combine linearity with many other useful programming mechanisms—a prominent example being the interaction of linearity with non-determinism and control effects, such as exceptions and (linear) continuations. The general issue is that by their very essence control effects (and associated programming language constructs) conflict with the linear, stateful usage discipline of values manipulated by programs, which makes it difficult to statically check programs. For example, when a communication channel is aborted, any linear values held by a channel client continuation must be aborted as well (which may not be always possible), or passed away to some candidate consumer code in scope. Likewise, after an exception is raised, it is not always clear how to safely discard a continuation holding linear values, nor how to proceed after the exception is caught. This situation is already present in non-deterministic programs where subexpressions may return more than one result, or even no result at all (e.g., “fail”), as in the non-deterministic monad.

Fig. 1.
figure 1

Two code snippets.

These challenges and conflicts are illustrated by the examples in Fig. 1, adapted from [46], which we express in an idealized linear functional language. The \( Ref \) function is assumed to return a stateful fresh value r that may either be initially discarded, or subject to a strictly linear protocol consisting of a \( write \;r\;x\) operation followed by a \( free \;r\) operation. We use a common idiom when programming with usage types, in which an operation f acting on a linear value a that needs to be used according to a stateful protocol would be called as \(a' = (f a)\), where \(a'\) refers to the new state of a (which gets “consumed” in the call \((f\;a)\)). So, in our examples, failure to call \( free \;r'\) after \(r'= write \;r\;x\) may result in, say, a memory leak. Now, in Fig. 1(left), if for some reason the call f() raises an exception, the continuation will be safely aborted, but if f() succeeds and the call g() raises an exception instead, the resulting behavior would be ill-defined, as the required execution of \( free \;a'\) will be discarded. On the other hand, consider the slightly different code snippet in Fig. 1(right): even assuming that f() or g() may raise an exception, there will be no value usage violations, since both a and b will still be in their initially discardable state at such stage. A suitable typing discipline should deem the left snippet unsafe but the right snippet safe, taking into account the interference between control effects and the linear usage behavior of values in scope.

Fig. 2.
figure 2

Code snippet for the server example.

For a further example, consider the slightly more involved scenario given in Fig. 2, which involves concurrent communication and explicit exception handling. Our idealized linear functional language is now assumed to include the ability to fork threads, and manipulate session typed communication channels. In the first line of Fig. 2, a thread representing a logging server is forked: the fork primitive \(\mathtt {FORK}\, l. e\) spawns a thread with body e accessing one endpoint of session channel l, and returns the other endpoint (bound to log) to the caller. The logging server receives precisely two messages, whose payload is a string, and closes the connection. Then another concurrent thread is spawned, mocking a resource allocation service: it receives a resource code, returns the quality of service constraints, and receives some reservation information \( bk \).

The server at channel \( res \) is used by client code that first sends a resource code \(\mathtt {QU2112}\), receives a quality of service \( qss \) spec, and then calls a conformance checking operation \( Check \) which, crucially, may raise an exception:

$$ \begin{array}{l} \mathtt {LET}\, Check =\lambda l. \lambda x.\mathtt {SEND}(l,``checkin");\mathsf {if} \; Valid (x) \;\mathsf {then}\; l \;\mathsf {else}\; \mathtt {THROW}\, (l)\,\mathtt {IN}\,... \end{array}$$

All interactions between client and server are meant to be logged, so channel log is also passed to the \( Check \) function together with the quality of service specification.

The overall expected behavior would be as follows. If \( Valid \)() returns \(\mathsf {true}\), then \( Check \) succeeds and the client will proceed booking the resource; however, if \( Check \) raises an exception, the continuation will be discarded and the exception handler invoked. In this case, the continuation of the resource allocation thread at the other endpoint will also need to be aborted. If the overall ongoing (linear) session between client and server could be safely discarded at that particular stage of the protocol, then the overall behavior may be deemed safe, even if the \( log \) could not be safely aborted, since the linear outstanding interaction on the \( log \) endpoint will be performed anyway by the exception handler. Indeed, notice that the \( log \) is linearly passed to the exception handler as \( logc \). Again, a suitable typing discipline combining effects and linearity should be able to express the assumptions underlying the reasoning above, and deem the code snippet safe, under the stated assumptions, but unsafe if the client server session is not safely abortable exactly before the \(\mathtt {RECV}(f,bk)\) interaction (cf. Line 2 in Fig. 2). Moreover, any such typing discipline should also be compatible with internal non-determinism, in the sense that if the result of \( Valid \)() is non-deterministic, then the resulting computation must be soundly typed for any alternative result, including the degenerate situation in which no value at all is returned, and the rest of the computation needs to safely abort, including, in that extreme case, the exception handler code itself!

The main goal of the paper is to investigate a principled foundation to express, reason and type-check a wide class of control effects in the context of a linear behavioral type system. Crucially, our approach builds on prior work on Curry-Howard correspondences between session types and various fragments of linear logic; our type system is a conservative extension of a standard system of classical linear logic. By approaching a Curry-Howard correspondence from the programming language perspective (in the spirit of, e.g., [5, 6, 18]), we introduce two new dual logical modalities—monadic and co-monadic \({\oplus }A\)—with associated programming constructs and proof reductions. As is often the case for type systems motivated by Curry-Howard correspondences, our system ensures global progress and usage/session protocol fidelity. Moreover, it is intrinsically compatible with all other logically motivated constructs and methods introduced in prior/related work, such as behavioral polymorphism [10, 48], logical relations [37], dependent types [42], higher-order code mobility [44], and multiparty protocols [9, 16].

It turns out that our new modalities and \({\oplus }A\) suffice to express general forms of internal non-determinism, and, importantly, include failure—an explicitly typed form of affinity—as a special case. These two modalities can be seen as an additional pair of linear logic exponentials, and as such obey the basic monadic/co-monadic laws. However, while the standard linear logic modalities !A and ?A encapsulate contraction and weakening, and \({\oplus }A\) encapsulate non-determinism and failure, in a sense to be made precise below. Although related to the non-deterministic monad and to well-known powerdomain models of non-determinism [39], a key novelty of our work is the perfect Curry-Howard match between proof reductions associated to the and \({\oplus }A\) modalities and sensible operational rules. This correspondence allows us to state cut-elimination, and to naturally derive key properties with practical impact (e.g., lock freedom, fidelity, and strong normalization), while supporting natural effectful programming idioms and powerful reasoning techniques (such as logical relations).

We will illustrate through examples how expressive linear usage protocols involving effects may be compiled down to the basic linear logic system extended with these two primitives and associated programming constructs. We will present our basic results and examples for a canonical session-based \(\pi \)-calculus model realizing a Curry-Howard interpretation of session types as linear logic propositions. As is well-known, the \(\pi \)-calculus is a complete foundational model, able to represent, e.g., general concurrent computation, higher-order data, and object-oriented features [40]. Hence, our development is carried out in the setting of most higher-level programming languages. In particular, we will use this process model as target language in a typed encoding of an effectful linear higher-order functional language with threads, session-typed channels, non-determinism, and exceptions, allowing us to show typings for the above examples.

Structure of the Paper. Next, Sect. 2 presents our Curry-Howard interpretation of session types for concurrent processes via examples. Sect.  3 establishes meta-theoretical results for typed processes: cut elimination (Theorem 3.1), type preservation (Theorem 3.2), progress (Theorem 3.3). Also, a postponing result (Theorem 3.5) connects our process model with the (non confluent) non-determinism typical of process calculi. Sect. 4 encodes \(\lambda ^{\mathsf {exc}}\) (a linear, higher-order functional language with concurrency and exceptions) into session-typed processes. \(\lambda ^{\mathsf {exc}}\) is the reference language for the motivating examples above. Theorem 4.1 ensures that our encoding preserves typing; therefore, all results in Sect. 3 will carry over to \(\lambda ^{\mathsf {exc}}\). In Sect. 5 we discuss related works, and Sect. 6 concludes.

2 The Core Language and Its Type System

We base our development on a standard session-typed \(\pi \)-calculus, a core language in which general higher-order concurrent programs may be modeled and analyzed [40]. The (binary) session discipline [27, 28] applies to pairs of name passing processes that communicate through point-to-point channels. In this setting, interaction between processes always occur in matching pairs: when one partner sends, the other receives; when one partner offers a selection, the other chooses; when a partner closes the session, the other must acknowledge—no further interactions may occur on the same channel. Sessions are initiated when a participant invokes a server, which acts as a shared provider, with the capability of unboundedly spawning fresh sessions between the invoking client and the newly created service instance process. A service name may be publicly shared by any clients in the environment. A session-based system exhibits concurrency and parallelism because many sessions may be executing simultaneously and independently. No races in communications within a session (or even between different sessions) can occur. Both session and server names may be passed around in communications. Session channels are subject to a linear usage discipline, conforming to a specific state dependent protocol, while server channels can be freely shared, and used by an arbitrary number of concurrent clients that can call on them for spawning new session instances.

Next we gradually introduce the ingredients of our typed process model (syntax, semantics, session types and their linear logic interpretation), which are summarized in Fig. 3. This presentation allows us to better motivate and describe the key novelty in this paper—the(dual) types for non-deterministic behaviors in sessions.

Caires and Pfenning [11] introduced a type system for \(\pi \)-calculus processes that corresponds to a linear logic proof system, revealing the first Curry-Howard interpretation of session types as linear logic propositions. Unlike traditional session type systems, Curry-Howard interpretations of behavioral types ensure global progress (i.e., well-typed processes never get stuck), livelock-freedom, and confluence (up to \(\equiv \)), and may be developed within intuitionistic [11, 13] or classical linear logic [13, 48], with certain subtle differences in expressiveness. Our system extends the presentation \(\varSigma _2\) of classical linear logic [1] with mix principles and, crucially, with new exponential modalities \({\oplus }A\) and , which will be interpreted as (dual) types for non-deterministic sessions.

Definition 2.1

(Types). Types (ABC) are given by

In examples we will also assume given some basic (data) types (e.g., naturals, strings, etc.), but will not elaborate the nature of such basic types (see, e.g., [42]). Despite notational similarity, there is no ambiguity between our new (unary) modalities \({\oplus }A\) and and standard linear logic (binary) operators for additive disjunction and conjunction.

For any type A, we define its dual \(\overline{A}\), where \(\overline{(\cdot )}\) corresponds to linear logic negation \((\cdot )^\perp \), following standard de Morgan-like laws. Intuitively, the type of a session endpoint is the dual of the type of the opposite endpoint.

Definition 2.2

(Duality). The duality relation on types is given by:

Typing judgments have the form \(P \vdash \varDelta ; \varTheta \), where P is a program term, \(\varDelta \) is the linear context and \(\varTheta \) is the unrestricted context, along the lines of DILL [4] and \(\varSigma _2\) [1]. Both contexts are assignments of types to (channel) names \(x,y,z,\ldots \). We write ‘\(\cdot \)’ to denote empty typing environments. After erasing the term P, our judgment corresponds exactly to a logical sequent in the classical linear logic \(\varSigma _2\) of [1]. Remarkably, this formulation naturally supports a Curry-Howard interpretation for the exponentials !A and ?A in terms of standard (\(\pi \)-calculus) semantics for lazy replication [11, 13].

2.1 Reduction Semantics

The operational semantics of our session calculus is defined by a relation of reduction (denoted \(P\rightarrow Q\)) that expresses dynamic evolution, and a relation of structural congruence (denoted \(P \equiv Q\)), which equates processes with the same spatial (or static) structure. This semantics exhibits a precise correspondence with cut elimination at the logic level. While most cut-reduction steps directly correspond to process reductions, other cut-reduction steps are better expressed in the process world as structural congruence principles or as behavioral equivalences; this applies similarly to the so-called commuting conversions, which are known to capture typed behavioral equivalences [37].

To describe reductions and conversions on proof trees (which correspond to typing derivations), we introduce a simple algebraic notation. For each typing rule with k premises \(d_1,\ldots , d_k\) we denote by \(\text{ T* }(p_1,p_2,\ldots )\) the derivation obtained by applying rule to the derivations \(p_1,\ldots ,p_k\). If the proof rule binds names \(\tilde{x}\) in the conclusion (as in, e.g., cut), we would then write \(\text{ T* }(\tilde{x})(p_1,p_2,\ldots )\) to make this binding explicit.

2.2 Basic Typing Rules, Congruence Rules, and Reduction Rules

The parallel composition of processes is typed in our system by rules corresponding to the cut and mix principles (dependent and independent composition, respectively).

The mix rule \((\mathrm{T} {{\;\mathbf {|}\;}})\) types the composition of two processes that do not share linear names; P and Q run in parallel but do not interact. The cut rule types the composition of P and Q while establishing a binary session between them using a single linear channel x; each process holds one of the two (dual) endpoints x of a session of type A and \(\overline{A}\). This channel is kept private to the composition by the restriction operator \(({{\mathbf {\nu }}}x)(...)\) so that the newly established session will not be affected by interferences. Rule \((\text {T}\cdot )\) allows the inactive process \(\mathbf{0}\) to be introduced. Neutrality of \(\mathbf{0}\) is expressed by the conversion at the level of proofs, which corresponds exactly to the usual structural congruence principle (we consider here a conversion, not a computational reduction, since it does not involve any process interaction). We take process terms up to basic structural congruence principles, namely we assume that is commutative and associative with unit \(\mathbf{0}\), etc. This way, e.g., and denote the same process, i.e., the (unique) parallel composition of P and Q. Thus, Rule  is symmetric w.r.t. its premises: if is a derivation then is the same derivation; we then also consider the conversion .

Session Send and Receive. Session-typed processes communicate by sending and receiving messages according to some session discipline. The message payload can be a value of some primitive data type or a session channel; we focus here on the general case of session passing (delegation). Type \(A\otimes B\) is the type of a session that first sends a session of type A and then continues as a session of type B. As such, it corresponds to the session type !A.B of [27]. Dually, is the type of a session that first receives a session of type A and then continues as a session of type B; it thus corresponds to the session type \(?\overline{A}.B\). Hence, the session type ?A.B corresponds to the linear type . We have the following typing rules for send \(A\otimes B\) and receive .

An output process is then of the form \(\overline{x}(y).M\), where y is a freshly created name. The behavior of such an output process is to send session y on x and then proceed as defined by M. In our typed language, the output continuation M has the form , where P defines the behavior of the session y being sent and Q the behavior of the continuation session on x. An input process is of the form x(y).R, a process that receives on session x a session n, passed in parameter y, and then proceeds as specified by R. The continuation R will use the received session but also any other open sessions (including x). Notice that y is bound both in \(\overline{x}(y).M\) and in x(y).R, and so only fresh names can be sent in output processes; this corresponds to the internal mobility discipline [7], without loss of expressiveness. The associated principal cut reduction corresponds to process communication, where \(C = \overline{A}\), \(D=\overline{B}\), expressed by

This reduction exactly captures (bound output) communication in the \(\pi \)-calculus

where we write . Although is commutative there is no ambiguity in Rule : P and Q are the split of M typed by \( P \vdash \varDelta , y{:}{A}; \varTheta \) and \( Q \vdash \varDelta ', x{:}{B}; \varTheta \), respectively, and \(\varDelta \) and \(\varDelta '\) are the split of the linear context in the conclusion. The multiplicative units \(\bot \) and \(\mathbf {1}\) type session termination actions as seen from each endpoint; no partner can further use a closed session.

The associated principal cut reduction corresponds to session termination, which we define at the level of processes and proof trees respectively by the rules

Types \(\mathbf {1}\) and \(\bot \) correspond to the single type \(\mathtt {end}\) in usual session types, and usually have a silent interpretation. In the presence of mix principles, as we consider here, propositions and are valid. Considering \(\bot =\mathbf {1}\), we could define a single type ‘\(\mathord {\bullet }\)’ as standing for “both” \(\mathbf {1}\) or \(\bot \), where \(\overline{\mathord {\bullet }}=\mathord {\bullet }\). (Recall that .)

Session Offer and Choice. The linear type \(A\oplus B\) types a session that first chooses (from the dual partner menu) either “left” or “right”, and then continues as a session of type A or B, depending on the choice. This type is the binary version of the session type \(\oplus _{i\in I} \{l_i{:}A_i \} \) (labeled internal choice). The linear type types a session that first offers both “left” or “right” menu options and then continues as a session of type A or B, depending on the choice made by the partner. Thus, is the binary version of the session type (labeled external choice). Offers and choices are typed by the additive linear conjunction and disjunction and \(\oplus \), as defined by the rules:

The associated principal cut reductions correspond to the process and proof reductions

In examples we may consider n-ary labeled sums, close to usual session types constructs:

with associated principal cut reduction expressed by

Example 2.3

(Movie Server (1)). Consider a toy scenario involving a movie server and some clients. We first model a single session (on channel s) established between client \( Alice (s)\) and server instance \( SBody(s) \). The server session offers two options: “buy movie” (\(\mathtt {inl}\)), and “preview trailer” (\(\mathtt {inr}\)). Alice selects the “preview” option from the server menu, and plays the corresponding protocol. Consider now the following terms:

Assume some given basic types for movie titles (T), credit card data (C) and movie files M, which are self-dual (since they do not type communication capabilities but values of basic types). We can then provide the following types and derivable type assignments for the various system components as follows:

We would then have \( System _1 \vdash \cdot ~; \cdot \). While the type of the server endpoint is \( SBT \), the type of a client endpoint would be .    \(\square \)

Shared Service Definition and Invocation. Shared service definition and invocation are typed by the linear logic exponentials \({\mathbf {!}}\) and ?. Type \({\mathbf {!}}A\) types a shared channel that persistently offers a replicated service which whenever invoked spawns a fresh session of type A (from the server’s perspective). Dually, type ?A types a shared channel on which requests to a persistently replicated service of type A can be unboundedly issued (from the client’s perspective). We consider the following typing rules:

The associated principal cut reduction corresponds to shared service invocation

This operational interpretation of the rules for \({\mathbf {!}}A\) and ?A (cf. [1, 4, 38], implementing “lazy” contraction) exactly coincides with the usual interpretation of lazy replication. Notice that Rule (T?) is silent on the term assignment: it implements a bookkeeping device to move the typed channel to the unrestricted context, and does not induce a computational effect (e.g., as exchange is also implicitly handled).

As our typing judgments have two different regions, linear and exponential, two cut rules are required [4], one for cutting a linear (session) channel in the linear context (Rule , already presented in Sect. 2.2), and the following rule, for cutting an unrestricted (shared) channel in the exponential context [4, 38]:

For typing “source programs” only the linear Rule  is required, but Rule  is required for cut-elimination; hence, Rule  is a “runtime” typing rule. The principal reduction above is expressed at the level of proofs by

Example 2.4

(Movie Server (2)). We illustrate the usage of \({\mathbf {!}}A\) and ?A types using a shared movie server, which may answer requests from an unbounded number of clients; here we use just two concurrent clients, \( SAlice \) and \( SBob \). Alice still selects the “preview trailer” option as in Example 2.3, but Bob selects the “buy movie” option. Recall the definitions of processes \( SBody (s)\) and \( Alice(s) \) and type \( SBT \) from Example 2.3.

The following typing judgments are derivable:

We can obtain \( System _2 \vdash \cdot ; \cdot \) as follows: we first use the (mix) Rule  to compose the two clients; then, Rule  is used to merge the shared endpoints under the explicit type \(?\overline{ SBT }\); finally, the clients are composed with the server using Rule .    \(\square \)

Identity. We interpret the identity axiom by the forwarder process \([x\leftrightarrow y]\) [12, 48], which denotes a bidirectional (linear) link between sessions x and y, giving a logical justification to a known concept in \(\pi \)-calculi (cf. [24]). The forwarder at type A is typed

The associated cut reduction (where y is not free in P) is akin to the application of an explicit substitution. It is known since [30] that linear forwarders can simulate substitution in the sense of the above reduction rule. We also introduce \([x\!\leftrightarrow \!y] \equiv [y\!\leftrightarrow \!x]\) as a structural congruence axiom, as a direct consequence of (implicit) exchange in the typing context. While a well-typed copycat process \(F_A\) without forwarder links can be easily constructed for any concrete type A by \(\eta \)-expansion (see [13]) the primitive forwarder is important when considering polymorphism [10]. It also allows us to represent the “free” output construct \(x\langle y\rangle .P\) (where y is a free channel name in scope) by (cf. [7]).

2.3 Non-determinism and Failure

The developments of this paper focus on the challenge of expressing fundamental primitives for non-deterministic behavior—including the special important case of abortable behavior—in the setting of our Curry-Howard correspondence for session types.

It is often believed that a Curry-Howard interpretation of a programming language is hard to reconcile with true (so-called internal) non-determinism in computation, since reduction steps should express at most behavioral equivalences on processes, via proof identities, which are inherently confluent from an operational viewpoint. However, it is clear, at least from work on denotational semantics and functional programming, that non-determinism can be handled equationally by working on the powerdomain of computation results. In the logical setting, developments on differential linear logic [22] also require the interpretation domain for proofs to be closed under a (formal) notion of “sum”, which could be interpreted as non-deterministic choice. Although partially inspired by such approaches, our proposal picks a fairly different road, which turns out to lead to the first example of a Curry-Howard interpretation of a realistic programming language with built-in internal non-determinism.

It is well-known after Girard that the linear logic exponential modalities !A and ?A, which have been used above to model the type of shared channel names, are not uniquely defined by their standard proof rules: not surprisingly, if one adds additional operators defined by the same rules, we obtain independent monad/comonad pairs. We exploit this fact to our advantage, noting that it allows us to modularly add new “exponential” modalities to the base logical system, defined by identical proof rules (in Girard’s original formulation), without semantically interfering with the existing ones. Any such pair of connectives (say, and \({\oplus }A\)) will yield a dual monad/comonad pair defined by the fundamental principles (in a simplified form):

For the usual modalities !A and ?A, additional specific rules for ?A define the intended semantics of the linear logic exponentials, which encapsulate the structural principles of weakening and contraction:

These observations suggest a logically justified methodology for adding new monadic operators to the basic linear logic framework, by means of independent monad/comonad pairs in which the monad semantics is defined by specific additional logical principles. We develop our type system on top of (classical) linear logic, conservatively extended with two operators capturing a (co)monad defined by (a refined version of the) following principles, which can be verified to be sound for an (additive) monad and comonad \({\oplus }-\).

The resulting proof (and type) system provides a Curry-Howard interpretation of a realistic programming language with built-in internal non-determinism and failure.

Getting back to the presentation of our type system, we capture non-deterministic behavior in the type structure by operators and \({\oplus }A\) related by duality () and defined by the following rules:

figure a

Intuitively, is the type of a session that may produce a behavior of type A: this potential is made concrete in Rule  where the behavior is indeed available (some), whereas Rule  describes the case in which is not available (none). Dually, the type \({\oplus }A\) is the type of a session that may consume a behavior of type A. Rule  accounts for the possibility of not being able to consume an A by considering sessions different from x as potentially not available (i.e., abortable - cf. in the rule, where \(\overline{w}\) denotes a sequence \(w_{1}, \ldots , w_{n}\) of names). Rule expresses non-deterministic choice. While it may be seem to correspond to a formal sum of proofs (cf. [22]), in our case it corresponds exactly to non-deterministic choice \(P\oplus Q\) of processesFootnote 1, and can only be used inside the monad . The principal cut reductions are:

At the level of the process interpretation, these reduction rules are expressed by

Notice how the reduction for safely discards the continuation Q. We also consider the following proof conversion (and corresponding process congruence) that expresses the distribution of parallel composition over internal choice:

Notice that, in principle, the two computational reduction rules above could be formally used to express the reduction rules for the “sharing” exponentials (cf. [48]) in presentations of linear logic with explicit weakening and dereliction rules, instead of the DILL-style presentation we have adopted here. Indeed, we prefer the DILL-style presentation as it more tightly express the behavior of sharing present in traditional session types. On the other hand, together with the conversion principle just shown, the primitives and reduction rules just presented turn out to be quite adequate to express the behavior of non-determinism and failure.

Before closing the section, we discuss examples that use \({\oplus }A\) and types.

Example 2.5

(Movie Server (3)). Getting back to our movie server scenario we illustrate how to model a system with a client \( Randy (s)\) that non-deterministically decides between either buying a movie or just seeing its trailer. Recalling process definitions for \( SBody (s)\), \( Alice (s)\), and \( Bob (s)\) from Examples 2.3 and 2.4, we would have:

where the suitable types and type assignments are now given by

Process \( Randy (s)\) is typed by using Rule  on each individual client; then, using Rule  one would obtain a typed non-deterministic choice between them. The server is typed using Rule  with \(\overline{w} = \emptyset \), for there are no sessions (besides s) in the linear context (recall that \( SBody (s) \vdash s: { SBT } ~; \cdot \)). This way, we derive \( USystem \vdash \cdot ~;~\cdot \).    \(\square \)

Interestingly, the non-deterministic choices enabled at the level of types by and \({\oplus }A\) (and at the process level by \(\oplus \)) are completely orthogonal to the usual deterministic choices enabled by labeled internal and external choices. The following example illustrates the pleasant interaction between deterministic and non-deterministic choices:

Example 2.6

(Movie Server (4)). Consider now a variant of the movie server that logs the request made by the client on a log service l of (boolean) type \(\mathbf {B}= \mathbf {1}\oplus \mathbf {1}\). We extend the process \( SBody (s)\) from Example 2.3 as follows:

We may provide a typing \( SBodyL (s) \vdash s{:} { SBT }, l {:}\mathbf {B}~;\cdot \) which cannot be composed with the non-deterministic client \( Randy (s)\) from Example 2.5. However, process

$$s.{\mathtt {some}}_{l};l.\overline{{\mathtt {some}}}; SBodyL (s)$$

may now be composed with client process \( Randy (s)\) as

Now we may derive: and

Writing \(P\Rightarrow Q\) to denote the reflexive-transitive closure of \(P\rightarrow Q\), we obtain the reduction sequence \( ULSystem \Rightarrow (l.{{\mathtt {inr}}};l.\overline{\mathtt {close}} \oplus l.{{\mathtt {inl}}};l.\overline{\mathtt {close}}) \).

Notice that the visible behavior of log channel l in \( ULSystem \) must be given the non-deterministic type : there is no typing \( ULSystem \vdash l {:} \mathbf {B}\), since the resulting interaction is essentially non-deterministic.    \(\square \)

In our system, the ability of representing (internal) non-determinism is intrinsically tied to that of describing, in a completely logically motivated manner, abortable behaviors as typical of programming constructs such as exceptions and compensations [23]. Our following example illustrates this distinctive aspect of our model.

Example 2.7

(Movie Server (5)). To consider the possibility of modeling failure, we introduce the code for a “faulty” client, that non-deterministically behaves like \( Bob (s)\) (cf. Example 2.4) or does not produce any behavior at all. Consider the non-deterministic server \( SBodyNDL (s)\) from Example 2.6; we may now have:

Notice how failure of sub-computations propagates inside the monad , encapsulated in a hereditarily safe way. Here, we have the reduction sequence

reflecting that the composed system either aborts or chooses on the log.    \(\square \)

We now illustrate how systems encapsulating non-deterministic behavior can nevertheless be given a globally deterministic type, thus showing that internal non-determinism and failure are not visible as long as they are typed by “plain” deterministic types.

Example 2.8

Consider the following processes and typings:

Notice that the although the producer process \( Prod \) sends a non-deterministic boolean to the consumer process \( Cons \), the type of the composed system \( Blob \) is \(b : Str \otimes \mathbf {1}\), a deterministic type. In fact, we may easily verify that \( Blob \Rightarrow b\langle ``done"\rangle .b.\overline{\mathtt {close}}\).    \(\square \)

Figure 3 summarizes our process language, and associated reduction and structural congruence relations. The main properties of our system will be established next.

Fig. 3.
figure 3

The process language.

3 Main Results

We collect in this section main sanity results for our non-deterministic linear logic-based type system for session process behavior. First, our system enjoys the cut-elimination property. Cut elimination may be derived given a suitable congruence \(\cong _{s}\) on processes consisting of reduction (computational conversions), structural congruence (structural conversions), and some key commuting conversions (cf. [11, 13, 37]).

Theorem 3.1

(Cut Elimination). If \(P \vdash \varDelta ; \varTheta \) then there is a process Q such that \(P \cong _s Q\) and \(Q \vdash \varDelta ; \varTheta \) is derivable without using rules \((\text {T}{{\mathsf {cut}}})\) and \((\text {T}{{\mathsf {cut}^{?}}})\).

The proof is an extension of the proof for classical linear logic with mix, but considering the new reductions and conversions introduced above for revealing and reducing principal cuts involving the and \({\oplus }A\) modalities.

Then, we may state type safety, witnessed by theorems of type preservation and global progress for closed systems. Type preservation states that the observable interface of a system is invariant under reduction.

Theorem 3.2

(Type Preservation). If \(P \vdash \varDelta ; \varTheta \) and \(P \rightarrow Q\) then \(Q \vdash \varDelta ; \varTheta \).

Proof

(Sketch) By induction on typing derivations, and case analysis on reduction steps. In each case, the result easily follows, given that reductions come from well-defined proof conversions, which by construction preserve typing.    \(\blacksquare \)

Unlike standard type systems for session types, our logical interpretation satisfies global progress, meaning that well-typed processes never get stuck on pending linear communications. More precisely, we say that a process P is live, noted live(P), if and only if \(P \equiv C[\pi .Q] \) where \(C[ - ]\) is a static context (e.g. a process term context in which the hole is not behind an action prefix, but only under parallel composition , name restriction \(({{\mathbf {\nu }}}x)-\), or sum \(-\oplus -\) operators) and \(\pi .Q\) is not a replicated process (i.e., \(\pi \) is a session input, output, offer, choice, or non-deterministic action). We then have:

Theorem 3.3

(Progress). If \(P \vdash \; ; \varTheta \) and live(P) then there is Q such that \(P\rightarrow Q\).

Proof

(Sketch) By induction on the typing derivation. Our proof relies on a contextual progress lemma, which uses a labeled transition system for processes, compatible with reduction (cf. [13]). This lemma yields a more general progress property for processes with free linear channels that transition by means of immediate external interactions. It extends Lemma 4.3 in [13] (which holds for a language without non-determinism) as follows: If \(P \vdash \varDelta ; \varTheta \) and live(P) then either (1) there is Q such that \(P\rightarrow Q\) or (2) there are \(P_i\) (\(i=1..n\)) such that \(P\equiv \oplus P_i\) and for all \(P_i\) there exist \(Q_i\) and \(\alpha \) such that \(P_i\mathop {\rightarrow }\limits ^{\alpha } Q_i\). The proof of this extended lemma is by induction on derivations.    \(\blacksquare \)

We now discuss additional results that clarify some key features of the our type system. We say that a process P is prime if it is not structurally congruent to a process of the form \(Q\oplus R\) with non-trivial (i.e., equivalent to \(\mathbf{0}\)) Q and R. We can then prove:

Proposition 3.4

Let \(P \vdash \varDelta ; \varTheta \) where types in \(\varDelta ; \varTheta \) are deterministic (do not contain or \({\oplus }A\) types at the top level), and let \(P \Rightarrow Q \not \rightarrow \). Then Q is prime.

Proof

(Sketch) By induction on the typing derivation.    \(\blacksquare \)

Based on a logical system in which reduction matches cut-elimination, it turns out that typing in our system enforces confluence and also strong normalization. These results can be established using (linear) logical relations, as developed in [37]. Intuitively, confluence holds because non-determinism is captured equationally without losing information, by means of delaying choice in processes \(P\oplus Q\), which express sets of alternative states. Still, it is interesting to relate our system with standard process calculi which explicitly commit non-deterministic states into alternative components. For that purpose, we investigate the extension of the reduction relation in Fig. 3 with non-confluent rules for internal choice, standard in process calculi but clearly incompatible with any Curry-Howard interpretation, namely \( P \oplus Q \rightarrow P\) and \(P \oplus Q \rightarrow Q\). We denote by \(P\rightarrow _c Q\) (and \(P \Rightarrow _{c} Q\)) the extended reduction relation, which can be proven to still satisfy preservation and progress in the sense of Theorems 3.2 and 3.3. We may then show the following property, expressing postponing of internal non-deterministic collapse of non-deterministic states into prime states.

Theorem 3.5

(Postponing). Let \(P \vdash \varDelta ; \varTheta \). We have

  1. 1.

    If \(P \Rightarrow P_1 \oplus \ldots \oplus P_n \not \rightarrow \) with \(P_i\) prime for all i, then \(P \Rightarrow _c P_i\) for all \(0< i\le n\).

  2. 2.

    Let \(\mathcal {C} = \{P_i \;|\; P \Rightarrow _c P_i \not \rightarrow _c \text{ and } P_i \text{ is } \text{ prime } \}\). Then \(\mathcal {C}\) is finite up to \(\equiv \), with \(\#\mathcal {C} = n\), and for all \(0 < i\le n\), \(P \Rightarrow P_1 \oplus \ldots \oplus P_n \rightarrow _c P_i\).

Proof

1. Trivial by definition. 2. By induction on the reduction sequence, using the fact that we may commute \(\Rightarrow \) reduction steps backwards with \(\Rightarrow _c\) reduction steps.    \(\blacksquare \)

Theorem 3.5(2) shows that no information is lost by \(\Rightarrow \) with respect to the (standard) non-deterministic (and non-confluent) semantics of internal choice \(P\oplus Q\) expressed by \(\Rightarrow _c\). We may therefore tightly relate our system, based on a logically motivated reduction relation, with a standard non-confluent reduction relation including rules for internal choice, in the sense that the former precisely captures the multiset of observable alternatives defined by the latter, while preserving compositional and equational reasoning about system behavior as expected from a Curry-Howard interpretation.

4 Higher-Order Concurrency, Non Determinism, and Exceptions

We illustrate the expressive power of our typed process model by embedding \(\lambda ^{\mathsf {exc}}\), a linear higher-order functional, concurrent programming language with concurrency, non-determinism—including failure—, and exceptions. Defined by a typed compositional encoding, this embedding allows us to showcase the generality of our developments and the relevance of our Curry-Howard correspondence in a broader setting; it will also enable us to give a rigorous footing to our motivating examples (cf. Figs. 1 and 2).

The Target Calculus. \(\lambda ^{\mathsf {exc}}\) is a typed call-by-value functional calculus, defined by the grammar below. We use \(e, e', \ldots \) to range over expressions; \(v, v', \ldots \) to range over values; \(x, y, z, \ldots \) to range over variables; \(c, c', \ldots \) to range over channels, and TUAB to range over types. The syntax of values, expressions, and types (T) is as follows:

We say expressions are effectful if they can raise an exception, and pure otherwise. Besides the unit type, types for \(\lambda ^{\mathsf {exc}}\) include (linear) arrow types of two forms: \(A \xrightarrow {\mathsf {0}} B\) is the type of functions that do not raise exceptions, whereas \(A \xrightarrow {T} B\) is the type of functions that may raise an exception of type T. We also have session types \({!}T.T'\) and \({?}T.T'\) for output and input channel-based communication. Types for labeled selection and choice are not included but can be easily accommodated. Types \({\mathsf {end}_!}\) and \({\mathsf {end}_?}\) denote the dual views of terminated endpoints. Furthermore, we have types \({\oplus }T\) and for expressions that may produce and consume values of a type T, respectively. We write \(S, S'\) to denote the session fragment of the type structure (i.e., no unit nor arrow types). On this fragment, we assume a duality relation, denoted \(\overline{S}\), defined as expected. The type syntax does not include general (non-linear) functional values nor shared sessions; the integration of these constructs is orthogonal and unsurprising.

As values, we consider variables, abstractions, and the unit value \(\mathsf {*}\); we also have the abortable value \(\langle \!\langle v\rangle \!\rangle \), which represents discardable (affine) values: given a value v of type T, value \(\langle \!\langle v\rangle \!\rangle \) will be of type \({\oplus }T\). For convenience, the language is let-expanded; as a result, application is of the form \((f \, x)\), for variables f and x. Expressions also include a try-catch construct for scoped exceptions, with the expected meaning, and a construct for raising/throwing exceptions with an explicit value. The key features of the process model in Sect. 2 appear as expressions that may produce a value of a certain type and one construct that may consume a value of a certain type. Non-deterministic choices between two expressions are also supported. Concurrency is enabled by spawning threads, using a forking construct. Moreover, \(\lambda ^{\mathsf {exc}}\) includes expressions for channel-based communication, enabling the exchange of values of any type (including channels).

As mentioned above, the intended operational model for \(\lambda ^{\mathsf {exc}}\) is call-by-value; rather than directly giving the operational semantics for the language, we first delineate its behavior via a type system and then give its semantics indirectly, via a type respecting encoding into the basic type system introduced in Sect. 2.

The type system we consider here is actually a type-and-effect system in which the effect represents the type of the exception that can be raised by the typed expression. Judgments are then the form \(\mathsf {D}\vdash ^{U} e:T\): under an environment \(\mathsf {D}\) (a set of typing assignments), the expression e has return type T, while the effect type U is either \(\mathsf {0}\) (the expression is pure) or T (the expected type of exceptions).

The typing rules for \(\lambda ^{\mathsf {exc}}\) are shown in Fig. 4. Rule  types abstractions; it decrees that the type of the exception possibly raised by the abstraction body will be used as the effect associated to the arrow type. Rule  types abortable values \(\langle \!\langle v\rangle \!\rangle \), as motivated earlier: it closely follows the principles of Rule  for session-based processes; in particular, it requires all free variables in v to be abortable (cf. the premise \({\oplus }\mathsf {D}\)). Rule  allows to cast an (trivially) effecful expression from a pure one.

There are three typing rules for let expressions \(\mathtt {LET}\,a=e_{1}\,\mathtt {IN}\,e_{2}\); the actual rule used depends on the exceptions possibly raised by its constituent sub-expressions \(e_1\) and \(e_2\). Rule  is used when both \(e_1\) and \(e_2\) are effectful. Observe that \(e_2\) must be typable in an abortable environment, in order to safely account for an exception raised in \(e_1\). Rule  handles the case in which both \(e_1\) and \(e_2\) are pure, while Rule  covers the case in which only \(e_1\) is pure. These three typing rules are crucial to isolate effects and to exploit the combination of pure with effectful computations.

Fig. 4.
figure 4

Typing rules for \(\lambda ^{\mathsf {exc}}\).

Rule  types the construct ; the type of the exception possibly raised by \(e_1\) must match with the type of x in \(e_2\). Notice that \(e_1\) and \(e_2\) must be of the same type (T in the rule). Rule  ensures that the type of the thrown value is propagated as an effect. Rule  captures the essence of thread spawning for communication types, creating a new (linear) session channel where one endpoint is handed to the thread body and the other endpoint returned by the fork operation. Rules  and  type session channel closing operations; Rules  and  type operations for sending and receiving values along session channels.

Rule  and Rule type the production and consumption of a non-deterministic value as z, respectively. In particular, Rule applies to expressions that do not return values, but that may interact with expressions that do return values via channel-based communication. Notice the similarities between Rules  and  (for functional expressions) and Rules  and (for process terms), respectively. In the same vein, Rule  can be seen as the analogue of Rule  but for abortable expressions in our functional language. Rule  enables the non-deterministic choice between two pure expressions that do not return values; this allows us to define, e.g., non-deterministic sessions.

In general, the (two-sided) typing rules in Fig. 4 encompass a notion of duality, in the sense that a connective appearing in the left-hand side of the turnstile in the Fig. 4 corresponds to its dual in the right-hand side of the turnstile. This intuition will be captured in our embedding of functional expressions as processes, detailed next.

Example 4.1

We can now return to the code snippet in Fig. 2 and give some typings using the type structure just introduced. As mentioned in the introduction, there is a precise stage of the protocol along (dual) names res and f after which failure is safe. In our type structure we can precisely delineate such a place. We would have:

These typings require minor modifications in the code of Fig. 2: we add prefix ‘\(\mathtt {SOME}\,?\, {f}\)’ before ‘\(\mathtt {RECV}(f,bk)\)’, and prefix ‘\(\mathtt {SOME}\,!\, res\)’ before ‘\(\mathtt {SEND}(res,book)\)’.

Fig. 5.
figure 5

Encoding of \(\lambda ^{\mathsf {exc}}\) types into logical propositions.

Embedding \(\lambda ^{\mathsf {exc}} \) Into Session Typed Processes. We now present a typeful encoding of \(\lambda ^{\mathsf {exc}}\) into the logically motivated typed process model of Sect. 2, and establish its correctness (Theorem 4.1). The encoding has two main components: the encoding of (functional) types into linear logic based session types, and the encoding of \(\lambda ^{\mathsf {exc}}\) expressions into (non-deterministic) concurrent processes.

Figure 5 gives the encoding of types. We use the following shorthand notations:

(1)
(2)
(3)

Also, we assume the expected extension of the encoding of types to typing environments: given \(\mathsf {D}= x_1{:}T_1, \cdots , x_n{:}T_n\) then \([\![\mathsf {D}]\!] = x_1{:}{[\![T_1]\!]}, \cdots , x_n{:}{[\![T_n]\!]}\).

The encoding of expressions is typeful: for each typing rule in Fig. 4 we give a corresponding type derivation for session-typed processes. Figures 6 and 7 give a complete account; for readability, in those figures we show only the conclusion (final judgment) in the derivation. Also, we use the following abbreviations for processes:

  • we write \(y\langle z\rangle .P\) (where z is free in P) for the free output process, represented as (cf. Sect. 2);

  • we write \(y.\mathbf {\mathtt {0}}; P\) and \(y.\overline{\mathbf {\mathtt {0}}}\) to stand for \(y.\mathtt {close}; P \) and \(y.\overline{\mathtt {close}}\), respectively;

  • we define \(\mathsf {S}_{q}\) as the process \(q(u).q.\mathbf {\mathtt {0}};u.\mathbf {\mathtt {0}};\mathbf{0}\). Notice that \(\mathsf {S}_{q} \vdash q:\overline{\uparrow \![\![\mathbf {unit}]\!]}\).

As usual in encodings of (call-by-value) functional languages into the \(\pi \)-calculus, our encoding of expressions is indexed by names, which are used to interact with the environment; they can be seen as continuations or as locations where the value returned by an expression will be made available. In our case, these names are related to the effects of the source expression e:

Fig. 6.
figure 6

Typeful encoding of \(\lambda ^{\mathsf {exc}}\) terms into basic processes (Part 1).

Fig. 7.
figure 7

Typeful encoding of \(\lambda ^{\mathsf {exc}}\) terms into basic processes (Part 2).

  • If e is pure then its encoding will be indexed by a single continuation name y. This will be denoted \([\!\![e]\!\!]_{y}\).

  • If e is effectful then its encoding will be indexed by names y and x. This will be denoted \([\!\![e]\!\!]_{y,x}\): name y represents an non-deterministic continuation, along which the value to which e reduces may be produced; name x represents the continuation to the enclosing try-catch block exception handler.

These intuitive distinctions are made precise in our main technical result, which exploits the shorthand notations (1), (2), and (3) above:

Theorem 4.1

(Typability). Suppose \(\mathsf {D}\vdash ^U e:T\). Then, for some names yx, we have:

  • \([\!\![e]\!\!]_{y} \vdash \overline{[\![\mathsf {D}]\!]}, y{:}\uparrow \![\![T]\!]\), if \(U = \mathsf {0}\).

  • , if \(U \ne \mathsf {0}\).

Consequently, our source language \(\lambda ^{\mathsf {exc}} \) (which combines functions, concurrency, non-determinism, and exceptions) will inherit key guarantees from the target process language, namely preservation and global progress (deadlock absence and lock-freedom).

Due to space limitations, in the following we only discuss selected cases of Figs. 6 and 7. As already mentioned, the type of a let expression \(\mathtt {LET}\,a=e_1\,\mathtt {IN}\,e_2\) considers different possibilities for the interplay of pure and effectful computations in \(e_1\) and \(e_2\). If both expressions are pure (cf. Rule ) then the encoding is simple:

Since \(e_1\) is pure, we know \([\!\![e_1]\!\!]_{q}\) will surely produce a value, which will be made available to \([\!\![e_2]\!\!]_{y}\) along the private (linear) name q. The case in which both \(e_1\) and \(e_2\) may raise exceptions (cf. Rule ) is more interesting:

In this case, since \(e_1\) may raise an exception, we account for this possibility via the prefix \(q.{\mathtt {some}}_D\), which requires all values (including sessions) in \([\!\![e_2]\!\!]_{y,s}\) (excepting a) to be in abortable state. The production of a value within \([\!\![e_1]\!\!]_{q,x}\) will be signaled by a prefix \(q.\overline{{\mathtt {some}}}\), while throwing of an exception will be signaled by a prefix (see next). Therefore, if \([\!\![e_1]\!\!]_{q,x}\) produces a value (\(q.\overline{{\mathtt {some}}}\) is executed) then this value will be passed to \([\!\![e_2]\!\!]_{q,s}\) using the private name q; subsequently, the reference to the enclosing try-block x will also be passed to \([\!\![e_2]\!\!]_{q,s}\) as parameter s, exploiting linearity of name-passing (delegation). Otherwise, if \([\!\![e_1]\!\!]_{q,x}\) ever raises an exception ( is executed) then all the values in D will be safely and hereditarily discarded.

The encoding of values takes into account that a value may occur in an abortable context. The encoding of a variable z of type T is as follows:

$$ [\!\![z]\!\!]_{y} = y\langle z\rangle .y.\overline{\mathbf {\mathtt {0}}} \qquad \qquad [\!\![z]\!\!]_{y,x} = y.\overline{{\mathtt {some}}};y\langle z\rangle .y\langle x\rangle .y.\overline{\mathbf {\mathtt {0}}} $$

If z occurs in a pure context/expression, then its encoding, given on the left, is standard; name y will have type \(\uparrow \![\![T]\!]\) (cf. (1)). Otherwise, if z occurs in an effectful (abortable) context, then its encoding, given on the right, first announces the production of a value using prefix \(y.\overline{{\mathtt {some}}}\); after z is sent along y, name x (representing the continuation of the enclosing try-catch block exception handler) will be sent along y. The type of x will be \([\![U]\!] \boxplus {[\![T]\!]}\), where U is the type of the enclosing exception (cf. (2)). Thus, intuitively, x encompasses the potential for a normal execution (\([\![T]\!]\)) but also contains information on the (exceptional) behavior to be triggered upon failure (\([\![U]\!]\)). A more concrete justification for the typing \(x{:}[\![U]\!] \boxplus {[\![T]\!]}\) will become apparent next, when discussing the deterministic choice that underlies the encoding of try-catch and throw expressions.

To encode an abstraction \(\lambda z.e\), we distinguish several cases, depending on whether e and \(\lambda z.e\) are effectful or not. The simplest case is when both e and \(\lambda z.e\) are pure:

We follow closely known encodings of \(\lambda \)-calculus in the \(\pi \)-calculus, here adapted to a linear setting in which the continuation y and the reference to the function body f are session-typed [43]. When both \(\lambda z.e\) and e are effectful we follow a similar principle:

The prefix \(y.\overline{{\mathtt {some}}}\) declares the production of a value, namely the reference to the function body f. An invocation to f must supply the parameter of the function (z) but also the continuations k and j, to be linearly used by the encoding \([\!\![e]\!\!]_{k,j}\).

The encoding of applications goes hand in hand with the encoding of let expressions. Given the let-expanded semantics (which forces an expression’s context to deal with potentially abortable expressions), the encoding of applications \((f \, a)\) is simple:

$$ [\!\![(f \, a)]\!\!]_{y,x} = f\langle a\rangle .f\langle y\rangle .f\langle x\rangle .f.\overline{\mathbf {\mathtt {0}}} $$

We may now discuss the encodings of try-catch and throw expressions:

The encoding of is in two parts, denoted (I) and (II) above. Part (I) concerns normal behaviors only; Part (II) concerns normal and exceptional behaviors:

  • If \(e_1\) does not raise an exception then \([\!\![e_1]\!\!]_{k,j}\) will trigger a prefix \(k.\overline{{\mathtt {some}}}\), which will synchronize with Part (I). Subsequently, the obtained value and the reference to the enclosing exception block will be passed around; in this case, z will be substituted by j, and the prefix \(j.{\mathtt {inl}}\) will synchronize with the choice on j (Part \(\text {(II)}\)) to send the resulting value along y. This choice discards the right branch containing \([\!\![e_2]\!\!]_{y}\).

  • If \(e_1\) raises an exception then, because of the encoding of throw, process \([\!\![e_1]\!\!]_{k,j}\) will trigger a prefix which will synchronize with Part (I). As a result, the remaining behavior on k and z will be discarded. However, the choice on j (Part (II)) will continue to be available: this is used by the encoding of throw, which by executing \(j.{\mathtt {inr}}\) will select the right branch of Part (II). The value raised by the exception will be then passed to \([\!\![e_2]\!\!]_{y}\), which can now be executed.

Our encoding of try-catch therefore elegantly amalgamates the key features of our process model: most notably, the presence of abortable behaviors in a pleasant coexistence with non-abortable behaviors, and the interplay between non-deterministic and deterministic choices—indeed, it is the deterministic choice that underlies the exception mechanism what ultimately justifies the type \([\![U]\!] \boxplus {[\![T]\!]}\) for x, given in (2).

In the typed model presented here (and its encoding into processes), we consider try-catch constructs in which \(e_2\) is pure (cf. Fig. 4). However, there is no fundamental obstacle to address the general case in which both \(e_1\) and \(e_2\) may raise exceptions; the encoding given in Fig. 7 can be extended following expected lines.

Constructs for non-deterministic behaviors have fairly straightforward encodings:

In \([\!\![\mathtt {SOME}\,?\, {z};e]\!\!]_{y,x}\), notice that typing ensures that e does not return a value; also, set D enables to safely discard behaviors in e in the event of an exception. Given the conditions ensured by typing, the encoding of non-deterministic choices is unsurprising:

In essence, processes \(\mathsf {S}_{z}\) consume the (unit) value produced by \([\!\![e_1]\!\!]_{y}\) and \([\!\![e_2]\!\!]_{y}\) through z. The resulting processes can then be composed first in a non-deterministic choice, and then in an independent parallel composition with \([\!\![\mathsf {*}]\!\!]_{y}\). It would not be hard to extend this encoding to handle the general case in which \(e_1\) and \(e_2\) may raise exceptions and return values different from \(\mathbf {unit}\). To that end, typing should ensure that \(e_1\) and \(e_2\) are each typable in an abortable context (cf. Rule ), but also that the name representing the continuation to the enclosing exception handlers (i.e., x) is given an abortable type.

5 Further Related Work

In the purely functional (and sequential) programming setting, control operators have been given Curry-Howard interpretations in the context of classical logic [2, 26, 36]. To our best knowledge, this paper presents the first attempt at tackling state-aware concurrent programming features, involving linearity (our main focus herein), while building on a Curry-Howard interpretation of classical linear logic as session types. A very tentative sketch of some ideas behind this work was presented at Cardelli’s Fest [8]; here we provide a complete account of non-determinism and failure, introduce new computational primitives, present associated results, and provide non-trivial examples, including the typeful embedding of a realistic functional, concurrent language with exceptions.

The tensions between affinity, linearity and control effects have been widely investigated in different settings, and already referred in the introduction. The work [45] considers a form of affinity in stateful settings (including session types) and explores how to safely interface an affine language with a conventional one. We share several high level aims with [45], although following a fundamentally different approach, and obtaining results of different relevance; in particular, we consider a unified (concurrent) language that admits a fundamental Curry-Howard correspondence with linear logic, and offers strong guarantees by static typing such as deadlock-freedom. Within the session types literature, the interplay of session types and functional languages (including encodings of functional calculi) has received much attention (see, e.g., [25, 32, 35, 48]) but non-determinism/failure do not seem to have been addressed. The paper [35] relates effect and session type systems, but effects such as exceptions are not addressed. A work exploring affinity in session calculi is [34]. Existing works on exception mechanisms for session types impose severe syntactic restrictions to typable programs and/or do not ensure progress: this observation applies to models of interactional exceptions and interruptible sessions based on both binary sessions (cf. [15]) and multiparty sessions (cf. [14, 21]). Further work is required to connect our process model (based on binary session types) with multiparty structured interactions with exceptions/interruptions, following logic-based relationships between binary and multiparty session types [9].

Also related are [3, 17]. The work in [3] explores forms of non-determinism and failure via the conflation of additive connectives. This is quite different from our approach, which is based on a new pair of monadic/comonadic connectives, fully justified by a Curry-Howard interpretation and expressive enough to represent forms of affinity and exceptions. The work in [17] does have non-determinism at the level of processes, but its expressiveness is not analyzed, and non-determinism at the level of types is not addressed. In contrast, we provide types for non-determinism via specific connectives in the context of a Curry-Howard correspondence, and exploit the expressiveness of the non-deterministic process model by modeling a realistic functional language.

As explained in the introduction, a main aim of this work is not just to propose yet another point in the design space solution for exceptions, affinity, or linearity. Instead, we show how a small set of logically motivated primitives is expressive enough to model fairly general notions of (controlled) affinity and non-determinism in higher-order concurrent programs (including exception handling) while preserving all the fundamental properties of a Curry-Howard interpretation for linear logic. We leave for future work a deeper study of the expressiveness of our model, as exceptions and compensations are key programming abstractions in models of service-oriented computing (see, e.g., [23]).

6 Concluding Remarks

We have presented the first type system that accommodates non-deterministic and abortable behaviors within session-based concurrent programs while building on a Curry-Howard correspondence with linear logic. Conceptually simple, our approach conservatively extends classical linear logic with two dual modal connectives, related to linear logic exponentials, but that express non-determinism and failure rather than sharing.

We have shown that our type system enforces progress and session fidelity; its underlying operational semantics, based on Curry-Howard principles, is actually compatible with standard non-confluent formulations of internal non-determinism for process algebra, in the sense of our postponing result (Theorem 3.5). Our system is very expressive, as illustrated by several examples, including a typed embedding of a higher-order linear functional language with threads, sessions, non-determinism, and exceptions.

We have not discussed the presence of intuitionistic (unrestricted) types in the functional language of Sect. 4, as the main focus in the paper is on linearity and its challenging combination with non-determinism and failure. The combination of these ingredients with general (non-linear) functional values and shared sessions would be as expected, resulting from the type discipline of the interpretation of the exponentials in the basic model. Also, key properties of our type system such as strong normalization and confluence can be established along predictable lines [37]. A further advantage of our approach is its natural compatibility with other extensions to the basic framework, for example behavioral polymorphism [10]. Another interesting direction for future work is to better understand the behavioral equivalences induced by our interpretation.