Keywords

figure a
figure b

1 Introduction

In this work, we build on the linear logic based foundation for session types [13, 15, 72] to construct SAM, an abstract machine specially designed for executing session processes typed by (classical) linear logic \(\textsf{CLL}\). Although motivated by the session type discipline, which originally emerged in the realm of concurrency and distribution [28, 31, 33, 34], a basic motivation for designing the SAM was to provide an efficient deterministic execution model for the implicitly sequential session-typed program idioms that often proliferate in concurrent session-based programming. It is well-known that in a world of fine-grained concurrency, building on many process-based encodings of concepts such as (abstract) data types, functions, continuations, and effects [10, 49, 54, 65, 66, 68, 70], large parts of the code turn out to be inherently sequential, further justifying the foundational and practical relevance of our results. A remarkable feature of the SAM’s design is therefore its potential to efficiently coordinate sequential with full-fledged concurrent behaviours in session-based programming.

Leveraging early work relating linear logic with the semantics of linear and concurrent computation [1, 2, 6], the proposition-as-types (PaT) interpretation [73] of linear logic proofs as a form of well-behaved session-typed nominal calculus has motivated many developments since its inception  [5, 12, 67, 68]. We believe that, much how the \(\lambda \)-calculus is deemed a canonical typed model for functional (sequential) computation with pure values, the session calculus can be accepted as a fairly canonical typed model for stateful concurrent computation with linear resources, well-rooted in the trunk of “classical” Type Theory. The PaT interpretation of session processes also establishes a bridge between more classical theories of computation and process algebra via logic. It also reinstates Robin Milner’s view of computation as interaction [48], “data-as-processes” [49] and “functions-as-processes” [47], now in the setting of a tightly typed world, based on linear logic, where types may statically ensure key properties like deadlock-freedom, termination, and correct resource usage in stateful programs. Session calculi are motivating novel programming language design, bringing up new insights on typeful programming [18] with linear and behavioral types, e.g., [5, 20, 24, 61]. Most systems of typed session calculi have been formulated in process algebraic form [28, 31, 33], or on top of concurrent \(\lambda \)-calculi with an extra layer of communication channels (e.g., [29]), logically inspired systems such as the those discussed in this paper (e.g.,  [13, 15, 23, 27, 39, 59, 61, 72]) are defined by a logical proof / type system where proof rules are seen as witnesses for the typing of process terms, proofs are read as processes, structural equivalence is proof conversion and computation corresponds to cut reduction. These formulations provide a fundamental semantic foundation to study the model’s expressiveness and meta-theory, but of course do not directly support the concrete implementation of programming languages based on them.

Although several programming language implementations of nominal calculi based languages have been proposed for some time (e.g. [57]), with some introducing abstract machines as the underlying technology (e.g., [46, 69]), we are not aware of any prior design proposal for an abstract machine for reducing session processes exploiting deep properties of a source session calculus, as e.g., the CAM [21] the LAM [41], or the KM [40], which also explore the Curry-Howard correspondences, may reclaim to be, respectively for call-by-value cartesian-closed structures, linear logic, and the call-by-name \(\lambda \)-calculus.

The SAM reduction strategy explores a form of “asynchronous” interaction that essentially expresses that, for processes typed by the logical discipline, sessions are always pairwise causally independent, in the sense that immediate communication on some session is never blocked by communication on a different session. This property is captured syntactically by prefix commutation equations, valid commuting conversions in the underlying logic: adding equations for such laws explicitly to process structural congruence keeps observational equivalence of \(\textsf{CLL}\) processes untouched [53]. Combined with insights related to focalisation and polarisation in linear logic [4, 44, 56], we realize that all communication in any session may be operationally structured as the exchange of bundles of positive actions from sender to receiver, where the roles sender/receiver flip whenever the session type swaps polarity. Communication may then be mediated by message buffers, first filled up by the sender (“write-biased” scheduling), and at a later time emptied by the receiver. Building on these observations and on key properties of linear logic proofs leveraged in well-known purely structural proofs of progress [13, 15, 61], we identify a sequential and deterministic reduction strategy for \(\textsf{CLL}\) typed processes, based on a form of co-routining where continuations are associated to session queues, and “context switching” occurs whenever polarity flips. That such strategy works at all, preserving all the required correctness properties of the \(\textsf{CLL}\) language does not seem immediately obvious, given that each processes may sequentially perform multiple actions on many different sessions, meaning that multiple context switches must be interleaved. The bulk of our paper is then devoted to establishing all such properties in a precise technical sense. We believe that the SAM may provide a principled foundation for safe execution environments for programming languages combining functional, imperative and concurrent idioms based on session and linear types, as witnessed in practice for Rust [37], (Linear) Haskell [8, 38, 45], Move [9], and in research languages [24, 36, 60]. To further substantiate these views we have developed an implementation of the SAM, integrated in a language for realistic session-based shared-state programs [17].

Outline and Contributions. In Section 2 we briefly review the session-typed calculus \(\textsf{CLL}\), which exactly corresponds to (classical) Linear Logic with mix. In Section 3 we discuss the motivation and design principles of the core SAM, gradually presenting its structure for the language fragment corresponding to session types without the exponentials, which will be introduced later. Even if the core SAM structure and transition rules are fairly simple, the proofs of correctness are more technically involved, and require progressive build up. Therefore, we first bridge between \(\textsf{CLL}\) and SAM via a intermediate logical language \(\textsf{CLLB}\), introducing explicit queues in cuts, presented in Section 4. We show preservation (Theorem 4.1) and progress (Theorem 4.2) for \(\textsf{CLLB}\), and prove that there is two way simulation between \(\textsf{CLLB}\) and \(\textsf{CLL}\) via a strong operational correspondence (Theorem 4.3). Given this correspondence, in Section 5 we state and prove the adequacy of the SAM for executing \(\textsf{CLL}\) processes, showing soundness wrt. \(\textsf{CLLB}\) (Theorem 5.1) and \(\textsf{CLL}\) (Theorem 5.2), and progress / deadlock absence (Theorem 5.3). In Section 6 modularly extend the previous results to the exponentials and mix, and revise the core SAM by introducing explicit environments, stating the associated adequacy results (Theorem 6.1 and Theorem 6.2). We also discuss how to accommodate concurrency, and other extensions in the SAM. We conclude by a discussion of related work and additional remarks. Additional definitions and proofs can be found in the companion technical report [16].

2 Background on \(\textsf{CLL}\), the core language and type system

We start by revisiting the language and type system of \(\textsf{CLL}{}\), and its operational semantics. The system is based on a PaT interpretation of classical linear logic (we follow the presentations of [11, 15, 60]).

Definition 2.1

(Types). Types AB are defined by

figure c

Types comprise of the units (\({\textbf{1}}\), \(\bot \)), multiplicatives (\(\otimes \), ), additives (\(\oplus _{\ell \in L}{A_\ell }\), ) and exponentials (!, ?). We adopt here a labeled version of the additives, where the linear logic sum type \(A_{\# \textsf{inl}}\oplus A_{\# \textsf{inr}}\) is defined by \(\oplus _{\ell \in \{\# \textsf{inl},\# \textsf{inr}\}}{A_\ell }\). The positive types are \({\textbf{1}}\), \(\otimes \), \(\oplus \), and !, while the negative types are \(\bot \), , and ?. We abbreviate by \(A\multimap B\). We write \(A^{+}\) (resp. \(A^{-}\)) to assert that A is a positive (resp. negative) type. Type duality \(\overline{A}\) corresponds to negation:

figure i

Duality captures the symmetry of behaviour in binary process interaction, as manifest in the cut rule.

Definition 2.2

(Processes). The syntax of processes PQ is given by:

figure j

Typing judgements have the form \(P \vdash \varDelta ; \varGamma \), where P is a process and the typing context \(\varDelta ;\varGamma \) is dyadic [4, 7, 13, 55]: both \(\varDelta \) and \(\varGamma \) assign types to names, the context \(\varDelta \) is handled linearly (no implicit contraction or weakening) while the exponential context \(\varGamma \) is unrestricted. The type system exactly corresponds, via a propositions-as-types correspondence, to the canonical proof system of Classical Linear Logic with Mix. When a cut type annotation is easily inferred, we may omit it and write . The typing rules of \(\textsf{CLL}{}\) are given in Fig. 1.

The process denotes the inactive process, typed in the empty linear context (rule [T]). denotes independent parallel composition of processes P and Q (rule [Tmix]), whereas denotes interfering parallel composition of P and Q, where P and Q share exactly one channel name x, typed as A in P and \(\overline{A}\) in Q (rule [Tcut]). The construct captures forwarding between dually typed names x and y (rule [Tfwd]), which operationally consists in (globally) renaming x for y.

Processes and denote session termination and the dual action of waiting for session termination, respectively (rules [T\({\textbf{1}}\)] and [T\(\bot \)]). The constructs and \(\# \textsf{l}\ x;P\) denote label input and output, respectively, where the input construct pattern matches on the received label to select the process continuation that is to run. Process and codify the output of (fresh) name y on channel x and the corresponding input action, where the received name will be substituted for z in Q (rules [T\(\otimes \)] and [T]). Typing ensures that the names used in \(P_1\) and \(P_2\) are disjoint.

Processes , and embody replicated servers and client processes. Process consists of a process that waits for inputs on x, spawning a replica of P (depending on no linear sessions – rule [T!]). Process and allow for replicated servers to be activated and subsequently used as (fresh) linear sessions (rules [T?] and [Tcall]). Composition of exponentials is achieved by the process, where P cannot depend on linear sessions and so may be safely replicated.

Fig. 1.
figure 1

Typing Rules of \(\textsf{CLL}\).

We call action any process that is either a forwarder or realizes an introduction rule, and denote by \(\mathcal {A}\) the set of all actions, by \(\mathcal {A}(x)\) the set of action with subject x (the subject of an action is the channel name in which it interacts [49]). An action is deemed positive (resp. negative) if its associated type is positive (resp. negative) in the sense of focusing. The set of positive (resp. negative) actions is denoted by \(\mathcal {A}^+\) (resp. \(\mathcal {A}^-\)). We sometimes use, e.g., \(\mathcal A\) or \(\mathcal {A}^+(x)\) to denote a process in the set. The \(\textsf{CLL}\) operational semantics is given by a structural congruence relation \(\equiv \) that captures static identities on processes, corresponding to commuting conversions in the logic, and a reduction relation \(\rightarrow \) that captures process interaction, and corresponds to cut-elimination steps.

Fig. 2.
figure 2

Structural congruence \(P \equiv Q\).

Definition 2.3

(\(P \equiv Q\)). Structural congruence \(\equiv \) is the least congruence on processes closed under \(\alpha \)-conversion and the \(\equiv \)-rules in Fig. 2.

The definition of \(\equiv \) reflects expected static laws, along the lines of the structural congruences / conversions in [13, 71]. The binary operators forwarder, cut, and mix are commutative. The set of processes modulo \(\equiv \) is a commutative monoid with operation the parallel composition and identity given by inaction ([par]). Any static constructs commute, as expressed by the laws [CM]-[C!sC!]. The unrestricted cut distributes over all the static constructs by law [C*], where stands for either a mix, linear or unrestricted cut. The laws [C\(+ *\)] and [C\(+\)] denote sound proof equivalences in linear logic and bring explicit the independence of linear actions ( noted a(x)), in different sessions x [53]. These conversions are not required to obtain deadlock freedom. However, they are necessary for full cut elimination (e.g., see [71]), and expose more redexes, thus more non-determinism in the choice of possible reductions. Perhaps surprisingly, this extra flexibility is important to allow the deterministic sequential evaluation strategy for \(\textsf{CLL}\) programs adopted by the SAM to be expressed.

Definition 2.4

(Reduction \(\rightarrow \)). Reduction \(\rightarrow \) is defined by the rules of Fig. 3.

We denote by \(\Rightarrow \) the reflexive-transitive closure of \(\rightarrow \). Reduction includes the set of principal cut conversions, i.e. the redexes for each pair of interacting constructs. It is closed by structural congruence ([\(\equiv \)]), in rule [cong] we consider that \(\mathcal C\) is a static context, i.e. a process context in which the single hole is covered only by the static constructs mix or cut. The forwarding behaviour is implemented by name substitution [fwd] [14]. All the other reductions act on a principal cut between two dual actions, and eliminate it on behalf of cuts involving their subprocesses.

Fig. 3.
figure 3

Reduction \(P \rightarrow Q\).

\(\textsf{CLL}\) satisfies basic safety properties [13] listed below, and also confluence, and termination [60, 61]. In particular we have:

Theorem 2.1

(Type Preservation). Let \(P \vdash \varDelta ; \varGamma \). (1) If \(P \equiv Q\), then \(Q \vdash \varDelta ; \varGamma \). (2) If \(P \rightarrow Q\), then \(Q \vdash \varDelta ; \varGamma \).

A process P is live if and only if \(P = \mathcal C[Q]\), for some static context \(\mathcal C\) (the hole lies within the scope of static constructs mix and cut) and Q is an active process (a process with a topmost action prefix).

Theorem 2.2

(Progress). Let \(P \vdash \emptyset ; \emptyset \) be live. Then \(P\rightarrow Q\) for some Q.

3 A Core Session Abstract Machine

In this section we develop the key insights that guide the construction of our session abstract machine (SAM) and introduce its operational rules in an incremental fashion. We omit the linear logic exponentials for the sake of clarity of presentation, postponing their discussion for Section 6.

One of the main observations that drives the design of the SAM is the nature of proof dynamics in (classical) linear logic, and thus of process execution dynamics in the \(\textsf{CLL}\) system of Section 2. The proof dynamics of linear logic are derived from the computational content of the cut elimination proof, which defines a proof simplification strategy that removes (all) instances of the cut rule from a proof. However, the strategy induced by cut elimination is non-deterministic insofar as multiple simplification steps may apply to a given proof. Transposing this observation to \(\textsf{CLL}\) and other related systems, we observe that their operational semantics is does not prescribe a rigid evaluation order for processes. For instance, in the process , reduction is allowed in both P and Q. This is of course in line with reduction in process calculi (e.g., [49]). However, in logical-based systems this amounts to don’t care non-determinism since, regardless of the evaluation order, confluence ensures that the same outcomes are produced (in opposition to don’t know non-determinism which breaks confluence and is thus disallowed in purely logical systems). The design of the SAM arises from attempting to fix a purely sequential reduction strategy for \(\textsf{CLL}\) processes, such that only one process is allowed to execute at any given point in time, in the style of coroutines. To construct such a strategy, we forego the use of purely synchronous communication channels, which require a handshake between two concurrently executing processes, and so consider session channels as a kind of buffered communication medium (this idea has been explored in the context of linear logic interpretations of sessions in [25]), or queue, where one process can asynchronously write messages so that another may, subsequently, read. To ensure the correct directionality of communication, the queue has a write endpoint (on which a process may only write) and a read endpoint (along which only reads may be performed), such that at any given point in time a process can only hold one of two endpoints of a queue. Moreover, our design takes inspiration from insights related to polarisation and focusing in linear logic, grouping communication in sequences of positive (i.e. write) actions.

Fig. 4.
figure 4

core SAM Components

Allowing session channels to buffer message sequences, we may then model process execution by alternating between writer processes (that inject messages into the respective queues) and corresponding reader processes. Thus, the SAM must maintain a heap that tracks the queue contents of each session (and its endpoints), as well as the suspended processes. The construction of the core of the SAM is given in Figure 4. An execution state is simply a pair consisting of the running process P and the heap H. For technical reasons that are made clear in Sections 4 and 5, the process language used in the SAM differs superficially from that of \(\textsf{CLL}\), but for the purposes of this overview we will use \(\textsf{CLL}\) process syntax. Later we show the two languages are equivalent in a strong sense.

A heap is a mapping between session identifiers and session records of the form \(x \langle q, Q\rangle y\), denoting a session with write endpoint x and read endpoint y, with queue contents q and a suspended process Q, holding one of the two endpoints. If Q holds the read endpoint then it is suspended waiting for the process holding the write endpoint to fill the queue with data for it to read. If Q holds the write endpoint, then Q has been suspended after filling the queue and is now waiting for the reader process on y to empty the queue.

We adopt the convention of placing the write endpoint on the left and the read endpoint on the right. In general, session records in the SAM support a form of coroutines through their contained processes, which are called on and returned from multiple times over the course of the execution of the machine. A queue can either be empty () or holding a sequence of values. A value is either a close session token (\(\checkmark \)), identifying the last output on a session; a choice label \(\# \textsf{l}\) or a process closure \(\textsf{clos}(x,P)\), used to model session send and receive. We overload the @ notation to also denote concatenation of queues.

Cut. We begin by considering how to execute a cut of the form where x is a positive type (in the sense of polarized logic [30]) in P. A positive type corresponds to a type denoting an output (or write) action, whereas a negative type denotes an input (or read) action. We maintain the invariant that in such a cut, P holds the write endpoint and Q the read endpoint. This means that the next action performed by P on the session will be to push some value onto the queue and, dually, the next action performed by Q on the session will be to read a value from the queue. In general, the holder of the write and read endpoint can change throughout execution.

Given the choice of either scheduling P or Q, we are effectively forced to schedule P before Q. Given that the cut introduces the (unique) session that is shared between the two processes, the only way for Q to exercise its read capability on the session successfully is to wait for P to have exercised (at least some of) its write capability. If we were to schedule Q before P, the process might attempt to read a value from an empty queue, resulting in a stuck state of the SAM. Thus, the SAM execution rule for cut is:

figure aj

The rule states that P is the process that is to be scheduled, adding the session record to the heap, which effectively suspends the execution of Q until P has exercised some of its write capabilities on the new session. Note that, in general, both P and Q can interact along many different sessions as both readers and writers before exercising any action on x (resp. y). However, they alone hold the freshly created endpoints x and y and so the next value sent along the session must come from P and Q is its intended receiver.

Channel Output. To execute an output of the form in the SAM we simply lookup the session record for x and add to the queue a process closure containing R (which interacts along z), continuing with the execution of Q:

figure am

Note that the SAM eagerly continues to execute Q instead of switching to P, the holder of the read endpoint of the queue. This allows for the running process to perform all available writes before a context switch occurs.

Session Closure. Executing a follows a similar spirit, but no continuation process exists and so execution switches to the process P holding the read endpoint y of the queue:

figure ao

The process P will eventually read the termination mark from the queue (triggering the deallocation of the session record from the heap):

figure ap

Note the requirement that \(\checkmark \) be the final element of the queue.

Negative Action on Write Endpoint. As hinted above for the case of executing a , the SAM has a kind of write bias insofar as the process chosen to execute in a cut is that which holds the write endpoint for the newly created session. Since \(\textsf{CLL}\) processes use channels bidirectionally, the role of writer and reader on a channel (and thus the holder of the write and read endpoints of the queue) may be exchanged during execution. For instance, a process P may wish to send a value v to Q and then receive a response on the same channel. However, when considering a queue-based semantics, the execution of the input action must not obtain the value v, intended for Q. Care is therefore needed to ensure that v is received by the holder of the read endpoint of the queue before P is allowed to execute its input action (and so taking over the read endpoint). This notion is captured by the following rule, where \(\mathcal {A}^-\) denotes any process performing a negative polarity action (i.e., a , , or, as we discuss later, a when x is a write endpoint with a negative polarity type):

figure av

If the executing process is to perform a negative polarity action on a write endpoint x, the SAM context switches to Q, the holder of the read endpoint y of the session, and suspends the previously running process. This will now allow for Q to perform the appropriate inputs before execution of the action \(\mathcal {A}^-\) resumes.

Channel Input. The rules for actions are as follows:

figure ax

where . The execution of an input action requires the corresponding queue to contain a process closure, denoting the process that interacts along the received channel w. In order to ensure that no inputs attempt to read from an empty queue, we must branch on the polarity of the communicated session (written \(w{:}+\) and \(w{:}-\) in the rules above): if the session has a positive type, then Q must take the write endpoint w of the newly generated queue (since Q uses the session with a dual type) and thus we execute Q and allocate a session record in the heap for the new session, with read endpoint z; if the exchanged session has a negative type, the converse holds and Q must take the read endpoint of the newly generated queue. In this scenario, we must execute R so that it may exercise its write capability on the queue and suspend Q in the new session record.

In either case, the session record for the original session is updated by removing the received message from the queue. Crucially, since processes are well-typed, if the resulting queue is empty then it must be the case that Q has no more reads to perform on the session, and so we swap the read and write endpoints of the session. This swap serves two purposes: first, it enables Q to perform writes if needed; secondly, and more subtly, it allows for the process, say, P, that holds the other endpoint of the queue to be resumed to perform its actions accordingly. To see how this is the case, consider that such a process will be suspended (due to rule [S\(- \)]) attempting to perform a negative action on the write endpoint of the queue. After the swap, the endpoint of the suspended process now matches its intended action. Since Q now holds the write endpoint, it will perform some number of positive actions on the session which end either in a , which context switches to P, or until it attempts to perform a negative action on the write endpoint, triggering rule [S\(- \)] and so context switching to P.

Choice and Selection. The treatment of the additive constructs in the SAM is straightforward:

figure ba

Sending a label \(\#\textsf{l} \) simply adds the \(\#\textsf{l} \) to the corresponding queue and proceeds with the execution, whereas executing a reads a label from the queue and continues execution of the appropriate branch. Since removing the label may empty the queue, we perform the same adjustment as in rules and .

Forwarding. Finally, let us consider the execution of a forwarder (we overload the @ notation to also denote concatenation of queues):

figure be

A forwarder denotes the merging of two sessions x and y. Since the forwarder holds the read and write endpoints x and y, respectively, Q has written (through z) the contents of \(q_1\), whereas the previous steps of the currently running process have written \(q_2\). Thus, P is waiting to read \(q_2 @ q_1\), justifying the rule above.

The reader may then wonder about other possible configurations of the SAM heap and how they interact with the forwarder. Specifically, what happens if y is of a positive type but a read endpoint of a queue, or, dually, if x is of a negative type but a write endpoint. The former case is ruled out by the SAM since the heap satisfies the invariant that any session record of the form \(x{:}A \langle q,P\rangle y{:}A\in H\) is such that A must be of negative polarity or P is the inert process (which cannot be forwarded). The latter case is possible and is handled by rule [S\(- \)], since such a forward stands for a process that wants to perform a negative polarity action on a write endpoint (or a positive action on a read endpoint).

3.1 On the Write-Bias of the SAM

Consider the following \(\textsf{CLL}\) process:

figure bg

Let us walk through the execution trace of P:

figure bh

The SAM begins in the state on line (1) above, executing the cut. Since the type of a is positive, we execute \(P_1\), and allocate the session record, suspending \(Q_1\), resulting in the state on line (2). Since \(P_1\) is a write action on a write endpoint, we proceed via the [S\(\otimes \)] rule, resulting in the SAM configuration in line (3), executing \(P_3\) and adding a closure containing \(P_2\) to the session queue with write endpoint a. Executing \(P_3\) (3), a action, requires adding the \(\checkmark \) to the queue and context switching to the process \(Q_1\), now ready to receive the sent value. The applicable rule is now (4) , and so execution will context switch to \(P_2\) after creating the session record for the new session with endpoints y and x. \(P_2\) will execute and the machine ends up in state (6) followed by (7), which consume the appropriate \(\checkmark \) and deallocate the session records.

Note how after executing the send action of \(P_1\) we eagerly execute the positive action in \(P_3\) rather than context switching to \(Q_1\). While in this particular process it would have been safe to execute the negative action in \(Q_1\), switch to \(P_2\) and then back to \(Q_2\), we would now need to somehow context switch to \(P_3\) before continuing with the execution of \(Q_3\), or execution would be stuck. However, the relationship between \(P_3\) and \(Q_2\) is unclear at best. Moreover, if the continuation of \(Q_1\) were of the form , the context switch after the execution of \(P_2\) would have to execute \(P_3\), or the machine would also be in a stuck state.

3.2 Illustrating Forwarding

To better illustrate the way in which effectively stands for a negative action, consider the following \(\textsf{CLL}\) process (to simplify the execution trace we assume the existence of output and input of integers typed as \(\textsf{int} \otimes A\) and , respectively, eliding the need for process closures in this example):

figure bn

If we consider the execution of P we observe:

figure bo

The first four steps of the execution of P allocate the two session records and the writes by \(Q_1\) and \(Q_2\) takes place. We are now in configuration (5), where is to execute and a is a write endpoint of a queue assigned a negative type (). This forwarder stands for a process performing a negative action on a write endpoint (i.e., \(P_1\)) and so context switching is required, rule [S[1.0]\(-\)] applies and the SAM context switches to \(R_1\), suspending \(Q_3\) until the forward can be performed. After \(R_1\) receives (6) and the queue endpoints a and d are swapped (7), \(R_2\) executes and then rule [S[1.0]\(-\)] applies (8), context switching back to \(Q_3\). Since the queue endpoints are now flipped, rule [Sfwd] now applies (9), collapsing the two session records (via queue concatenation) and proceeding with the execution of \(P_1\), \(P_2\), \(P_3\) and \(R_3\) (10-14). Note the correct ordering in which the sent values are dequeued, where 3 is read before 2, as intended.

Discussion. The core execution rules for the SAM are summarized in Figure 5. At this point, the reader may wonder just how reasonable the SAM’s evaluation strategy is. Our evaluation strategy is devised to be a deterministic, sequential strategy, where exactly one process is executing at any given point in time, supported by a queue-based buffer structure for channels and a heap for session records. Moreover, taking inspiration from focusing and polarized logic, we adopt a write-biased stance and prioritize (bundles of) write actions over reads, where suspended processes hold the read endpoint of queues while waiting for the writer process to fill the queue, and hold write endpoints of queues after filling them, waiting for the reader process to empty the queue.

While this latter point seems like a reasonable way to ensure that inputs never get stuck, it is not immediately obvious that the strategy is sound wrt the more standard (asynchronous) semantics of \(\textsf{CLL}\) and related languages, given that processes are free to act on multiple sessions. Thus, the write-bias of the cut rule (and the overall SAM) does not necessarily mean that the process that is chosen to execute will immediately perform a write action on the freshly cut session x. In general, such a process may perform multiple write or read actions on many other sessions before performing the write on x, meaning that multiple context switches may occur. Given this, it is not obvious that this strategy is adequate insofar as preserving the correctness properties of \(\textsf{CLL}\) in terms of soundness, progress and type preservation. The remainder of this paper is devoted to establishing this correspondence in a precise technical sense.

Fig. 5.
figure 5

The core SAM Transition Rules

4 \(\textsf{CLLB}\): A Buffered Formulation of \(\textsf{CLL}\)

There is a substantial gap between the language \(\textsf{CLL}\), presented in an abstract algebraic style, and its operational semantics, defined by equational and rewriting systems, and an abstract machine as the SAM, a deterministic state machine manipulating several low level structures. Therefore, even if the core SAM structure and transition rules are fairly simple, proving its correctness is more challenging and technically involved, and require progressive build up. Therefore, we first bridge between \(\textsf{CLL}\) and SAM via a intermediate logical language \(\textsf{CLLB}\), which extends \(\textsf{CLL}\) with a buffered cut construct.

Fig. 6.
figure 6

Additional typing rules for \(\textsf{CLLB}\).

figure br

The buffered cut construct models interaction via a “message queue" with two polarised endpoints a and b, held respectively by the processes P and Q. A polarised endpoint has the form x or \(\overline{x}\). The endpoint marked \(\overline{x}\) is the only allowing writes, the unmarked y is the only one allowing reads, exactly one of the two endpoints is marked. The endpoints types AB are of course related but do not need to be exact duals, the type of the writer endpoint may be advanced in time wrt the type of the reader endpoint, reflecting the messages already enqueued but not yet consumed. If the queue is empty, we have \(A = \overline{B}\). Thus a buffered cut with empty queue corresponds to the basic cut of \(\textsf{CLL}\).

figure bs

The queue q stores values V defined by

figure bt
figure bu

We use @ to also denote (associative) concatenation operation of queues, with unit . Enqueue and dequeue operations occur respectively on the lhs and rhs.

The type system \(\textsf{CLLB}\) is obtained from \(\textsf{CLL}\) by replacing [TCut] with the typing rules (and symmetric ones) in Fig. 6. We distinguish the type judgements as \(P\vdash \varDelta ; \varGamma \) for \(\textsf{CLL}\) and \(P \vdash ^{\textsf{B}}\varDelta ; \varGamma \) for \(\textsf{CLLB}\). The [TCutB] rule sets the endpoints mode based in the cut type polarity, applicable whenever the queue is empty. The remaining rules relate queue contents with their corresponding (positive action) processes. For instance, rule [Tcut-\(\otimes \)] can be read bottom-up as stating that typing processes mediated by a queue containing a process closure amounts to typing the process that will emit the session y (bound to R), interacting with the queue with the closure removed. Rules [Tcut-\(\oplus \)] and [Tcut!] apply a similar principle to the other possible queue contents. In [Tcut-\({\textbf{1}}\)] and [Tcut!] the write endpoint is typed \(\emptyset \), as the sender has terminated ().

Fig. 7.
figure 7

Additional structural congruence rules for \(\textsf{CLLB}\).

Structural congruence for \(\textsf{B}\) (noted \(\equiv ^\textsf{B}\)) is obtained by extending \(\equiv \) with commutative conversions for the buffered cut, listed in Fig. 7. The following provisos apply: [CM] \(y \in \textsf{fn}(Q)\); in [CC] \(y,z \in \textsf{fn}(Q)\); in [CC!] \(x \notin \textsf{fn}(P)\). Accordingly, reduction for \(\textsf{B}\) (noted \(\rightarrow ^\textsf{B}\)) is obtained by replacing the \(\rightarrow \) rules [fwd], [\({\textbf{1}}\bot \)], [ and [ by the rules in Fig. 8. Essentially each principal cut reduction rule of \(\textsf{CLL}\) is replaced by a pair of “positive” (\(\rightarrow _p\)) / “negative” (\(\rightarrow _n\)) reduction rules that allow processes to interact asynchronously via the queue, that is, positive process actions (corresponding to positive types) are non-blocking. For example, the rule [\(\otimes \)] for send appends a session closure to the tail of the queue (rhs) and the rule for receive pops a session closure from the head of the queue (lhs). Notice that positive rules are enabled only if the relevant endpoint is in write mode (\(\overline{x}\)), and negative rules are enabled only if the relevant endpoint is in read mode (y). In [] above the target cuts endpoint polarities depends on the types of the composed processes. To uniformly express the appropriate marking of endpoint polarities we define some convenient abbreviations:

Definition 4.1

(Setting polarities).

figure cb
Fig. 8.
figure 8

Reduction \(P \rightarrow ^\textsf{B} Q\).

The following definition then formalizes the intuition given above about how to encode processes of \(\textsf{CLL}\) into processes of \(\textsf{CLLB}\).

Definition 4.2

(Embedding). Let \(P\vdash \varDelta ;\varGamma \). \(P^\dagger \) is the \(\textsf{B}\) process such that

figure cc

homomorphically defined in the remaining constructs. Clearly \(P^\dagger \vdash ^{\textsf{B}} \varDelta ; \varGamma \).

4.1 Preservation and Progress for \(\textsf{CLLB}\)

In this section, we prove basic safety properties of \(\textsf{CLLB}\): Preservation (Theorem 4.1) and Progress (Theorem 4.2). To reason about type derivations involving buffered cuts, we formulate some auxiliary inversion principles that allow us, by aggregating sequences of application of [TCut-\(*\)] rules of \(\textsf{CLLB}\), to talk in a uniform way about typing of values in queues and typing of processes connected by queues. To assert typing of queue values c we use judgments the form \(\varGamma ;\varDelta \vdash c{:}E\), where E is a either a type or a one hole type context, defined by

figure cd

where in only branch type \(E_{\# \textsf{l}}\) for some selected label \(\# \textsf{l}\in L\) is a one hole context (to plug the continuation type); only the branch chosen by the selected label in a queue is relevant to type next queue values. We identify the selected branch in the type by tagging it with the corresponding label \(\# \textsf{l}\) thus . We then introduce the following typing rules for queue values.

Definition 4.3

(Typing of Queue Values).

figure cg

Given a sequence of k one hole queue value types \(E_i\) and a type A, we denote by \(\textbf{E}_k;A\) the type \(E_1[E_2[...E_k[A]]]\). Queue value types allow us to talk in a uniform way about the type a receiver processes compatible with the types of enqueued values, as characterized by the following Lemma 4.1 and Lemma 4.2.

Lemma 4.1

(Non-full). For the rule below is admissible and invertible:

figure ci

Notice that a session type, as defined by a \(\textsf{CLL}\) proposition, may terminate in either \({\textbf{1}}\), \(\bot \) or an exponential type !A/?A. We then also have

Lemma 4.2

(Full). The proof rules below are admissible:

figure cj
figure ck

Moreover, one of them must apply for inverting the judgment in the conclusion.

Theorem 4.1

(Preservation). Let \(P \vdash ^{\textsf{B}} \varDelta ; \varGamma \).

(1) If \(P \equiv ^\textsf{B} Q\), then \(Q \vdash ^{\textsf{B}} \varDelta ; \varGamma \). (2) If \(P \rightarrow ^\textsf{B} Q\), then \(Q \vdash ^{\textsf{B}} \varDelta ; \varGamma \).

A process P is live if and only if \(P = \mathcal C[Q]\), for some static context \(\mathcal C\) (the hole lies within the scope of static constructs mix, cut) and Q is an action process. We first show that a live process either reduces or offers an interaction on a free name. The observability predicate defined in Fig. 9 (cf. [63]) characterises interactions of a process with the environment.

Fig. 9.
figure 9

Observability Predicate \(P\downarrow _{x}\).

Lemma 4.3

(Liveness). Let \(P \vdash ^{\textsf{B}}\varDelta ; \varGamma \) be live. Either \(P\downarrow _{x}\) or \(P\rightarrow ^\textsf{B}\).

Theorem 4.2

(Progress). Let \(P \vdash \emptyset ; \emptyset \) be a live process. Then, \(P\rightarrow ^\textsf{B}\).

4.2 Correspondence between \(\textsf{CLL}\) and \(\textsf{CLLB}\)

In this section we establish the correspondence between reduction in \(\textsf{CLL}\) and \(\textsf{CLLB}\), proving that the two languages simulate each other in a tight sense. Intuitively, the correspondence shows that \(\textsf{CLLB}\) allows some positive actions to be buffered ahead of reception, while in \(\textsf{CLL}\) a single positive action synchronises with the corresponding dual in one step, or a forward reduction takes place.

We write a reduction \(P{\rightarrow ^\textsf{B}}Q\) as \(P{{\mathop {\rightarrow }\limits ^{}}{\!\!\!\;}^{\textsf{B}p}\;} Q\) if the reduced action is positive, \(P{{\mathop {\rightarrow }\limits ^{}}{\!\!\!\;}^{\textsf{B}n}\;} Q\) if the reduced action is negative (we consider [call] negative), \(P{{\mathop {\rightarrow }\limits ^{}}{\!\!\!\;}^{\textsf{B}a}}\;{} Q\) if the reduced action is a forwarder, and \(P{{\mathop {\rightarrow }\limits ^{}}{\!\!\!\;}^{\textsf{Bap}}}\;{} Q\) if the reduced action is positive or a forwarder. We also write \(P\rightarrow ^\textsf{Br}{}Q\) for positive action followed by a matching negative action on the same cut with an initially empty queue.

Lemma 4.4

The following commutations of reductions hold.

  1. 1.

    Let \(P_1 {{\mathop {\rightarrow }\limits ^{}}{\!\!\!\;}^{\textsf{B}p}\;} S {{\mathop {\rightarrow }\limits ^{}}{\!\!\!\;}^{\textsf{B}n}}\;P_2 \). Either \(P_1 \rightarrow ^\textsf{Br}{} P_2 \), or \(P_1 {{\mathop {\rightarrow }\limits ^{}}{\!\!\!\;}^{\textsf{B}n}}\;S' {{\mathop {\rightarrow }\limits ^{}}{\!\!\!\;}^{\textsf{B}p}\;} P_2\) for some \(S'\).

  2. 2.

    Let \(P_1 {{\mathop {\rightarrow }\limits ^{}}{\!\!\!\;}^{\textsf{B}a}}\;{} S {{\mathop {\rightarrow }\limits ^{}}{\!\!\!\;}^{\textsf{B}n}}\;P_2 \). Then \(P_1 {{\mathop {\rightarrow }\limits ^{}}{\!\!\!\;}^{\textsf{B}n}}\;S' {{\mathop {\rightarrow }\limits ^{}}{\!\!\!\;}^{\textsf{B}a}}\;{} P_2\) for some \(S'\).

  3. 3.

    If \(P_1 {{\mathop {\rightarrow }\limits ^{}}{\!\!\!\;}^{\textsf{Bap}}}\;{} S {{\mathop {\rightarrow }\limits ^{}}{\!\!\!\;}^{\textsf{B}n}}\;P_2 \), either \(P_1 \rightarrow ^\textsf{Br}{} P_2 \), or \(P_1 {{\mathop {\rightarrow }\limits ^{}}{\!\!\!\;}^{\textsf{B}n}}\;S' {{\mathop {\rightarrow }\limits ^{}}{\!\!\!\;}^{\textsf{Bap}}}\;{} P_2\) for some \(S'\).

  4. 4.

    Let \(P_1 {{\mathop {\rightarrow }\limits ^{}}{\!\!\!\;}^{\textsf{Bap}}}\;{} N {{\mathop {\Rightarrow }\limits ^{\epsilon }}{\!\!\!\;}^{\textsf{B}a}}\; S \rightarrow ^\textsf{Br}{} P_2 \). Either \(P_1 {{\mathop {\rightarrow }\limits ^{\epsilon }}{\!\!\!\;}^{\textsf{B}a}}\; N\) or \(P_1 \rightarrow ^\textsf{Br}{} S' {{\mathop {\rightarrow }\limits ^{}}{\!\!\!\;}^{\textsf{Bap}}}\;{} P_2 \) for some \(S'\).

Lemma 4.5

(Simulation). Let \(P\vdash \emptyset ;\emptyset \). If \(P \rightarrow Q\) then \(P^\dagger \Rightarrow ^\textsf{B}Q^\dagger \).

Proof

Each cut reduction of \(\textsf{CLL}\) is either simulated by two reduction steps of \(\textsf{B}\) in sequence or by a [fwd] reduction.

The following lemma identifies that in \(\textsf{CLLB}\), a sequence of positive actions (or forwards) followed by a negative action can always be commuted either by pulling out the negative action first, followed by the sequence of positive actions and forwards; having the negative action follow a positive action on the same channel and then performing the remaining actions; or by first performing a sequence of forward actions, the output and input on the relevant session and then the remaining actions.

Lemma 4.6

(Simulation). Let \(P \vdash ^{\textsf{B}}\emptyset ; \emptyset \). If \(P {{\mathop {\Rightarrow }\limits ^{}}{\!\!\!\;}^{\textsf{Bap}}}\;{} {{\mathop {\rightarrow }\limits ^{}}{\!\!\!\;}^{\textsf{B}n}}\;{} Q\) then (1) \( P {{\mathop {\rightarrow }\limits ^{}}{\!\!\!\;}^{\textsf{B}n}}\;R\) and \(R {{\mathop {\Rightarrow }\limits ^{}}{\!\!\!\;}^{\textsf{Bap}}}\;{} Q\) for some R, or; (2) \( P \rightarrow ^\textsf{Br}{} R\) and \(R {{\mathop {\Rightarrow }\limits ^{}}{\!\!\!\;}^{\textsf{Bap}}}\;{} {\;} Q\) for some R, or; (3) \( P {{\mathop {\Rightarrow }\limits ^{\epsilon }}{\!\!\!\;}^{\textsf{B}a}}\; \rightarrow ^\textsf{Br}{} R \) and \(R {{\mathop {\Rightarrow }\limits ^{}}{\!\!\!\;}^{\textsf{Bap}}}\;{} Q\) for some R.

Theorem 4.3

(Operational correspondence \(\textsf{CLL}\)-\(\textsf{CLLB}\)). Let \(P \vdash \emptyset ; \emptyset \).

  1. 1.

    If \(P\Rightarrow R\) then \(P^\dagger \Rightarrow ^\textsf{B} R^\dagger \).

  2. 2.

    If \(P^\dagger ({{\mathop {\Rightarrow }\limits ^{}}{\!\!\!\;}^{\textsf{Bap}}}\;{} {{\mathop {\rightarrow }\limits ^{}}{\!\!\!\;}^{\textsf{B}n}}\;)^* Q\) then there is R such that \(P\Rightarrow R\) and \(R^\dagger {{\mathop {\Rightarrow }\limits ^{}}{\!\!\!\;}^{\textsf{Bap}}}\;{} Q\).

Due to the progress property for \(\textsf{CLLB}\) (Theorem  4.2) and because queues are bounded by the size of positive/negative sections in types, after a sequence of positive or forwarder reductions a negative reduction consuming a queue value must occur. Theorem 4.3(2) states that every reduction sequence in \(\textsf{CLLB}\) is simulated by a reduction sequence in \(\textsf{CLL}\) up to some anticipated forwarding and buffering of positive actions. Our results imply that every reduction path in \(\textsf{CLLB}\) maps to a reduction path in \(\textsf{CLL}\) in which every negative reduction step in the former is mapped, in order, to a cut reduction step in the latter.

5 Correctness of the core SAM

We now prove that every execution trace of the core SAM defined in Fig. 5 represents a correct process reduction sequence \(\textsf{CLLB}\) (and therefore of \(\textsf{CLL}\), in the light of Theorem 4.3), first for the language without exponentials and mix, which will be treated in Section 6. In what follows, we annotate endpoints of session records with their types (e.g. as \(x{:}A \langle q,P\rangle y{:}B\)), these annotations are not needed to guide the operation of the SAM, but convenient for the proofs; they will be omitted when not relevant or are obvious from the context. We first define a simple encoding of well-typed \(\textsf{CLLB}\) processes to SAM states.

Definition 5.1

(Encode). Given \(P\vdash ^{\textsf{B}}\emptyset \) we define \(enc(P)=\mathcal {C}\) as \(enc(P,\emptyset ) {\mathop {\Mapsto }\limits ^{\textsf{cut}*}} \mathcal {C}\) where \(enc(P,H) {\mathop {\Mapsto }\limits ^{\mathsf {cut*}}}\mathcal {C}\) is defined by the rules

figure cl

Notice that enc(P) maximally applies the SAM execution rule for cut to \((P,\emptyset )\) until an action is reached. Clearly, for any \(P\vdash ^{\textsf{B}}\emptyset \), if \(enc(P) = \mathcal {C}\) then \(P \Mapsto ^* \mathcal {C}\). Also, if all cuts in a state C have empty queues then there is a process Q of \(\textsf{CLL}\) such that \(enc(Q^\dagger ) = C\). We then have

Theorem 5.1

(Soundness wrt \(\textsf{CLLB}\)). Let \(P\vdash ^{\textsf{B}}\emptyset \).

If \(enc(P) \Mapsto D {\mathop {\Mapsto }\limits ^{\mathsf {cut*}}}\mathcal {C}\) then there is Q such that \(P \rightarrow \cup \equiv Q\) and \(\mathcal {C} = enc(Q)\).

We can then combine soundness with the operational correspondence between \(\textsf{CLL}\) and \(\textsf{CLLB}\) (Theorem 4.3) to obtain an overall soundness result for the SAM with respect to \(\textsf{CLL}\):

Theorem 5.2

(Soundness wrt \(\textsf{CLL}\)). Let \(P\vdash ^{\textsf{B}}\emptyset \).

1. If \(enc(P) {\mathop {\Mapsto }\limits ^{*}} {\mathop {\Mapsto }\limits ^{\mathsf {cut*}}}C\) there is Q such that \(P \Rightarrow \cup \equiv Q\) and \(\mathcal {C}= enc(Q)\).

2. Let \(P\vdash \emptyset \). If \(enc(P^\dagger ) {\mathop {\Mapsto }\limits ^{*}} enc(Q^\dagger )\) then \(P\Rightarrow Q\).

In Definition 5.2 we identify readiness, the fundamental invariant property of SAM states, key to prove progress of its execution strategy. Readiness means that any running process holding an endpoint of negative type, and thus attempting to execute a negative action (e.g., a receive or offer action) on it, will always find an appropriate value (resp. a closure or a label) to be read in the appropriate session queue. No busy waiting or context switching will be necessary since the sequential execution semantics of the SAM enforces that all actions corresponding to a positive section of a session type have always been enqueued by the “caller" process before the "callee" takes over. As discussed in Section 3 it might not seem obvious whether all such input endpoints, (including endpoints moved around via send / receive interactions), always refer to non-empty queues.

Readiness must also be maintained by processes suspended in session records, even if a suspended process waiting on a read endpoint will not necessarily have the corresponding queue already populated. Intuitively, a process P is (HN)-ready if all its “reads" in the input channels (except those in N) will be matched by values already stored in the corresponding session queue.

Definition 5.2

(Ready).  Process P is HN-ready if for all \(y\in \textsf{fn}(P)\setminus N\) and \({x:A} \langle q,R\rangle {y}\in H\) then A is negative or void. We abbreviate \(H,\emptyset \)-ready by H-ready. Heap H is ready if, for all \(x \langle q,R\rangle y\in H\), the following conditions hold:

  1. 1.

    sep 0pt

  2. 2.

    if R(y) then R is \(H,\{y\}\)-ready

  3. 3.

    if R(x) then R is H-ready

  4. 4.

    if , R is \(H,\{z\}\)-ready.

  5. 5.

    if , R is H-ready.

State \(C=(P,H)\) is ready if H is ready and P is H-ready.

Lemma 5.1

(Readiness).  Let \(P\vdash \emptyset \) and \((P,\emptyset ) {\mathop {\Mapsto }\limits ^{*}} S\). Then \(\mathcal S\) is ready.

Theorem 5.3

(Progress). Let \(P\vdash ^{\textsf{B}}\emptyset \) and P live. Then \(enc(P)\Mapsto S'\).

6 The SAM for full \(\textsf{CLL}\)

In this section, we complete our initial presentation of the SAM, in particular, we introduce support for the exponentials, allowing the machine to compute with non-linear values, and a selective concurrency semantics.

Fig. 10.
figure 10

The SAM

We have delayed the introduction of an environment structure for the SAM, to make the presentation easier to follow. However, this was done at the expense of a more abstract formalisation of the operational semantics, making use of \(\alpha \)-conversion, and overloading language syntax names as heap references for allocated session records.

The SAM actually relies on environment-based implementation of name management, presented in Fig. 10. A SAM state is then a triple \((\mathcal {E},P,H)\) where \(\mathcal {E}\) is an environment that maps each free name of the code P into either a closure or a heap record endpoint. These heap references are freshly allocated and unique, thus avoiding any clashes and enforcing proper static scoping. Closures, representing suspended linear () and exponential behaviour (), pair the code in its environment, and we expect the following structural safety conditions for name biding in configurations to hold.

Definition 6.1

(Closure).

A process P is \((\mathcal {E},N)\)-closed if \(\textsf{fn}(P)\setminus N \subseteq dom(\mathcal {E})\), and \(\mathcal {E}\)-closed if \((\mathcal {E},\emptyset )\)-closed.

Environment \(\mathcal {E}\) is H-closed if for all \(x\in dom(\mathcal {E})\) if \(\mathcal {E}(x)\) is a reference then \(x\in H\), if then \(\mathcal {F}\) is H-closed and R is \((\mathcal {F},\{z\})\)-closed.

Heap H is closed if for all \(x \langle q,\mathcal {G},Q\rangle y\in H\), \(\mathcal {G}\) is H-closed, Q is \(\mathcal {G}\)-closed, and for all and , \(\mathcal {F}\) is H closed and R is \((\mathcal {F},\{z\})\)-closed. State \((\mathcal {E},P,H)\) is closed if H is closed, \(\mathcal {E}\) is H-closed, and P is \(\mathcal {E}\)-closed.

Fig. 11.
figure 11

SAM Transition Rules for the complete \(\textsf{CLL}\)

In Figure 11 we present the environment-based execution rules for the SAM. All rules except those for exponentials have already been essentially presented in Fig. 5 and discussed in previous sections. The only changes to those rules are due to the presence of environments, which at all times record the bindings for free names in the code. Overall, we have

Lemma 6.1

Let \(P\vdash ^{\textsf{B}}\emptyset ;\emptyset \). For all S such that \((P,\emptyset ,\emptyset ) {\mathop {\Mapsto }\limits ^{*}} S\), S is closed.

We discuss the SAM rules for the exponentials. Values of exponential type are represented by exponential closures . Recall that a session type may terminate in either type \({\textbf{1}}\), type \(\bot \) or in an exponential type !A/?A (cf.  4.2). So, the (positive) execution rule [S!] is similar to rule [S\({\textbf{1}}\)]: it enqueues the closure representing the replicated process, and switches context, since the session terminates (cf. [!] Fig. 8). The execution rule [S?] is similar to rule [S]: it pops a closure from the queue (which, in this case, always becomes empty), and instead of using it immediately, adds it to the environment to become persistently available to client code (cf. reduction rule [S?] Fig. 8). Any such closure representing a replicated process may be called by client code with transition rule [Scall], which essentially creates a new linear session composed by cut with the client code, similarly to [S]. Rule [SCall] operates with some similarity to rule [S]: instead of activating a linear closure popped from the queue, it activate an exponential closure fetched from the environment.

We extend the enc map to the exponential cut and environment states \((\mathcal {E},P,H)\) by adapting Definition 5.1, and adding the clause:

figure cx

We now update our meta-theoretical results for the complete SAM.

Theorem 6.1

(Soundness). Let \(P\vdash ^{\textsf{B}}\emptyset ;\emptyset \).

If \(enc(P) \Mapsto D {\mathop {\Mapsto }\limits ^{\mathsf {cut*}}}C\) then there is Q such that \(P \rightarrow \cup \equiv Q\) and \(C = enc(Q)\).

Theorem 6.2

(Progress). Let \(P\vdash ^{\textsf{B}}\emptyset ;\emptyset \) and P live. Then \(enc(P)\Mapsto C\).

6.1 Concurrent Semantics of Cut and Mix

Intuitively, the execution of mix consists in the parallel execution of (non-interfering) processes P and Q. We may execute by sequentialising P and Q in some arbirary way, and this actually may be useful in some cases.

However, much more interesting is the accommodation in the SAM of interfering concurrency, as required to support full-fledged concurrent languages for session-based programming. First, we evolve the SAM from single threaded to multithreaded, where states now expose a multiset of processes \(P_i\) ready for execution by the basic SAM sequential transitions: \((\{P_1,P_2,\ldots ,P_n\},H)\) and introduce an annotated variant of the cut. It has the same \(\textsf{CLL}/\textsf{CLLB}\) semantics, but to be implemented as a fork construct where P and Q spawn concurrently, their interaction mediated by an atomic concurrent session record . The type system ensuring that concurrent channels may be forwarded only to concurrent channels. We extend the SAM with transition rule for multisets:

figure dc

Each individual thread executes locally according to the SAM sequential transitions presented before, until an action on a concurrent queue is reached. Concurrent process actions on concurrent queues are atomic, and defined as expected. Positive actions always progress by pushing a value into the queue, while negative actions will either pop off a value from the queue or block, waiting for a value to become available. We illustrate with the rules for \({\textbf{1}}\),\(\bot \) typed actions.

figure dd

Notice that, as in the case for above, any negative action in the thread queue is unable to progress if the corresponding queue is empty. It should be clear how to define transition rules for all other pairs of dual actions. Given an appropriate encoding \(enc^c\) of annotated \(\textsf{CLLB}\) processes in concurrent SAM states, and as consequence of typing and leveraging the proof scheme for progress in \(\textsf{CLLB}\) (Theorem 4.2), we have:

Theorem 6.3

(Soundness-c). Let \(P\vdash ^{\textsf{B}}\emptyset ;\emptyset \).

If \(enc^c(P) \Mapsto D {\mathop {\Mapsto }\limits ^{\mathsf {cut*}}}C\) then there is Q such that \(P \rightarrow \cup \equiv Q\) and \(C = enc^c(Q)\).

Theorem 6.4

(Progress-c). Let \(P\vdash ^{\textsf{B}}\emptyset ;\emptyset \) and P live. Then \(enc_c(P)\Mapsto C\).

The extended SAM executes concurrent session programs, consisting in an arbitrary number of concurrent threads. Each thread deterministically executes sequential code, but can at any moment spawn new concurrent threads. The whole model is expressed in the common language of (classical) linear logic, statically ensuring safety, proper resource usage, termination, and deadlock absence by static typing.

7 Concluding Remarks and Related Work

We introduce the Session Abstract Machine, or SAM, an abstract machine for executing session processes typed by (classical) linear logic \(\textsf{CLL}\), deriving a deterministic, sequential evaluation strategy, where exactly one process is executing at any given point in time. In the SAM, session channels are implemented as single queues with a write and a read endpoint, which are written to, and read by executing processes. Positive actions are non-blocking, giving rise to a degree of asynchrony. However, processes in a session synchronise at polarity inversions, where they alternate execution, according to a fixed co-routining strategy. Despite its specific strategy, the SAM semantics is sound wrt \(\textsf{CLL}\) and satisfies the correctness properties of logic-based session type systems. We also present a conservative concurrent extension of the SAM, allowing the degrees of concurrency to be modularly expressed at a fine grain, ranging from fully sequential to fully concurrent execution. Indeed, a practical concern with the SAM design lies in providing a principled foundation for an execution environment for multi-paradigm languages, combining concurrent, imperative and functional programming. The overall SAM design as presented here may be uniformly extended to cover any other polarised language constructs that conservatively extend the PaT paradigm, such as polymorphism, affine types, recursive and co-recursive types, and shared state [56, 61]. We have implemented a SAM-based version [17] of an open-source implementation of \(\textsf{CLL}\) [62].

A machine model provides evidence of the algorithmic feasibility of a programming language abstract semantics, and illuminates its operational meaning from certain concrete semantic perspective. Since the seminal work of Landin on the SECD [43], several machines to support the execution of programs for a given programming language have been proposed. The SAM is then proposed herein in this same spirit of Cousineau, Curien and Mauny’s Categorical Abstract Machine for the call-by-value \(\lambda \)-calculus [21], Lafont’s Linear Abstract Machine for the linear \(\lambda \)-calculus [41], and Krivine’s Machine for the call-by-name \(\lambda \)-calculus [40] ; these works explored Curry-Howard correspondences to propose provably correct solutions. In [22], Danvy developed a deconstruction of the SECD based on a sequence of program transformations. The SAM is also derived from Curry-Howard correspondences for linear logic \(\textsf{CLL}\) [15, 72], and we also rely on program conversions, via the intermediate buffered language \(\textsf{CLLB}\), as a key proof technique. We believe that the SAM is the first proposal of its kind to tackle the challenges of a process language, while building on several deep properties of its type structure towards a principled design. Among those, focusing [4] and polarisation [32, 44, 56] played an important role to achieve a deterministic sequential reduction strategy for session-based programming, perhaps our main initial motivation. That allows the SAM to naturally and efficiently integrate the execution of sequential and concurrent session behaviours, and suggests effective compilation schemes for mainstream virtual machines or compiler frameworks.

The adoption of session and linear types is clearly increasing in research (e.g., [3, 24, 26, 56, 58, 61, 66, 74]) and general purpose languages (e.g., Haskell [8, 38], Rust [20, 42] Ocaml [35, 52], F# [51], Move [9], among many others), which either require sophisticated encodings of linear typing via type-level computation or forego of some static correctness properties for usability purposes. Such developments typically have as a main focus the realization of the session typing discipline (or of a particular refinement of such typing), with the underlying concurrent execution model often offloaded to existing language infrastructure.

We highlight the work [19], which studies the relationship between synchronous session types and game semantics, which are fundamentally asynchronous. Their work proposes an encoding of synchronous strategies into asynchronous strategies by so-called call-return protocols. While their focus differs significantly from ours, the encoding via asynchrony is reminiscent of our own.

We further note the work [50] which develops a polarized variant of the \(\overline{\lambda }\mu \tilde{\mu }\) suitable for sequent calculi like that of linear logic. While we draw upon similar inspirations in the design of the SAM, there are several key distinctions: the work [50] presents \(\lambda \mu \)-calculi featuring values and substitution of terms for variables (potentially deep within the term structure). Our system, being based on processes calculus, features neither – there is no term representing the outcome of a computation, since computation is the interactive behavior of processes (cf. game semantics); nor does computation rely on substitution in the same sense. Another significant distinction is that our work materializes a heap-based abstract machine rather than a stack-based machine. Finally, our type and term structure is not itself polarized. Instead, we draw inspiration from focusing insofar as we extract from focusing the insights that drive execution in the SAM.

In future work, we plan to study the semantics of the SAM in terms of games (and categories), along the lines of [19, 21, 41]. We also plan to investigate the ways in which the evaluation strategy of the SAM can be leveraged to develop efficient compilation of fine-grained session-based programming, and its relationship with effect handlers, coroutines and delimited continuations. Linearity plays a key role in programming languages and environments for smart contracts in distributed ledgers [24, 64] manipulating linear resources (assets); it would be interesting to investigate how linear abstract machines like the SAM would provide a basis for certifying resource sensitive computing infrastructures [9, 75].

..