Alice or Bob?: Process polymorphism in choreographies

Abstract We present PolyChor 
$\lambda$
 , a language for higher-order functional choreographic programming—an emerging paradigm for concurrent programming. In choreographic programming, programmers write the desired cooperative behaviour of a system of processes and then compile it into an implementation for each process, a translation called endpoint projection. Unlike its predecessor, Chor 
$\lambda$
 , PolyChor 
$\lambda$
 has both type and process polymorphism inspired by System F 
$_\omega$
 . That is, PolyChor 
$\lambda$
 is the first (higher-order) functional choreographic language which gives programmers the ability to write generic choreographies and determine the participants at runtime. This novel combination of features also allows PolyChor 
$\lambda$
 processes to communicate distributed values, leading to a new and intuitive way to write delegation. While some of the functional features of PolyChor 
$\lambda$
 give it a weaker correspondence between the semantics of choreographies and their endpoint-projected concurrent systems than some other choreographic languages, we still get the hallmark end result of choreographic programming: projected programmes are deadlock-free by design.


Introduction
Concurrent programs involve interacting processes.Usually, programmers write one program per process, and then compose those programs in parallel.These programs contain send and receive expressions which transmit data between processes.This makes it easy to write code that deadlocks, or gets stuck because patterns of sends and receives do not match.Session types (Honda, 1993;Honda et al., 1998) can be used to describe the patterns of sends and receives in a program, offering a foundation for static analyses aimed at preventing communication mismatches and deadlocks (Scalas and Yoshida, 2019;Caires and Pfenning, 2010;Wadler, 2012;DeYoung et al., 2012;Honda et al., 2016;Dardha et al., 2012).Working with session types enables the programmer to ensure the communications in their system follow compatible send/receive patterns.
Alternatively, programmers can use a global point of view (see e.g., Dalla Preda et al., 2017;Giallorenzo et al., 2021;Carbone and Montesi, 2013;Cruz-Filipe et al., 2021;Hirsch and Garg, 2022;Richter et al., 2022;Jongmans and van den Bos, 2022;López et al., 2016).A programmer using the global point of view writes one program, which is then compiled to a program for each process.Choreographic programming (Montesi, 2013) is a global programming paradigm with particularly well-explored foundations (Cruz-Filipe and Montesi, 2020;Montesi, 2023).In choreographies, send and receive commands are written together, usually using a notation inspired by security protocols (Needham and Schroeder, 1978).This has two key advantages.First, it gives programmers the power to express the desired communication flow among processes, but without the burden of manually coding send and receive actions.Second, it ensures that there is no mismatch which can cause deadlock, a property that has become known as deadlock-freedom by design (Carbone and Montesi, 2013).
To see the power of this, consider the (in)famous bookseller example-a recurring example in the literature of choreographic programming and session types (Carbone and Montesi, 2013;Honda et al., 2016;Montesi, 2023).Buyer wants to buy a book from Seller.To this end, Buyer sends the title of the book-say, "The Importance of Being Earnest"-to Seller, who then sends back the price.Buyer then can compare the price with its budget and based on the result informs Seller that they want to buy the book if it is within their budget, or informs them that they do not want to buy the book otherwise.We can describe this via the following choreography: let x = com Buyer, Seller ("The Importance of Being Earnest" @ Buyer) in let y = com Seller, Buyer (price lookup x) in if y < budget then select Buyer,Seller Buy (() @ Seller) else select Buyer,Seller Quit (() @ Seller) In this choreography, as in all choreographic programs, computation takes place among multiple processes communicating via message passing.Values are located at processes; for example, in the first line of the choreography, the title of the book is initially located at Buyer.The function com P, Q communicates a value from the process P to the process Q.
It takes a local value at P and returns a local value at Q. 1 Thus, x represents the string "The Importance of Being Earnest" at the process Seller, while y represents the price at the process Buyer.Finally, we check locally if the book's price is in Buyer's budget.Either way, we use function select to send a label from Buyer to Seller representing Buyer's choice to either proceed with the purchase or not.Either way, the choreography returns the dummy value () at Seller.While most of the early work on choreographies focused on simple lower-order imperative programming like in the example above, recent work has shown how to develop higher-order choreographic programming languages.These languages allow a programmer to write deadlock-free code using the usual abstractions of higher-order programming, such as objects (Giallorenzo et al., 2020) and higher-order functions (Hirsch and Garg, 2022;Cruz-Filipe et al., 2021).
For instance, the program above bakes in the title and the value of the book.However, we may want to use this code whenever Buyer wants to buy any book, and let Buyer use any local function to decide whether to buy the book at a price.
λ buyAtPrice?: Int @ Buyer → Bool @ Buyer.let x = com Buyer, Seller title in let y = com Seller, Buyer (price lookup x) in if buyAtPrice? y then select Buyer,Seller Buy (() @ Seller) else select Buyer,Seller Quit (() @ Seller) A programmer using a higher-order choreographic language, like a programmer using any higher-order programming language, can write a program once and use it in a large number of situations.For instance, by supplying different values of title and buyAtPrice?, this same code can be used to buy several different titles and Buyer can determine if they are willing to buy the book at the price using any method they desire.
While the move from first-order programming to higher-order programming is significant, previous work on the theoretical foundations of higher-order choreographic programming still did not account for other forms of abstraction (Hirsch and Garg, 2022;Cruz-Filipe et al., 2021).In particular, they did not allow for polymorphism, where programs can abstract over types as well as data, allowing them to operate in many more settings; nor did they allow for delegation, where one process can ask another process to act in its stead.
These forms of abstraction are relatively standard: delegation is an important operation in concurrent calculi, and polymorphism is vital to modern programming.In choreographic programming, however, another form of abstraction becomes natural: abstraction over processes.Current higher-order choreographic languages require that code mention concrete process names.However, we often want to write more-generic code, allowing the same code to run on many processes.For example, the above program allows Buyer to decide whether to buy a book from Seller using any local function buyAtPrice?.It would be more natural to write Seller as a book-selling service which different clients could interact with in the same way to buy a book.
In this paper, we tackle three styles of abstraction.Firstly, we show that abstraction over processes is a type of polymorphism, which we refer to as process polymorphism.Secondly, we extend Chorλ -a simply-typed functional choreographic language-with polymorphism, including process polymorphism, and call this new language PolyChorλ .Thirdly, we add the ability to communicate distributed values such as functions.Surprisingly, this addition also allows us to write code with delegation, giving a clean language to study all three forms of abstraction.
Let us examine the bookseller service in our extended language: Λ B :: Proc.
λ title : String @ B. λ buyAtPrice?: Int @ B → ∅ Bool @ B. let x = com B, Seller title in let y = com Seller, B (price lookup x) in if buyAtPrice? y then select B,Seller Buy (() @ Seller) else select B,Seller Quit (() @ Seller) This program allows a process named B to connect with Seller to buy a book.B then provides a string title and a decision function buyAtPrice?.Thus, we no longer have to write a separate function for every process which may want to buy a book from Seller.While this addition may appear simple, it poses some unique theoretical challenges.First, the goal of a choreographic language is to compile a global program to one local program per process.However, since B does not represent any particular process, it is unclear how to compile the polymorphic code above.We solve this problem via a simple principle: each process knows its identity.With this principle in place, we can compile the code to a conditional in each process: one option to run if they take the role of B, and the other to run if they do not.
Notably, each process chooses dynamically which interpretation of the code to run.This flexibility is important, since we may want to allow different processes to occupy B's place dynamically.For instance, we can imagine a situation where Buyer 1 and Buyer 2 work together to buy a particularly expensive book: perhaps they compare bank accounts, and whoever has more money buys the book for them to share.This can be achieved in our system with the following code, where seller service is the name of the function above:  A related challenge shows up in the operational semantics of our extended language.Languages like PolyChorλ generally have operational semantics which match the semantics of the compiled code by allowing out-of-order execution: redices in different processes might be reduced in any order.However, care must be taken with process polymorphism, since it may not be clear whether two redices are in the same or different processes.
In addition to type and process polymorphism, PolyChorλ is the first choreographic language to allow the communication of distributed values: values not located entirely at the sender.These values include full choreographies described by distributed functions, which can be used to model delegation.To see how process polymorphism and communication of distributed values enables delegation, consider the following scenario: when a buyer asks for a book, the seller first checks whether it is in stock.If it is, the sale continues as normal.
If not, the seller delegates to a second seller, which may sell the book to the buyer.We can define this code as follows: Structure of the Paper.We begin in Section 2 by examining the system model of PolyChorλ .We then proceed with the following contributions: • In Section 3, we describe the PolyChorλ language in detail.This language includes both type polymorphism and process polymorphism.We develop both a type system and kind system and an operational semantics for PolyChorλ .• In Section 4, we describe the local network language used to describe the distributed implementation.We also detail how to obtain this implementation via endpoint projection, which compiles PolyChorλ programs to a program for each process.• In Section 5 we describe the main theorem of this paper, the correctness of endpoint projection with respect to our operational semantics.Because of the dynamic nature of process polymorphism, this requires significant reasoning compared to previous works on choreographies.
Finally, we discuss related work in Section 6 and conclude in Section 7.

System Model
We begin by discussing the assumptions we make about how PolyChorλ programs will be run.These assumptions are as light as possible, allowing for PolyChorλ to be run in many different scenarios.In particular, we assume that we have a fixed set of processes, which can communicate via messages.These processes can each be described by a polymorphic λ -calculus, similar to System Fω, but with the addition of communication primitives.

Processes
We assume that there is a fixed set N of process names P, Q, Alice, et cetera.These processes can represent nodes in a distributed system, system processes, threads, or more.Process polymorphism allows us to refer to processes using type variables, which may go in or out of scope.Despite this, the set of physically-running processes remains the same.We assume every process knows its identity.Thus, every process can choose what code to run on the basis of its identity.This assumption is reasonable for many practical settings, for instance it is common for nodes in distributed systems to know their identity.This capability is essential to our strategy for enabling process polymorphism.

Communication
We assume that process communicate via synchronous message passing.Thus, if P sends a message to Q, then P does not continue until Q has received the message.Moreover, we assume that message passing is instantaneous and certain, so messages do not get lost.
Processes can receive two kinds of messages: values of local programs (described below) and labels describing choices made during a computation.These are used to ensure that different processes stay in lock-step with each other.

Local Programs
We assume that processes run a local language described in Section 4. This is a functional language extended with communication features, similar to the language GV (Gay and Vasconcelos, 2010;Wadler, 2012).However, unlike GV or its derivatives, our local language includes type and process polymorphism.
Endpoint projection translates PolyChorλ into this "Network Process" language.We have thus further extended GV with features required for our endpoint-projection mechanism.For instance, we provide an AmI expression form, which allows a process to choose which code to run based on its identity.Despite these extensions, the language should feel familiar to any reader familiar with polymorphic λ -calculi.

Variables
x, y, . . .We now turn to our first major contribution: the design of the polymorphic, choreographic λ -calculus, PolyChorλ .This calculus extends the choreographic λ -calculus Chorλ of Cruz-Filipe et al. (2021) with both type and, more importantly, process polymorphism.We begin by describing the features that PolyChorλ shares with the base Chorλ before describing the new features.The syntax of PolyChorλ can be found in Figure 1.
Syntax Inherited from Chorλ .Since choreographic programs describe the behavior of an entire communicating network of processes, we need to reason about where terms are located.In other words, we need to know which processes store the data denoted by a term.Terms of base type, like integers, are stored by exactly one process.This is represented in our type system by matching base types with a process name.For example, integers stored by the process Alice are represented by the type Int @ Alice.Values of this type also mark the process which stores them, so a value 5 @ Alice ( read "the integer 5 at Alice") has type Int @ Alice.In Figure 1, the only base types are () @ P and Int @ P, but it is easy to extend the language with other base types, such as the types String @ P or Bool @ P used in the introduction.We will continue to freely use other base types in our examples.
While base types are located on just one process, data of more-complex types may involve multiple processes.For instance, the term (5 @ Alice, 42 @ Bob) involves both data stored by Alice and Bob.This is still recorded in the type: the term above has type Int @ Alice × Int @ Bob.In addition to base types and product types, PolyChorλ also has sum types (written τ 1 + τ 2 ), along with their normal introduction and elimination forms.
Functions are treated more unusually: while we have standard λ and application forms, we also allow functions to be defined mutually-recursively with each other.In order to do so, any PolyChorλ choreography is associated with a list, D, of bindings of functions to function variables f , which are also expressions.A function variable can then during execution be instantiated with its definition according to this list.As we will see in Section 3.3, PolyChorλ terms are evaluated in a context which associates each function variable with a term.Note that, while in the original Chorλ types were mutually recursive in a similar way, in PolyChorλ we do not support recursive types.To see why, note that we syntactically restrict many types to type values.This prevents us having to reason about processes denoted by arbitrary terms-e.g., we cannot send to the "process" (λ X :: Proc.X) P but we can write (Λ Y :: Proc.com τ Q,Y ) ((λ X :: Proc.X) P) which, due to our call-by-value semantics, will force the type to reduce to P before Y gets instantiated.As we will see in Section 4, allowing communication between arbitrary types would make endpoint projection difficult.However, since recursive types cannot necessarily reduce to a type value, they cannot be used in many parts of the type system.
Function types are also more specific than their usual construction in λ -calculus: they are written τ 1 → ρ τ 2 .Here, ρ is the set of processes which describes which processes, other than those in τ 1 and τ 2 , may participate in the function body (note that ρ may contain type variables).Thus, if Alice wants to communicate an integer to Bob directly (without intermediaries), then she should use a function of type Int @ Alice → ∅ Int @ Bob.However, if she is willing to use the process Proxy as an intermediary, then she should use a function of type Int @ Alice → {Proxy} Int @ Bob.
In order to allow values to be communicated between processes, we provide the primitive communication function com τ P, Q .This function takes a value of type τ at P and returns the corresponding type at Q.As mentioned in the introduction, most choreographic languages provide a communication term modeled after the "Alice-and-Bob" notation of cryptographic protocols.For instance, Alice -> Bob : 5 might represent Alice sending 5 to Bob.This is easily recovered by applying the function com τ Alice, Bob .For example, the term com Int@Alice Alice, Bob (5 @ Alice) represents Alice sending a message containing 5 to Bob: it evaluates to 5 @ Bob and has type Int @ Bob.
Finally, consider the following, where M has type Int @ Alice + Int @ Alice: case M of inl x ⇒ 3 @ Bob; inr y ⇒ 4 @ Bob Clearly, Bob needs to know which branch is taken, since he needs to store a different return value in each branch.However, only Alice knows which whether M evaluates to inr Int@Alice V or inl Int@Alice V (here inl and inr are used to denote that a value is either the right or left part of a sum and annotated with the type of the other part of the sum to ensure type principality).Thus, this choreography cannot correspond to any network program.Using the terminology found in the literature of choreographic languages, we might say that the choreography is unrealisable because there is insufficient knowledge of choice (Castagna et al., 2012;Montesi, 2023).
In order to enable programs where a process's behaviour differs depending on other processes data, such as how Bob behaved differently depending on Alice's data, we provide select terms.These allow one process to tell another which branch has been taken, preventing knowledge from "appearing out of nowhere."For instance, we can extend the program above to: This represents the same program as above, except Alice tells Bob whether the left or the right branch has been taken.Unlike the previous version of this example, it does represent a (deadlock-free) network program.In general, we allow arbitrary labels to be sent by select terms, so semantically-meaningful labels can be chosen.
While com and select both transfer information between two processes, they differ in what information they transfer.com moves a value, e.g., as an integer or a function, from the sender to the receiver.select on the other hand uses a label to inform the receiver of a choice made by the sender.Some choreographic languages combine the two, so both a label and a value is communicated at the same time, but like most choreographic languages PolyChorλ keeps the two separate.
Syntax Additions over Chorλ .In order to achieve (both type and process) polymorphism in PolyChorλ , we add several features based on System Fω (Girard, 1972).In particular, we add kinds and universal types ∀X :: K. τ along with type abstraction and application.
From System Fω we inherit the kind * , which is the kind of types.We additionally inherit the kind K 1 ⇒ K 2 which represents functions from types to types.
Moreover, we inherit type-level functions λ X :: K. τ from System Fω.These represent the definition of type constructors.We additionally have type-level function application τ 1 τ 2 .Since types contain computation, we also define type values, which are simply types without application.
Note that the base types () @ ν and Int @ ν, like local values, are syntactically restricted to only allow type values as subterms.This allows us to use a type variable to compute the location of a value dynamically, but not arbitrary terms, which would make it much harder to tell at time of projection where the value is located.Thus, we can write (λ X :: Proc.Int @ X) (Y P) to compute the location of an integer dynamically (Y P has to reduce to a type value before X can be instantiated), but we cannot write Int @ (Y P) directly.This way, our projected calculus can tell when instantiating X (at runtime) whether it gets instantiated as P. It would be more complicated to create runtime checks for whether Y gets instantiated as a function type that outputs P or not.
In addition to the kinds * and K 1 ⇒ K 2 of System Fω, we also have the kind Proc of process names.Thus, process names are types, but they cannot be used to type any terms.Additionally, we have kinds K \ ρ, which represents types of kind K which do not mention any of the processes in the set ρ. Since we restrict the types that can be communicated based on which processes they contain, as we will see soon, this restricted kind can be used to define polymorphic functions which contain communication.For instance, the term defines a function which, given distinct processes X and Y , causes X to send 5 to Y .
In the rest of this section, we explore the semantics of PolyChorλ .First, we look at its static semantics, both in the form of typing and kinding.Second, we describe its operational semantics.Throughout, we will continue to give intuitions based on the concurrent interpretation of PolyChorλ , though the semantics we give here does not correspond directly to that interpretation.

Typing
We now turn to the type system for PolyChorλ .As before, our type system builds on that for Chorλ .Here, we focus on the rules that are new in this work.Thus, we focus on rules related to polymorphism, and those that have had to change due to polymorphism.
Typing judgements for PolyChorλ have the form Θ; Γ ⊢ M : τ, where Θ is the set of process names-either names in N or type variables with kind Proc-used in M or the type of M. The typing environment Γ is a list associating variables and function names to their types and type variables and process names to their kinds.We sometimes refer to the pair Θ; Γ as a typing context.
Selected rules for our type system can be found in Figure 2. The full collection of rules are given in Appendix 1. Again, many of the rules are inherited directly from Chorλ (Cruz-Filipe et al., 2021); we thus focus on the rules that have changed due to our additions.Many, if not most, of these rules are inspired by System Fω.However, the addition of the kind of processes and Without kinds-i.e., kinds of the form K \ ρ-also lead to some changes.
The rules [Tunit] and [Tint] give types to values of base types.Here, we have to ensure that the location of the term is a process.Intuitively, then, we want the location to have kind Proc.However, it might have restrictions-that is, it might be of the form Proc \ ρ.In this case, our subkinding system (which you can find details about in Section 3.2) still allows us to apply the rule.
We express function application and abstraction via the [Tapp] and [Tabs] rules, respectively.The application rule [Tapp] is largely standard-the only addition is the addition of a set ρ on the function type, as discussed earlier.The abstraction rule [Tabs], on the other hand, is more complicated.First, it ensures that the argument type, τ 1 , has kind * .Then, it ensures that every element in the set decorating the arrow is a process name-i.e., that it has kind Proc.Finally, it checks that, in an extended environment, the body of the function has the output type τ 2 .As is usual, this extended environment gives a type to the argument.However, it restricts the available process names to those in the set ρ and those mentioned in the types τ 1 and τ 2 .
There are two ways that a type τ can mention a process: it can either name it directly, or it can name it via a type variable.Thus, in the rule [Tabs] we allow the free variables of τ 1 and τ 2 to remain in the process context, computing them using the (standard) free-typevariable function where ∀X :: K. M and λ X :: K. M both bind X.However, we must also identify the involved processes in a type, which we write ip(τ) and compute as follows: The involved processes of other types are defined homomorphically.
The communication primitives select and com are typed with [Tsel] and [Tcom], respectively.A term select ν 1 ,ν 2 ℓ M behaves as M, where the process ν 1 informs the process ν 2 that the ℓ branch has been taken, as we saw earlier.Thus, the entire term has type τ if M does.Moreover, ν 1 and ν 2 must be processes.
The rule [Tcom] types com terms.So far we have been simplifying the type used in com τ P, Q for readability.We have been using τ to denote the input type, but as it turns out to type com τ P, Q correctly, we have to complicate things a little.Intuitively, a term com τ ν 1 , ν 2 M represents ν 1 communicating the parts of M on ν 1 to ν 2 .Thus, we require that τ be a type transformer requiring a process.Moreover, ν 1 and ν 2 cannot be mentioned in τ; otherwise not every part of the type of M on ν 1 in our example above would transfer to ν 2 .For this we use the following notion of mentioned processes: Again, with other types being defined homomorphically.The difference between involved and mentioned processes is subtle.If there is no polymorphism, they are the same, but when dealing with polymorphism with restriction they are opposites: involved processes includes every process not in the restriction (the variable could be instantiated as something involving those processes and thus they may be involved), while mentioned names includes the processes mentioned in the restriction.Mentioned names is used only when typing com.If we have such a type-level function, τ, and two type values ν 1 and ν 2 which are not and will not be instantiated to anything mentioned in τ then we can type com τ ν 1 , ν 2 as a function from τ ν 1 to τ ν 2 .Since this is direct communication, no intermediaries are necessary and we can associate this arrow with the empty set ∅.
It is worth noting at this point that the communication rule inspired our use of System Fω rather than plain System F, which lacks type-level computation.In Chorλ and other previous choreographic languages, communicated values must be local to the sender.In PolyChorλ , this would mean not allowing the communicated type to include type variables or processes other than the sender.Since we are introducing the idea of using communication as a means of delegation, we have slackened that restriction.This means that PolyChorλ programs can communicate larger choreographies whose type may involve other processes, and importantly other type variables.We see this in the delegation example at the end of Section 1, where we have the communication com Seller, Seller 2 . Adding in the required type annotation (which we had suppressed in the introduction), this becomes com λ X::Proc.String@X→ ∅ ()@B Seller, Seller 2 .Note that this still leaves us with a free type variable B, representing the unknown process that Seller is telling Seller 2 to interact with!Since we cannot ban free type variables in communicated types, we must create a typing system that can handle them, and this requires type level computation.
To see why this led us to type-level computation, consider the alternative.In Chorλ and other choreographic works, we would have a type communication using process substitution instead of communication.The annotated program would then be com String@Seller→ ∅ ()@B Seller, Seller 2 . When applied to a program of appropriate type, the result would have type Note that, because B is a type variable, it was ignored by the substitution.If B is later substituted with something which contains Seller, then we have lost the information that we need to change this to Seller 2 .Thus, we need some mechanism to delay this substitution; rather than use a mechanism like explicit substitutions, we instead reached for the standard tool of System Fω.This seemed more elegant and less ad-hoc; moreover, it adds features which a real-world implementation of PolyChorλ would want anyway.
Returning now to the typing rules of Figure 2, we next have the [TappT] and [TabsT] rules, which type universal quantification.The [TappT] rule is completely standard, while the [TabsT] rule is our most-complicated rule.In this rule, we have 4 different definitions for the typing context of M, depending on the kind of X.As is standard, we check if the body of the function has the right type when the parameter X has kind K.But first, if X is a process, then we need to extend Θ with X.In addition, we must further manipulate the context in order to ensure that the types whose kinds are restricted of X correspond to the restriction on the kind of X.
First, the new type variable X may shadow a previously-defined X.Thus, we need to remove X from any restrictions already in the context.We do this using the following operation K + ν: We define + on other kinds homomorphically, and extend this to contexts as usual: has a Without kind-that is, X's kind tells us it cannot be any of the processes in ρ-then we need to symmetrically add a restriction on X to every type in ρ.Otherwise, we would not be able to use the roles in ρ in any place where we cannot use X, even though we know X will not be instantiated with them.We do this with the operation Γ & ρ \ X, which we define as follows:

and τ ∈ ρ}
With these operations in place, we can now fully understand [TabsT].When K is actually a Without kind, then we must handle both shadowing and symmetrical restrictions.However, when it is not a Without kind, we must only handle shadowing.
The final addition to our type system is the rule [Teq].This is another standard rule from System Fω; it tells us that we are allowed to compute in types.More specifically, it tells us that we can replace a type with an equivalent type, using the following equivalence: While [Teq] is the last rule we added to our type system, it is not the last rule in Figure 2. We also add an extra judgement of the form Θ; Γ ⊢ D where Θ; Γ is a typing context as before, and D is a set of definitions for function variables-i.e., D = { f 1 = M 1 , . . .f n = M n }.We write D( f ) for the term associated with f in D. The only rule for this judgement is [Tdefs], which says that a set of definitions is well-formed if every variable in D is associated with a type τ in Γ, and the body of f in D can be given be given type τ in the context / 0; Γ.We require that the body of f can be typed with an empty set of roles because they are global predefined functions, and as such they should not be local to any one process. [TDEFS]

Kinding
We finish our discussion of the static semantics of PolyChorλ by looking at our kinding system.Our kinding system uses only one judgement, Θ; Γ ⊢ τ :: K, which says that in the typing context Θ; Γ, the type τ has kind K.You can find the rules of our kinding system in Figure 3.These are mostly directly inherited from System Fω.However, we must account for Proc and Without kinds.
For instance, the rules [Kunit] and [Kint] check that the type representing which process is storing the data indeed has the kind Proc.Similarly, [Kfun] ensures that all of the types in the set of possible intermediaries are processes.The rule for type variables, [Kvar], ensures that if a type variable X is assigned kind Proc, then X must also be in Θ.
One of the biggest differences between our kinding system and that of System Fω, however, is the rule [Ksub] which tells us that our system enjoys subkinding.The subkinding rules come from the subset ordering on Without kinds.The rules for subkinding are as Fig. 3. Kinding Rules follows: [SKREFL] Let τ be a type.If there exists a typing context Θ; Γ such that Θ; Γ ⊢ τ :: K then there exists a unique type value ν such that τ ≡ ν.
Proof Follows from induction on the derivation of Θ; Γ ⊢ M : τ and the kinding rules.
We also find that types have the same kinds as their equivalent type values.Due to βexpansion, a kindable type can be equivalent to an unkindable type, but not an unkindable type value.
Proof Follows from the kinding and type equivalence rules.We take advantage of the fact that if we use the rule (λ X :: K. τ 1 ) τ 2 ≡ τ 1 [X → τ 2 ] to create an unkindable τ 2 ≡ τ 1 with an extra application, then we must also use the same rule to remove this new type application before we get to a base type.
Example 1.We return to the delegation example from Section 1 and try to type it.As B appears free in the type of a value, F, being communicated between Seller and Seller 2 , B must actually have the restricted kind Proc \ {Seller, Seller 2 }.The choreography therefore gets the type This type shows both the input, output, and involved roles of the choreography.

Operational Semantics
Finally, we consider the operational semantics of PolyChorλ .These are mostly a standard call-by-value reduction semantics for a typed λ calculus.However, the reduction semantics must also carry a set D of function definitions.Only a few rules are unusual or must be modified; those can be found in Figure 4.You can find the rest of the rules in Appendix 2.
The rules [AppTAbs] and [MTApp1] come from System Fω.The rule [AppTAbs] is similar to ordinary CBV β reduction, but tells us how to reduce a type abstraction applied to a type value, but with the caveat that if we do not have a type value we must use type equivalence to get one before reducing.The rule [MTApp1] tells us that we can reduce a type function applied to any argument.
The rule [Def] allows us to reduce function names by looking up their definition in the set D.
Finally, we have the rules for communication.The rule [Sel] says that select acts as a no-op, as we stated earlier.While this may seem redundant, such terms are vital for projection, as we will see in the next section.More importantly, the [Com] rule says tells us how we represent communication at the choreography level: via substitution of roles.This also helps explain some of the restrictions in [Tcom].Since we replace all mentions of P with Q in V , we cannot allow other mentions of P in the type transformer of V .Otherwise, there could be some mentions of P which should not be replaced during communication, which we do not model.Unlike when typing com τ P, Q V , when executing a communication we know (since we only consider choreographies without free variables) that any type variables in τ or V have already been instantiated and as such do we do not need to consider how to substitute variables which may later be instantiated to P or Q.
It may be surprising to learn that our semantics are simply call-by-value reduction semantics, especially for those readers familiar with choreographies.After all, choreographies are supposed to represent concurrent programs, and so multiple redices should be available at any time.Indeed, previous works on choreographic programming (e.g.Hirsch and Garg, 2022;Cruz-Filipe and Montesi, 2020;Carbone and Montesi, 2013) provided a semantics with out-of-order execution, so that the operational semantics of the choreographies matched with all possible reductions in the concurrent interpretation.We use these simpler semantics, without out-of-order execution, instead.In exchange, our result in Section 5 will be weaker: we only promise that any value which the choreography can reduce to, so can the concurrent interpretation.
To see why we chose to obtain this weaker result, consider the choreography Here we have a function f which needs to be instantiated with a distributed pair.P is ready to feed its part of the argument into f and start computing the result, while Q 1 and Q 2 are still working on computing their part of the argument.There are two ways we could interpret PolyChorλ concurrently: we can synchronize when all processes enter a function or we can allow P to enter the function early.We take the second, more practical, route.However, this means it is not possible to reflect at least one evaluation order into the semantics of the choreography without banning distributed values or allowing us to somehow call a single value in multiple steps.This insight led to us adopting the weaker guarantee discussed above.
As is standard for call-by-value λ -calculi, we are able to show that our type system is sound with respect to our operational semantics, as expressed in the following two theorems: Proof Follows from the typing and semantic rules.

Endpoint Projection
We now proceed to the most-important result for any choreographic programming language: endpoint projection.Endpoint projection gives a concurrent interpretation to our language PolyChorλ by translating it to a parallel composition of programs, one for each process.In order to define endpoint projection, though, we must define our process language, which we refer to as a local language.The syntax of the local language can be found in Figure 5.There you can also find the syntax of local transition labels and network transition labels, both of which will be described when we describe the operational semantics of networks.
As in PolyChorλ , our local language inherits much of its structure from System Fω.In particular, we have products, sums, functions, universal quantification, and λ types, along with their corresponding terms.In fact, some types look more like standard System Fω than PolyChorλ : function types do not need a set of processes which may participate in the function, and base types no longer need a location.
However, not everything is familiar; we have introduced new terms and new types.The terms send ν and recv ν allow terms to send and receive values, respectively.We also split select terms into two terms: an offer term & ν {ℓ 1 : L 1 , . . ., ℓ n : L n } which allows ν to choose how this term will evolve.We represent such choices using choice terms of the form ⊕ ν ℓ L. This term informs the process represented by ν that it should reduce to its subterm labeled by ℓ, and then itself reduces to the term L. While these are unusual pieces of a polymorphic language like System Fω, they are familiar from process languages like π calculus.We also add undefined types and terms, written ⊥ and ⊥, respectively.These represent terms which are ill-defined; we use them to represent data which does not exist on some process P, but which needs to be written structurally in P's program.
We also include a more-unusual feature: explicit substitutions of processes.The term sub[ν 1 → ν 2 ] is a function which, when applied, replaces the role denoted by ν 1 with that denoted by ν 2 in its argument.This function allows us to represent the view of communication according to third parties: the roles simply change, without any mechanism necessary.For instance, imagine that Alice wants to tell Bob to communicate an integer to Cathy.She can do this by sending Bob the function com λ X::Proc.Int@X Alice, Cathy  this corresponds to the choreography com λ X::Proc.Int@X→ ∅ Int@Cathy Alice, Bob com λ X::Proc.Int@X

Alice, Cathy
In order to project this choreography, we need to be able to project the communication function above even when it is not applied to any arguments.This is where we use explicit substitutions: we project the communication function to sub[Alice → Bob].
Finally, we introduce our unique feature: AmI terms and their corresponding type.These represent the ability of a process to know its own identity, and to take actions based on that knowledge.Process polymorphism requires an instantiation of a process variable at process P to be accompanied by a conditional determining whether the variable has been instantiated as P or as some other process P may interact with.In particular, the term AmI ν ?M 1 & M 2 reduces to M 1 if the term is run by the process denoted by ν, and M 2 otherwise.Since M 1 and M 2 may have different types, we provide types of the form AmI ν ?τ 1 & τ 2 , which represent either the type τ 1 (if typing a term on the process denoted by ν) or τ 2 (otherwise).These terms form a backbone of endpoint projection for PolyChorλ : every Λ term binding a process gets translated to include an AmI term.For instance, consider projecting the choreography Λ X :: Proc.com λ X ′ ::Proc.Int@X ′ Q, X 4 @ Q to some process P. Depending on the argument to which this function is applied, P should behave very differently: if it is applied to P itself, it should receive something from Q.However, if it's applied to any other term, it should do nothing.We therefore project the choreography above to the following program for P: Note that the AmI construct is necessary for process polymorphism in general, unless process variables cannot be instantiated to the process they are located at.It, and the combinatorial explosion caused by having multiple process abstractions, is not caused by the choreographic language but instead the choreographic language hides it and lets programmers avoid explicitly describing both sides of the AmI separately.
Note that we do not have a kinding system for local programs.In fact, we do not check the types of local programs at all.However, because types have computational content, we need to project them as well.In order to preserve that computational content, we again use an equivalence of types which corresponds to β , η-equivalence.However, in order to accommodate AmI types, we must index that equivalence with a process.Then, we have two rules regarding AmI types: [IAM] Now that we have seen the syntax of the programs which run on each process, we can look at whole networks: Definition 1.A network N is a finite map from a set of processes to local programs.We often write P for the network where process P i has behaviour L i .
The parallel composition of two networks N and N ′ with disjoint domains, N | N ′ , simply assigns to each process its behaviour in the network defining it.Any network is equivalent to a parallel composition of networks with singleton domain, as suggested by the syntax above.
We now consider the operational semantics of local programs and networks.These are given via labelled-transition systems; the syntax of both sorts of label can be found in Figure 5. Selected rules for both operational semantics can be found in Figure 6.The full rules can be found in Appendix 3. As before, transitions are indexed by a set d of function definitions.Function variables reduce by looking up their definition in d.Since this transition involves no communication, it is labelled with the empty transition, τ.
Perhaps surprisingly, undefined arguments to functions do not immediately cause the application to be undefined.To see why, think about choreographies of the form (λ x : Int @ P. M) N where some process Q = P is involved in both M and N. We project this to an application on Q of the form (λ x : ⊥.M Q ) N Q .Note that because we know that N has type Int @ P, the projection N Q has type ⊥ and eventually evaluates to ⊥.Thus, if (λ x : ⊥.M Q ) ⊥ immediately evaluated to ⊥, the process Q could not participate in M, as they need to do!We therefore allow this to evaluate to M Q .However, when the function is also undefined, we evaluate this to ⊥ with the empty label τ, as you can see in the rules [NBot] and [NBott] Local Language: Networks: As mentioned earlier, the explicit substitutions sub[P → Q] are functions which, when applied, perform the requested substitution in the value to which they are applied.This is implemented in the rule [NSub].
The AmI terms are given meaning via the rules [NAmIR] and [NAmIL].The rule [NAmIR] says that the term AmI P ?L 1 & L 2 can evaluate to L 1 with label P, while the rule [NAmIL] says that it can instead reduce to L 2 with label Q where Q = P.We will see later that in the network semantics, we only allow transitions labeled with the process performing the transition.
Choice and offer terms reduce via the rules [NCho] and [Noff].The first, [Ncho], tells us that a choice term simply reduces to its continuation with a transition label indicating the choice that has been made.The second, [Noff], tells us that an offer term can reduce to any continuation, with a transition label indicating the label of the continuation it reduced to.We will see later that the semantics of networks only allows the offer term to reduce to the continuation chosen by a matching choice term.
Finally, the send and recv terms are given meaning via [NSend] and [NRecv], respectively.However, these rules behave somewhat-differently than might be expected: rather than acting as a plain send and receive, they behave more like a swap of information.
In a plain send, the sender would not have any information after the send-perhaps the term would come with a continuation, but this would not be related to the send.Moreover, the receiver would not provide any information, but merely receive the information from the sender.However, when sending a choreography with process polymorphism, the sender may need to participate in the continuation, depending on how polymorphic functions are applied.For instance, consider the following choreography, where P sends a polymorphic function to Q, and the resulting polymorphic function is applied to P: (com λY ::Proc.∀X::Proc.Int@X P, Q (Λ X :: Proc.com λY ′ ::Proc.Int@Y ′ P, X (5 @ P))) P The polymorphic function that results from the com above is as follows: (5 @ Q) Applying this to P leads to a program where P receives from Q. Since P needs to participate in this program, P must have a program remaining after sending the polymorphic function to Q.
While this explains why send terms cannot simply, for instance, return unit, it does not explain why send and recv terms swap results.To see this, consider what happens when a term is sent from a process P to another process Q.We know from our type system that Q is not mentioned in the type of the term being sent, and we know that after the send all mentions of P are changed to mentions of Q.Hence, after the send, P's version of the term should be the view of a process not involved in the term.This is exactly what Q's version of the term is before the send.Thus, sends and recvs behaving as swaps leads to the correct behaviour.Now that we have discussed the semantics of local programs, we discuss the semantics of networks.Each transition in the network semantics has a silent label indexed with the processes participating in that reduction: τ P , where P consists of either one process name (for local actions at that process) or two process names (for interactions involving these two processes).We treat P as a set, implicitly allowing for exchange.
For instance, the rule [NCom] describes communication.Here, one local term must reduce with a send label, while another reduces with a recv label.These labels must match, in the sense that the value received by the recv must be the value sent by the send-though with the receiver in place of the sender-and vice-versa.Then, a network in which the local terms are associated with the appropriate processes, Q and P, can reduce with the label τ Q,P .Similarly, the rules [NSel] reduces matching choice and select terms, resulting in the label τ Q,P .
While [NCom] and [NSel] describe communication, the rest of the rules describe how a single process's term can evolve over time in a network.Particularly interesting is [NProam], which says that a AmI term can reduce only according to the process it is associated with.We can see here that the resulting label is τ P , indicating that this reduction step only involves P.
The rules [NPro] tells us how to lift steps with an empty label τ.Such steps make no assumptions about the network, and so such terms can be associated with any process P. When such a reduction takes place in a network, we label the resulting transition τ P .
Finally, the rule [NPar] says that if one part of a network can reduce with a label τ P , then the entire network can reduce with that same label.This allows the other rules, which assume minimal networks, to be applied in larger networks.In the future we will use → * and → + to denote respectively a sequence and a sequence of at least one action with arbitrary labels.

Projection
We can now define the endpoint projection (EPP) of choreographies.This describes a single process's view of the choreography; the concurrent interpretation of a choreography is given by composing the projection to every process in parallel.Endpoint projection to a particular process P is defined as a recursive function over typing derivations Θ; Γ ⊢ M : τ.For readability, however, we write it as a recursive function over the term M, and use the notation typeof (N) to refer to the types assigned to any term N in the implicit typing derivation.Similarly, we use kindof(τ) to refer to the kind of a type τ in the implicit typing derivation.We write M P to denote the projection of the term M (implicitly a typing derivation for M, proving that it has some type) to the process P.
Intuitively, projection translates a choreography term to its corresponding local behavior.For example, a communication action projects to a send (for the sender), a receive (for the receiver), a substitution (for the other processes in the type of the value being communicated) or an empty process (for the remaining processes).However, this is more complicated for case statements.For instance, consider the following choreography, which matches on a sum type which is either an integer on Alice or nothing on Alice.If it is an integer, then Bob receives that integer from Alice and returns it.Otherwise, Bob returns the default value of 42.Alice informs Bob of which branch she has taken using select terms.
Imagine projecting this to Bob's point of view.He does not have any of the information in the sum, so he cannot participate in choosing which branch of the case expression gets evaluated.Instead, he has to wait for Alice to tell him which branch he is in.If we naïvely translate just the first branch of the case expression, Bob waits for Alice to send him the label Just and then waits for Alice to send him an integer.Similarly, in the second branch Bob waits for Alice to send him the label Nothing before returning the default value 42.Somehow, we need to combine these so that Bob waits for either label, and then takes the corresponding action.
We do this by merging Bob's local programs for each branch (Carbone et al., 2012;Cruz-Filipe and Montesi, 2020;Honda et al., 2016).Merging is a partial function which combines two compatible local programs, combining choice statements.In other words, the key property of merging is: Merging is defined homomorphically on other terms, though it is undefined on incompatible terms.Thus, for example, We can then use this to project the choreography above to Bob as: Where ⊥ represents a part of the choreography executed by Alice.
Definition 2. The EPP of a choreography M for process P is defined by the rules in Figures 7, 8 and 9.
To project a network from a choreography, we therefore project the choreography for each process and combine the results in parallel: Intuitively, projecting a choreography to a process that is not involved in it returns a ⊥.More complex choreographies, though, may involve processes that are not shown in their type.This explains the first clause for projecting an application: even if P does not appear in the type of M, it may participate in interactions inside M. A similar observation applies to the projection of case, where merging is also used.
Selections and communications follow the intuition given above, with one interesting detail: self-selections are ignored, and self-communications project to the identity function.This is different from many other choreography calculi, where self-communications are not allowed-we do not want to impose this in PolyChorλ , since we have process polymorphism and therefore do not want to place unnecessary restrictions on which processes a choreography can be instantiated with.
Any process P must prepare two behaviours for a process abstraction Λ X :: Proc.M: one for when X is instantiated with P itself, and one for when X is instantiated with another process.To do this, we use AmI terms, which allow P to use its knowledge of its identity to select which behaviour takes place.(This also holds when the kind of X is restricted, as long as the base kind is Proc, though if P is excluded from the type of X and P does not participate in M then we simply project to ⊥.)However, type abstractions Λ X :: K. M do not use AmI terms if K is not a kind of processes, since P cannot instantiate X.
In general, projecting a type yields ⊥ at any process not used in that type.We use the restrictions on kinds to avoid projecting type variables and type abstractions when we know we do not need to and project all process names to themselves, but otherwise the projection of type constructs is similar to that of corresponding process terms.
Finally, to execute a projected choreography, we need to project the set of definitions of choreographic functions to a set of definitions of local functions.Since these functions are all parametrised over every involved process, this is as simple as projecting the definitions Programs (ctd.)onto an arbitrarily chosen process name.
Note that function names always get projected everywhere.This means that if we have a function which does not terminate when applied to some value in any process, then it diverges when applied to that value in the choreography and in every other process.
Example 2. We will now show how to project the bookseller service example from Section 1.As in that example we use let x = M in M ′ as syntactic sugar for λ x : τ.M ′ M for some τ and if M 1 then M 2 else M 3 as syntactic sugar for case M 1 of inl x ⇒ M 2 ; inr x ⇒ M 3 for some x / ∈ (fv (M 2 ) ∪ fv (M 3 )).We project for Seller and get the following process: Here we can see that if the buyer B turns out to be Seller itself, then all the communications become identity functions, and the seller does not inform itself of its choice.Otherwise, we get a function which, after being instantiated with a buyer, receives two ⊥s representing values existing at B. It then waits for B to send a title, returns the price of this title, and waits for B to decide whether to buy or not.It might seem strange to have a function parametric on two ⊥s, but this example actually illustrates why we cannot necessarily remove the first two λ s from the process without causing a deadlock.Consider that let y = send B (price lookup x)in & B {Buy : (), Quit : ()} is syntactic sugar for (λ y. & B {Buy : (), Quit : ()}) (send B (price lookup x)).Here we need to have the abstraction on y even though it gets instantiated as ⊥ after Seller sends the result of price lookup x to B. If instead we only had (& B {Buy : (), Quit : ()}) (send B (price lookup x)), the first part of the application would not be a value, and would be waiting for B to choose between Buy and Quit while B has the abstraction on y and therefore considers the first part of the application a function which must wait to be instantiated.B therefore expects to receive the result of price lookup x, and we get a deadlock in our system.This is why we never want to project a value to a non-value term, and need to keep any abstractions guarding a part of the choreography involving Seller.

The Correctness of Endpoint Projection
We now show that there is a close correspondence between the executions of choreographies and of their projections.Intuitively, this correspondence states that a choreography can execute an action if, and only if, its projection can execute the same action, and both transition to new terms in the same relation.However, this is not completely true: if a choreography M reduces by rule [CaseL], then the result has fewer branches than the network obtained by performing the corresponding reduction in the projection of C.
In order to capture this we revert to the branching relation (Montesi, 2023;Cruz-Filipe and Montesi, 2020), defined by Using this, we can show that the EPP of a choreography can do all that (completeness) and only what (soundness) the original choreography does.For traditional imperative choreographic languages, this correspondence takes the form of one action in the choreography corresponding to one action in the projected network.We instead have a correspondence between one action in the choreography and multiple actions in the network due to allowing choreographies to manipulate distributed values in one action such as in λ x : Int @ Bob × Int @ Alice.M (3 @ Bob, 3 @ Alice) where both Bob and Alice independently take the first part of the pair.In the foregoing we use L to denote local expressions and U to denote local values in order to make the proofs more readable, as we will be switching back and forth between layers a lot.
Before we can prove completeness, we need a few lemmas.First, we show that choreographic values always project to local values.
Lemma 3.For any choreographic value V and process P, if Θ; Γ ⊢ V : τ then V P is either a value or undefined.
Proof Straightforward from the projection rules.
We then prove the same for type values.
Proof Straightforward from the projection rules.
We then show that type values are projected to ⊥ at uninvolved processes.
Lemma 5. Given a type value ν = P, for any process Proof Straightforward from induction on ν.
And similarly, we show that choreographic values project to ⊥ at processes not involved in their type.Lemma 6.Given a value V , if Θ; Γ ⊢ V : τ then for any process P / ∈ ip(τ), we have V P = ⊥ or V P is undefined.
Proof Follows from Lemmas 3 and 5 and the projection rules.
Finally, we show that equivalent types are projected to equivalent local types.
Proof We prove this by structural induction on τ ≡ τ ′ .All but one case follow by simple induction.
As with completeness, we need some ancillary lemmas before we can prove soundness.For this, we need a notion of removing processes from a network.Proof Straightforward from the network semantics.
Then we show that removing processes from a network does not prevent it from performing actions involving different processes.
Lemma 9.For any set of processes ρ and network N , if Proof Straightforward from the network semantics.
We finally show that if the projection of a choreographic type is equivalent to a local type value, then the original choreographic type is equivalent to a choreographic type value.
Proof We prove this by structural induction on τ 1 .All but one case follows from simple induction.

Choreographies
Choreographies are inspired by the "Alice and Bob" notation for security protocols by Needham and Schroeder (1978), which included a term for expressing a communication from a participant to another.The same idea inspired later several graphical notations for modelling interactions among processes, including sequence diagrams and message sequence charts (Object Management Group, 2017;International Telecommunication Union, 1996).
The first sophisticated languages for expressing choreographies were invented to describe interactions between web services.The Web Services Choreography Description Language (WS-CDL) by the W3C ((W3C), 2004) is a choreographic language which describes the expected observable interactions between web services from a global point of view (Zongyan et al., 2007).Carbone et al. (2012) later formalized endpoint projection for a theory of choreographies based on WS-CDL, and in particular introduced the merging operator (which we adjusted and extended to our setting).This inspired more work on choreographies and projection and eventually the birth of choreographic programming-where choreographies are programs compiled to executable code-and the first choreographic programming language, Chor (Montesi, 2013).As choreographic programming languages became more complex, Cruz-Filipe and Montesi (2020) developed a core calculus of choreographies (CC).Montesi (2023) revisited and generalised CC in his text on foundations of choreographic languages.Cruz-Filipe et al. (2021,?) then formalized this new version and its properties in the Coq theorem prover (The Coq development team, 2004).Later, Pohjola et al. (2022) developed a certified end-to-end compiler from another variation of CC to CakeML by using the HOL theorem prover.
One of the primary design goals of all of choreographic programming languages is deadlock-freedom by design (Carbone and Montesi, 2013)-the operational correspondence between the choreography and the distributed network ensures deadlock-freedom for the network.PolyChorλ achieves this goal.
The first choreographic language with limited process polymorphism was Procedural Choreographies (PC) (Cruz-Filipe and Montesi, 2017).In PC, a choreography comes with an environment of predefined procedures parametric on process names which may be called by the choreography.These procedures have a number of limitations compared to the process polymorphism of PolyChorλ : they cannot contain any free processes, they cannot be partially instantiated, and they are lower-order-that is, they must be defined in the environment rather than as part of a larger choreography.These limitations allow the projection function to know how the procedure will be instantiated, whereas in PolyChorλ we may need to compute the processes involved first.This has major implications for projection: in PC, it is easy to tell when projecting a procedure call which processes are involved and therefore need a local copy of the call.However, PolyChorλ 's full process polymorphism allows the function and process names to each be enclosed in a context.While this allows greater flexibility for programmers, it forces us to project a process-polymorphic functions to every process and let each process determine at runtime whether it is involved.
Recently, there has been a fair amount of interest in higher-order programming for choreographies (Giallorenzo et al., 2020;Hirsch and Garg, 2022;Cruz-Filipe et al., 2021).The first higher-order choreographic programming language, Choral (Giallorenzo et al., 2020), is an object-oriented choreographic language compiled to Java code.Thus, Choral choreographies can depend on other choreographies, allowing programmers to reuse code.Choral was also the first choreographic language to treat com τ P, Q as a first-class function.While Choral gave a practical implementation of higher-order choreographies, it did not establish their theoretical properties.Two different-but independently-developed-works filled this gap, including Chorλ , the basis of PolyChorλ .Chorλ is a functional choreographic calculus based on the λ -calculus.In this work, we extended Chorλ with type and process polymorphism and the ability to send non-local values such as choreographies.Chorλ , and hence PolyChorλ , provides a core language for higher-order choreographies, thus allowing us to establish their properties.Since the original Chorλ has parametric procedures like PC and Choral, it lacks PolyChorλ 's property that a choreography diverging in one process must diverge in every process.This forces Chorλ to have both a complex notion of out-of-order execution and a more lax correspondence between actions in the network and the choreography.
The other work establishing the theoretical properties of higher-order choreographic programming is Pirouette (Hirsch and Garg, 2022), which is also a functional choreographic programming language based on simply-typed λ calculus.Unlike Chorλ (and thus PolyChorλ ), Pirouette does not allow processes to send messages written in Pirouette.Instead, it takes inspiration from lower-order choreographic programming languages in which (the computations to produce) messages are written in their own separate language.Like the choreographic languages in (Montesi, 2023;Cruz-Filipe et al., 2021), Pirouette's design is parametrized by the language for writing messages.Thus, Pirouette can describe communication patterns between processes that draw from a large swath of languages for their local computations.Nevertheless, this design means that Pirouette fundamentally cannot allow programs to send choreographic functions, unlike PolyChorλ .
Moreover, unlike Chorλ and PolyChorλ , Pirouette forces every process to synchronize when applying a function.This conceit allows Pirouette to establish a bisimulation relation with its network programming language, a result formalized in Coq.This result allows a traditional-and verified-proof of deadlock-freedom by construction.However, this constant synchronization would be a bottleneck in real-world systems; PolyChorλ 's choice to obtain a weaker-but strong-enough-connection between the languages allows it to avoid this high cost.

Concurrent Functional Programming
Functional concurrent programming has a long history, starting with attempts to parallelize executions of functional programs (Burton, 1987).The first language for functional programming with communications on channels was Facile (Giacalone et al., 1989) which, unlike later choreographic languages, had an abstraction over process IDs very similar to process polymorphism.A more recent work, which more-closely resembles choreographic programming, is Links (Cooper et al., 2006), with the RPC calculus (Cooper and Wadler, 2009) as its core language.Links and the RPC calculus, like choreographies, allow a programmer to describe programs where different parts of the computation takes place at different locations and then compile it to separate code for each location.Interestingly, though Links has explicit communication, in the RPC calculus the top level does not, and communications are created only in the projection when projecting a function located at a different process.Moreover, the RPC calculus does not feature multiple threads of computation; instead, on communication the single thread of computation moves to a new location while other locations block.The RPC calculus was later extended with location polymorphism, very similar to our and Facile's process polymorphism (Choi et al., 2020).However, as the RPC calculus only deals with systems of 2 processes, a client and a server, they project a process abstraction as a pair, and then the location as picking the correct part of this pair.This solution creates a simpler network language but is not suitable for a framework with an arbitrary number of participants such as PolyChorλ .Moreover, the RPC calculus-like PolyChorλ but unlike traditional choreographic languages-does not have out-of-order execution at the top level.
Session types were applied to a concurrent functional calculus with asynchronous communication by Gay and Vasconcelos (2010).However, their session types do not give them a deadlock-freedom result.Instead, they get a runtime safety result: the only time a thread cannot perform an action is if it has terminated, is trying to read from an empty buffer, or is trying to start a new channel with another thread which isn't ready yet.
ML5 (Licata and Harper, 2010;Murphy VII et al., 2007) is a functional concurrent programming language based on the semantics of modal logic.However, instead of the send and recv terms of choreographic languages, they have a primitive get[w] M, which makes another process w evaluate M and return the result.Since M may include other gets, this construct gives ML5 something resembling PolyChorλ 's ability to send a full choreography.However, the result of evaluating this "choreography" must be at the receiver and then returned to the sender.
Multitier programming languages, like ScalaLoci (Weisenburger and Salvaneschi, 2020), offer another paradigm for describing global views of distributed systems.Like Choral, ScalaLoci is built on top of an existing object-oriented language: in this case, Scala.In ScalaLoci and other tierless languages, an object describes a whole system containing multiple processes and functions.Differently from choreographic programming, multitier programming does not allow for modelling the intended flow of communications.Rather, communication happens implicitly through remote function calls and the concrete protocol to be followed is largely left to be decided by a runtime middleware.For a more detailed comparison of choreographic and multitier languages, see the work of Giallorenzo et al. (2021).

Conclusion
In this paper, we presented PolyChorλ , the first higher-order choreographic programming language with process polymorphism.PolyChorλ has a type and kind system based on System Fω, but extended such that process names are types of kind Proc.Moreover, we showed how to obtain a concurrent interpretation of PolyChorλ programs in a process language by using a new construct corresponding by the ability of a process to know its identity.We found that this construct was necessary if process variables are able to be instantiated as the process they are located at, but using a choreographic language abstracts from this necessity.Our explorations of process polymorphism also allowed PolyChorλ to describe a communication of a non-local value from P to Q as sending the part of the message owned by P to Q.These non-local values include full choreographies, creating a simple and flexible way to describe delegation by communicating a distributed function describing the delegated task.This innovation required a new notion of communication as an exchange in which the delegator rather than being left with an empty value after sending a choreography is left with a function which will allow it to potentially take part in the delegated task, e.g. by receiving a result at the end.
Process polymorphism fills much of the gap between previous works on the theory of higher-order choreographies and practical languages.However, there is still more work to do.For instance, currently PolyChorλ does not support recursive types.In order to support recursive types, we would need to either make endpoint projection capable of projecting to a possibly-nonterminating description of a process, or limit recursive types ability to make type computations fail to terminate.
Furthermore, one can imagine allowing processes to send types and process names as well as values.This would, for example, allow us to program a server to wait to receive the name of a client which it will have to interact with.Since this form of delegation is common in practice, understanding how to provide this capability in a choreographic language, while retaining the guarantees of choreographic programming, would enable programmers to apply their usual programming patterns to choreographic code.
We project local type despite lacking a typing system for local processes.Our unusual network communication semantics have made it difficult to define local typing rules for sends and recvs, and we therefore leave local typing (or alternatively type erasure) as future work.
Finally, while certain, instant, and synchronous communication is convenient for theoretical study, such assumptions do not match real-world distributed systems.Cruz-Filipe and Montesi (2017) modeled asynchronous communication in choreographies via runtime terms representing messages in transit.We could adapt this method to PolyChorλ by having the communication primitive reduce in two steps: first to a runtime term and then to the delivered value.However, this extension would be nontrivial, since it is not clear how to represent messages in transit when those messages are non-local values such as choreographies.
While these gaps between theory and practice persist, process polymorphism in PolyChorλ brings us much closer to realistic choreographic languages for distributed systems.Choreographic programs promise to provide easier and cleaner concurrent and distributed programming with better guarantees.With higher-order choreographic programming and process polymorphism, the fulfilment of that promise is nearly within reach.

Theorem 3 (
Type Preservation).Let M be a choreography and D a function mapping containing every function in M. If there exists a typing context Θ; Γ such that Θ; Γ ⊢ M : τ and Θ; Γ ⊢ D, then Θ; Γ ⊢ M ′ : τ for any M ′ such that M → D M ′ .Proof Follows from the typing and semantic rules and Theorem 2. Theorem 4 (Progress).Let M be a closed choreography and D a function mapping containing every function in M. If there exists a typing context Θ; Γ such that Θ; Γ ⊢ M : τ and Θ; Γ ⊢ D, then either M = V or there exists M ′ such that M → D M ′ .

Definition 3 .
Given a network N = ∏ P∈ρ P[L P ], we have N \ ρ ′ = ∏ P∈(ρ\ρ ′ )P[L P ]First we show that actions in a network do not affect the roles not mentioned in the transition label.Lemma 8.For any process P and network N , if N τ P − − → d N ′ and P / ∈ P then N (P) = N ′ (P).

Corollary 1 .
and since X and ν ′ 3 are both base types, so are ν ′ 4 [X := ν 3 ] P and ν ′ 4 [X := ν ′ 3 ].We are then ready to prove soundness.Proof [Proof of Theorem 6] We prove this by structural induction on M. • Assume M = V .Then for any process P, M P = U, and therefore M τ P − − →. • Assume M = N 1 N 2 .Then for any process P such that N 1 P = N 2 P = ⊥, we have M P = ⊥.For any process P ′ such that P ′ ∈ ip(typeof(N 1 )) or N 1 P ′ = ⊥ = From Theorems 3, 4, 5, and 6, we get the following corollary, which states that a network derived from a well-typed closed choreography can continue to reduce until all roles contain only local values.Given a closed choreography M and a function environment D containing all the function names of M, if Θ; Γ ⊢ M : T and Θ; Γ ⊢ D, then: whenever M → * D N for some network N , either there exists P such that N τ P − − → D N ′ or N = ∏ P∈ip(M) P[V P ]. 1

,
Buyer 2 bank balance 1 in if x < bank balance 2 then select Buyer 2 ,Buyer 1 Me select Buyer 2 ,Seller Me seller service Buyer 2 (com Buyer 1 , Buyer 2 title) (λ z. z < bank balance 2 ) else select Buyer 2 ,Buyer 1 You select Buyer 2 ,Seller Them (seller service Buyer 1 title (λ z. z < x))