Keywords

1 Introduction

Extensions of the \(\lambda \)-calculus to include increasingly sophisticated type structures have been extensively studied and have led to systems whose importance is widely recognized: System F [60], System \(F^{\mu }\) [30], System \(F_{\omega }\) [36], System \(F^{\mu }_\omega \) [14]. Ideally, we would like to combine a wishlist of type structures and get a super-powerful system with vast expressiveness. However, the expressiveness of types is naturally limited by the universe where they are supposed to live: programming languages. Expressive type systems pose challenges to compilers that other (less expressive) types do not even reveal; one such example is type equivalence checking.

System F can be enriched with different type constructors for specifying communication protocols. We analyse the impact of combinations of such constructors on the type equivalence problem. In order to do so, we extend System F with session types [42, 43, 67]. Session types provide for detailed protocol specifications in the form of types. Traditional recursive session types are limited to tail recursion, thus failing to capture all protocols whose traces cannot be characterized by regular languages. Context-free session types overcome this limitation by extending types with a notion of sequential composition, [2, 68]. The set of types together with the binary operation constitutes a monoid, for which a new type, , acts as the neutral element and acts as an absorbing element.

The regular recursive type describes an integer stream as seen from the point of view of the consumer. It offers a choice between —after which the channel must be closed (as witnessed by type )—and —after which an integer value must be received, followed by the rest of the stream. Types are categorised by kinds, so that we know that the recursion variable is of kind session—denoted by —and, thus, can be used with semicolon. Instead, we might want to write a type with a more context-free flavour. The type describes a protocol for the type-safe streaming of integer trees on channels. The continuation to the option is , where no communication occurs but the channel is still open for further composition. The continuation to the choice receives a left subtree, an integer at the root and a right subtree. In either case, once the whole tree is received, the channel must be closed, as witnessed by the final . Beyond first-order context-free session types (where only basic types are exchanged) [2, 68] we may be interested in higher-order session types capable of exchanging values of complex types [19]. A goal of this paper is the integration of higher-order context-free session types into system \(F^{\mu }_\omega \). We want to be able to abstract the type that is received on a tree channel, which is now possible by writing , where is the kind of functional types.

A form of abstraction over session types with general recursion was proposed by Das et al.  [24, 25] via (nested) parametric polymorphism. In the notation of Das et al., we can write a type equation for abstracting the type being received on a stream channel . Using abstractions, we can write as a function of its parameter , ; alternatively, we can use the -operator to rewrite the type as Das et al. proved that parametrized type definitions over regular session types are strictly more expressive than context-free session types. To some extent, this analogy guides our approach: if adding abstraction (via parametric polymorphism) to regular types leads to nested types, what exactly does it mean to add abstraction (via a type-level \(\lambda \)-operator) to context-free types? Throughout this paper we analyse several increments to System \(F^\mu \) that culminate in adding \(\lambda \)-abstraction to context-free session types.

One of our focuses is necessarily the analysis of the type equivalence problem. The uncertainty about the decidability of this problem over recursive parametric types goes back to the 1970s [16, 63]. Although the type equivalence problem for parametric (nested) session types and context-free session types is decidable, that for the combination of abstractions over context-free types may no longer be. In fact, this analysis constitutes an interesting journey towards a better understanding of the role of higher-order polymorphic recursion in presence of sequential composition, as well as the gains (and losses) resulting from combining abstraction with arbitrary (rather than tail) recursion.

Ultimately, decidability is not a sufficiently valuable measure regarding a type system’s practicality. We look for type systems that may be incorporated into compilers. For that reason, we are interested in algorithms for type equivalence checking. Equivalence in \(F^{\mu }_\omega \) alone is already at least as hard as equivalence of deterministic pushdown automata. If we restrict recursion to the monomorphic case (requiring recursion variables to denote proper types, that is of kind or , collectively denoted by ) we lower the complexity of type equivalence to that of equivalence for finite-state automata. The extension with context-free session types is slightly more complex. In order to obtain “good” algorithms, we restrict the recursion to the monomorphic case, arriving at classes \(F^{\mu _*}_\omega , F^{\mu _*;}_\omega \). Now the type equality problem for \(F^{\mu _*;}_\omega \) translates to the equivalence problem for simple grammars, which is still decidable [4, 33]. Since \(F^{\mu _*;}_\omega \) subsumes \(F^{\mu _*}_\omega \), our proof of the decidability of type equivalence serves as an alternative to that of Cai et al.  [14] (restricted to contractive types).

Higher-order polymorphism allows for the definition of type operators and the internalisation of various (session-type) constructs that would otherwise be offered as built-in constructors. In this way, we are able to internalise basic session-type constructors such as sequential composition and the type operator (which reverses the direction of communication between parties). Duality is often treated as an external macro. Gay et al. [34] explore different ways of handling the dual operator, all in a monomorphic setting. In the presence of polymorphism the dual operator cannot be fully eliminated without introducing co-variables. Internalisation offers a much cleaner solution.

Due to the presence of sequential composition, regular trees are not a powerful enough model for representing types ( in Section 2 is an example). The main technical challenge when combining System \(F^{\mu }_\omega \) and context-free session types is making sure that the resulting model can still be represented by simple grammars, so that type equivalence may be decided by a practical algorithm. The difficulties arise with renaming bound variables. For infinite types, both renaming with fresh variables and using de Bruijn indices may create an infinite number of distinct variables, which makes the construction of a simple grammar simply impossible. For example, take the type , which stands for the infinite type Renaming this type using a fresh variable at each step would result in a type of the form , requiring infinitely many variables. Similarly, de Bruijn indices [27] yield a type of the form that requires an infinite number of natural indices. We thus introduce minimal renaming that uses the least amount of variable names as possible (cf. Gauthier and Pottier [30]). This ensures that only finitely many terminal symbols are necessary, allowing for translating types into simple grammars.

Type languages live in term languages and we propose a term language to consume \(F^{\mu ;}_\omega \) types. Based on Almeida et al.  [2], we introduce a message-passing concurrent programming language. Type checking is decidable if type equivalence is, and it is, in particular, for \(F^{\mu _*;}_\omega \).

The main contributions of this paper are as follows.

  • The integration of (higher-order) context-free session types into system \(F^{\mu }_\omega \), dubbed \(F^{\mu ;}_\omega \).

  • A semantic definition of type equivalence via a labelled transition system.

  • The identification of a suitable fragment of System \(F^{\mu ;}_\omega \) for which type equivalence is reduced to the bisimilarity of simple grammars.

  • A proof that type equivalence on the full System \(F^{\mu ;}_\omega \) is at least as hard as bisimilarity of deterministic pushdown automata.

  • The first internalisation of the type operator in a type language.

  • A term language to consume \(F^{\mu ;}_\omega \) types and an accompanying metatheory.

The type system presented in the paper combines three constructions: sequential composition of session types, higher-order kinds via type-level abstraction and application, and higher-order recursion. Prior to our work there is the system by Almeida et al. [4] which incorporates sequential composition and (first-order) recursion, but no higher-order kinds. There is also the system by Cai et al.  [14] which incorporates higher-order kinds and higher-order recursion, but no sequential composition. Our system is the first to incorporate all three constructions. Although some of the results are incremental and generalize results from the literature, the main technical challenge is understanding the border past which they do not hold anymore. For example, “just” including higher-order kinds into the system by Almeida et al. does not work, since we need to pay close attention to variable names, making sure that type equivalence is invariant with respect to alpha-conversion (renaming of bound variables). This called for a novel notion of renaming, inspired by Gauthier and Pottier [30]. Similarly, “just” including sequential composition into the system of Cai et al. does not work, since finite-state automata (or regular trees) are not enough to capture the expressive power of the new type system, even when restricted to first-order recursion. This required us to look at the more expressive framework of simple grammars, and introduce a translation from types to words of a simple grammar.

The rest of the paper is organised as follows. The next section motivates the type language and introduces the term language with an example. Section 3 introduces System \(F^{\mu ;}_\omega \), Section 4 discusses type equivalence and Section 5 shows that type equivalence is decidable for a fragment of the type language. Section 6 presents the term language and its metatheory. Section 7 discusses related work and Section 8 concludes the paper with pointers for future work. Proofs for the main results can be found in a technical report on arXiv [20].

Fig. 1.
figure 1

Six F-systems.

2 Motivation

Our goal is to study type systems that combine equirecursion, higher-order polymorphism, and higher-order context-free session types, while incorporating these in programming languages.

Extensions of System F. Figure 1 motivates the construction by proposing six different type languages, culminating with \(F^{\mu ;}_\omega \). The initial system, \(F^{\mu }\), includes well-known basic type operators [57]: functions , records and variants . Type is short for , the empty record; we can imagine that stands in place of an arbitrary scalar type such as and . We also include variable names , type quantification and recursion . To control type formation, all variable bindings must be kinded with some kind , even if for the initial system, \(F^{\mu }\), we only use the functional kind .

We then build on \(F^{\mu }\) by considering (regular, tail recursive) session types; we represent the resulting system by \(F^{\mu \cdot }\). For example is a type for a channel endpoint that receives an integer, sends a boolean, and terminates. At this point we introduce a kind of session types to restrict the ways in which we can combine session and functional types together. For example, a well-formed type is of kind and requires to be also of kind (whereas can be of kind , that is or ). An example of an infinite session type is that endlessly outputs integer values. For a more elaborate example consider the type that specifies a channel endpoint for receiving a (finite or infinite) stream of integer values. Communication ends after choice is selected.

The next step of our construction takes us to context-free session types; the resulting system is denoted by \(F^{\mu ;}\). We introduce a new construct for sequential composition , and a new type , acting as the neutral element of sequential composition [68]. The message constructors are now unary ( and ) rather than binary. In System \(F^{\mu ;}\) we distinguish between the traditional type and the type. These types have different behaviours: terminates a channel, while allows for further communication. Type equality is more subtle for context-free session types, because of the monoidal semantics of sequential composition. It is derivable from the following axioms:

figure bs

Although the syntax of \(F^{\mu \cdot }\) is not formally included in the syntax of \(F^{\mu ;}\), we can embed recursive session types into context-free session types by mapping into . It is well-known that context-free session types allow for higher computational expressivity: while \(F^{\mu }\) and \(F^{\mu \cdot }\) can be represented via finite-state automata, \(F^{\mu ;}\) can only be represented with simple grammars [4, 33].

To finalise our construction, we include type abstraction and type application . Again, type abstraction binds a variable which must be kinded. Kinds can now be of higher-order . For each of the three systems \(F^{\mu }\), \(F^{\mu \cdot }\), \(F^{\mu ;}\) we arrive at a higher-order version, respectively \(F^{\mu }_\omega \), \(F^{\mu \cdot }_\omega \), \(F^{\mu ;}_\omega \) (all of which we represent as \(F^{M}_\omega \)). In System \(F^{\mu \cdot }_\omega \), for example, we can specify channels for receiving (finite or infinite) sequences of values of arbitrary (but fixed) types,

figure by

where can be instantiated with the desired type; in particular, would be equivalent to the aforementioned .

It turns out that the expressive power of general higher-order systems \(F^{M}_\omega \) is too large for practical purposes. Even the simplest case \(F^{\mu }_\omega \) is at least as expressive as deterministic pushdown automata (or equivalently, first-order grammars), for which known equivalence algorithms are notoriously impractical. By impractical we mean that, although there exists a proof of decidability (due to Sénizergues [61], later improved by Stirling and Jancar [46, 65]), the underlying algorithm is rather complex. To the best of our knowledge, there is no practical implementation of an algorithm to decide the equivalence of deterministic pushdown automata. This is essentially due to polymorphic recursion, which can be encoded by a higher-order \(\mu \)-operator (we provide an example at the end of Section 5). Therefore, it makes sense to restrict the kind of the recursion operator . We use the notation \(\mu _*\) to mean the subclass of types written using only -kinded recursion, i.e., or .

Fig. 2.
figure 2

Relation between the main classes of types in this paper (arrows denote strict inclusions).

Figure 2 summarizes the main relations between the classes of types in our paper. Firstly, we obtain a lattice where the expressive power increases as we travel down (from functional to session to context-free session types) and right (from simple polymorphism to higher-order polymorphism with monomorphic recursion to arbitrary recursion). Four of the classes can be represented using finite-state automata (up to \(F^{\mu _*\cdot }_\omega \)). By including sequential composition (\(F^{\mu ;}\) and \(F^{\mu _*;}_\omega \)) we are still able to represent types using simple grammars. Once we allow for arbitrary recursion, the expressiveness of our model requires the computational power of deterministic pushdown automata.

Programming with \(F^{\mu ;}_\omega \). We now turn our attention to the term language, a message passing, concurrent functional language, equipped with context-free session types. Start with a stream of values of type a. Such a stream, when seen from the side of the reader, offers two choices: Done and More. In the former case the interaction is over; in the latter the reader reads a value of type a, as in ?a, and recurses. This is the stream type we have seen before only that, rather than closing the channel endpoint (with type ), it terminates with type , so that it may be sequentially composed with other types. In this informal introduction to the term language we omit the kinds of type variables.

figure cj

A fold channel, as seen from the side of the folder, is a type of the following form. We assume that application binds tighter than semicolon, that is, type is interpreted as .

figure cm

Consumers of this type first receive the folding function, then the starting element, then the elements to fold in the form of a stream, and finally output the result of the fold. The type terminates with for we do not expect type Fold to be further composed. Compare Fold with the type for a conventional functional left fold: .

We now develop a function that consumes a Fold channel. Syntax is for the inverse function application with low priority, that is . Recall that is an alternative notation for the empty record type, {}.

figure cs

Function foldServer consumes the initial part of the channel and passes the rest of the channel to the recursive function foldS that consumes the whole stream while accumulating the fold value. In the end, when branch Done is selected, the fold value is written on the channel and the channel closed. In general, the channel operators— , , —return the same channel in the form of a new identifier. It is customary to reuse the identifier name—c in the example, as in —since it denotes the same channel. Syntax hides the continuation channel. The case for the external choice— —also returns the continuation (in each branch) so that interaction on the channel endpoint may proceed.

We may now write different clients for the foldServer. Examples include a client that generates a stream from a pair of integer values (denoting an interval); another that generates the stream from a list of values; and yet another that generates the stream from a binary tree. We propose a further client. Consider the type of a channel that exchanges trees in a serialized format [68]. Its polymorphic version, as seen from the point of view of the reader, is as follows:

figure cz

We transform trees as we read from tree channels into streams. Function flatten receives a tree channel and a stream channel (as seen from the point of view of the writer, hence the ) and returns the unused part of the latter.

figure db

We are now in a position to write a client that checks whether all values in a tree channel are positive.

figure dc

The client sends a function and the starting value on the fold channel. Then, it flattens the given tree t, receives the folded value and closes the channel. Syntax is for term-level type application. We mean to flatten a tree of values on a stream channel whose continuation is of type . The continuation channel is bound to so that we may further receive the fold value and thereupon close the channel. Syntax is for sequential composition and abbreviates given that {}, the value, is linear and hence must be consumed.

Finally, a simple application creates a new channel, passing one end to a thread that produces a tree channel. Function creates a channel and returns its two ends. It then creates a channel, distributes one end to a thread foldServer and the other to function allPositive. The primitive receives a suspended computation (a thunk, of the form ) and creates a new thread that runs in parallel with that from where the was issued.

figure dq

3 Kinds and Types

Fig. 3.
figure 3

The syntax of types.

Fig. 4.
figure 4

Type constants and kinds.

This section introduces in detail System \(F^{\mu ;}_\omega \), an extension of System \(F^{\mu }_\omega \) incorporating higher-order context-free session types. The syntax of types is presented in Fig. 3. A type is either a constant  (as in Fig. 4), a type variable , an abstraction or an application . Besides incorporating the standard session type constructors as constants, system \(F^{\mu ;}_\omega \) also includes as a constant for a type operator mapping a session type to its dual. Note also that is syntactic sugar for . Analogously, abbreviates . This simplifies our analysis as lambda abstraction becomes the only binding operator.

A distinction between session and functional types is made resorting to kinds and , respectively. These are the kinds of proper types, ; we use the symbol to represent either the kind of a proper type or that of a type operator, of the form . A kinding context \(\varDelta \) stores kinds for type variables using bindings of the form . Notation denotes the update of kinding context \(\varDelta \), defined as and when .

Fig. 5.
figure 5

Type renaming.

To define type formation, we require a few notions. Firstly comes the notion of renaming, adapted from Gauthier and Pottier [30] and presented in Fig. 5. Renaming essentially replaces a type by a minimal alpha-conversion of . By alpha-conversion we mean that renames bound variables in . By “minimal” we mean that each bound variable is renamed to its lowest possible value. We assume at our disposal a countable well-ordered set of type variables . In , parameter S is a set containing type variables unavailable for renaming; in the outset of the renaming process S is the empty set, since all variables are available. In that case the subscript S is often omitted. The case for lambda abstraction renames the bound variable by the smallest variable not in the set , which we denote by .

Renaming is what allows us to check whether type abstractions , are equivalent. For the types to be equivalent, both bound variables and ought to be renamed to the same variable . In summary, renaming provides a syntax-guided approach to the equivalence of lambda-abstractions, where the names of bound variables should not matter. Our notion of type equivalence preserves alpha-conversions up to renaming: if and only differ on bound variables, then and in particular . We will come back to this point after we define type equivalence in Section 4.

We can easily see that renaming uses the minimum amount of variable names possible; for example, . Notice how both bound variables and are renamed to , the first variable available for replacement. Also, renaming blatantly violates the Barendregt’s variable convention [9] used in so many works; for example , where variable is both free and bound in the resulting type. Even if renaming violates the variable convention, substitution can still be performed without resorting to the “on-the-fly” renaming of Curry and Feys [21, 40]. When , we have that

figure fi

Then, we have since the renaming rule for application guarantees that . Otherwise if , we have . This justifies the inclusion of set S in the renaming process. From now on, we assume that all types have gone through the renaming process.

Fig. 6.
figure 6

Type reduction.

Next comes the notion of type reduction (Fig. 6). Apart from beta reduction (rule R-\(\beta \)), the definition provides for sequential composition, for unfolding recursive types and for reducing   types. Note that renaming is further invoked in rule R-\(\beta \) for beta reduction does not preserve renaming: consider the renamed type   . The type resulting from the substitution   is which is not renamed and, therefore, not equivalent to   according to our rules in Section 4. Thanks to our modified rule R-\(\beta \), we preserve renaming under reductions: if and then .

We also need the notion of weak head normal form borrowed from the lambda calculus [9, 10]. We say that a type is in weak head normal form, , if it is irreducible, i.e., . Although this is a negative definition, in the technical report we provide an equivalent, rule-based characterisation of weak head normal form types, which can be used in a compiler as well as in our proofs. We say that type normalises to type   , written , if and is reached from in a finite number of reduction steps (note that any term which is already whnf normalises to itself). We write to denote that for some .

For example, suppose we want to normalise the type , where   is the type   . By computing all reductions from , we obtain for which we conclude that . Similarly, we can reason that   ,   and   are all examples of non-normalising expressions.

Fig. 7.
figure 7

Type formation.

Equipped with normalisation, we can introduce type formation, which we do via the rules in Fig. 7. Rule K-Const introduces constants as types whose kinds match those of Fig. 4. Rule K-Var reads the kind of a type variable from context \(\varDelta \). An abstraction is a well-formed type with kind if is well formed in context \(\varDelta \) updated with entry (rule K-TAbs). The update is necessary since we are dealing with renamed types and the same type variable may appear with different kinds in nested abstractions.

It is not until we reach rule K-TApp that we find a proviso about the normalisation of a type. This is standard and analogous to a condition on contractivity. The goal is to eliminate types that reduce indefinitely without reaching a whnf.

Theorem 1

Let .

  • Preservation. If , then .

  • Confluence. If and , then and .

  • Weak normalisation. for some . Furthermore, if , then .

We finally arrive at the main decidability result in this section. In its proof, we make use of the fact that recursion is restricted to kind to limit the possible subexpressions of the form that might appear in the normalisation of .

Theorem 2 (Decidability of type formation)

is decidable for types in \(F^{\mu _*;}_\omega \).

4 Type equivalence

This section introduces type bisimulation as our notion of type equivalence. We define a labelled transition system (LTS) on the space of all types and write to denote that has a transition by label to . The grammar for labels and the LTS rules are in Fig. 8.

Fig. 8.
figure 8

Labelled transition system for types.

If is not in weak head normal form, then we must normalise it to some type , so that has the same transitions as (rule L-Red). Otherwise if , then the transitions of can be immediately derived by looking at the corresponding rule for as follows. If is a variable, use rule L-Var1 (with \(m=0\)). If is a constant (other than ), use rule L-Const. Note that if is a lone , then it has no transitions. If is an abstraction, use rule L-Abs.

If is an application, then we need to look inside the head. We write as with \(m\ge 1\) where is not an application, and look at . If is a variable, use rules L-Var1 and L-Var2. If is one of the constants , , or , use rule L-ConstApp. Note that is neither an abstraction nor , since is in weak head normal form. If is , we use rules L-Msg1 and L-Msg2. If is , then the only way for to be well-formed and in weak head normal form is if \(m=1\) and is or , in which case we use rules L-DualVar1 and L-DualVar2.

If is , we require an additional case analysis on . If \(m=1\), use rule L-Seq1. Otherwise \(m=2\) due to kinding. If is a variable, use rule L-VarSeq1 (with \(m=0\)). If is a constant, then it must be of kind . cannot be , because is in weak normal form, so it must be , in which case we use rule L-EndSeq ( is an absorbing element, so simply makes a transition to without executing ). If is . Note that cannot be an abstraction due to kinding.

If is an application, then again we write as with \(n\ge 1\) where the head is not an application, and look at . If is a variable, use rules L-VarSeq1 and L-VarSeq2. If is a constant, it must be one of , , , or due to kinding. If is , use rules L-MsgSeq1 and L-MsgSeq2. If is , use rule L-ChoiceSeq. If is , the only way for to be well-formed and in weak head normal form is if \(n=1\) and is or , in which case we use rules L-DualSeq1 and L-DualSeq2. Note that cannot be , or an abstraction, since is in weak normal form.

Fig. 9.
figure 9

The LTS for type . Normalisation is represented as and is a shorthand for type .

Let us clarify our LTS rules with an example. Consider the following type and call it . is a type abstraction (on type variable ), of kind . It specifies a channel alternating between: offer a choice and output a value of type ; or select a choice and input a value of type . The polarity is swapped thanks to the application of constant to the recursion variable . To construct the (fragment of the) LTS generated by this type, let us first desugar into where is the type . Notice that normalises to . The LTS for the example is sketched in Fig. 9. In this case, only finitely many types appear. However, more elaborate examples involving sequential composition or higher-order recursion may lead to an infinite graph of transitions.

Given the LTS rules, we can define, in the standard way, a notion of bisimulation. A binary relation R on types is called a bisimulation if, for every and every transition label :

  1. 1.

    if , then there exists s.t. and ;

  2. 2.

    if , then there exists s.t. and .

We say that types and are bisimilar, written , if there exists a bisimulation R such that .

Intuitively, a notion of type equivalence must preserve and reflect the syntax of type constructors: for example, a type is equivalent to a type iff , are equivalent and , are equivalent. Using the bisimulation technique, we achieve this by considering a labelled transition system on types: has a transition labelled to and a transition labelled to . In this way, can only be equivalent to another type which has two transitions with those same labels. For each of the type constructors ( , , , , , and so on) we have suitable transition rules. Moreover, a type sometimes needs to be reduced before a type constructor is found at the root of the syntax tree. If normalizes to , then we expect and to be bisimilar, which is achieved thanks to rule L-Red. This handles the various reductions: beta-reductions arising from lambda-abstraction and applications (e.g., reduces to ), reductions arising from the monoidal structure of sequential composition (e.g., reduces to ), reductions arising from the internalisation of duality as a type constructor (e.g., reduces to ) and reductions arising from the recursion (e.g., reduces to ).

Our notion of type equivalence enjoys natural properties and behaves as expected with respect to the notions of reduction, normalisation and kinding from Section 3. We can derive rules for type equivalence, that could be used to define another coinductive notion of equivalence, via effective syntax-directed rules. We can show that type equivalence is preserved under renaming, reduction and normalisation. We can also show that the axioms for sequential composition in the introduction (1) are derivable from our notion of bisimulation. These additional results are presented in the technical report [20].

5 Decidability of type equivalence

This section presents results on decidability of type equivalence. Our approach consists in translating types to objects in some computational model. We look at finite-state automata (for types in \(F^{\mu }\), \(F^{\mu _*}_\omega \), \(F^{\mu \cdot }\), and \(F^{\mu _*\cdot }_\omega \)), simple grammars (for types in \(F^{\mu ;}\) and \(F^{\mu _*;}_\omega \)) and deterministic pushdown automata (for types in \(F^{\mu }_\omega \), \(F^{\mu \cdot }_\omega \) and \(F^{\mu ;}_\omega \)).

We say that a grammar in Greibach normal form is a tuple where: \(\mathcal T \) is a set of terminal symbols, denoted by ; \(\mathcal N \) is a set of nonterminal symbols, denoted by ; is the starting word; and \(\mathcal R \subseteq \mathcal N \times \mathcal T \times \mathcal N ^*\) is a set of productions. A grammar is said to be simple if, for every nonterminal and every terminal , there is at most one production [51].

Greek letters and denote (possibly empty) words of nonterminal symbols. Productions are written as . We define a notion of bisimulation for grammars via a labelled transition system. The system comprises a set of states \(\mathcal N ^*\) corresponding to words of nonterminal symbols. For each production and each word of nonterminal symbols , we have a labelled transition . We let \(\approx \) denote the bisimulation relation for grammars (the definition is similar to that in Section 4).

For the moment we focus on the class \(F^{\mu _*;}_\omega \) and we explain how to convert a type into a simple grammar . The conversion is based on a function that maps each type into a word of nonterminal symbols, while introducing fresh nonterminals and productions. In our construction, following the approach by Costa et al.  [19], we use a nonterminal symbol with no productions, denoted by , in order to separate the two descendants of a send/receive operation such as . The sequence of nonterminal symbols is defined as follows. First consider the cases in which .

  • For any \(m\ge 0\): for a fresh nonterminal symbol with a production as well as for each \(1\le j\le m\).

  • .

  • for a fresh symbol with a single production .

  • for any : for a fresh nonterminal symbol with a single production .

  • for a fresh symbol with a production .

  • for any \(m\ge 1\) and for one of , , , : for a fresh nonterminal with a production for each \(1\le j\le m\).

  • for fresh with productions and .

  • for a fresh symbol with a production .

  • .

  • for a fresh symbol with productions and .

Finally, let us handle the cases where is not in weak head normal form.

  • If , then .

  • Otherwise if , then for a fresh nonterminal symbol. Let . Then has a production for each production .

In the above construction, we create fresh symbols each time we encounter a weak head normal form other than . In other words, is the set containing and all nonterminals created during the computation of . Another key insight is that the sequential composition of types is translated into a concatenation of words: . This allows our construction to terminate: even if the transitions lead to infinitely many types, they are split on the sequential composition operator, and so we only need to consider finitely many subexpressions.

For the last case in our construction to be well-defined, i.e., when , we require to be non-empty. Indeed, if , then we can observe (by inspecting all cases) that iff . We also need to argue that the construction of eventually terminates. For this, we keep track of all types visited during the construction, and we only add a fresh nonterminal to our grammar if the type visited is syntactically different from all types visited so far. Therefore, we reuse the same symbol with the same productions each time we revisit a type. With all these observations, we get the following result.

Lemma 1

Suppose that . Then the construction of terminates producing a simple grammar.

We illustrate the above construction with the polymorphic tree exchanging example from Section 2,

figure qc

that is written in \(F^{\mu _*;}_\omega \) as For ease of notation, in this example we write as shorthand for . Since is in weak head normal form, returns a fresh symbol, which we call . We also have a production , where is the type . Since is not in whnf, we must normalise it, to get . Therefore returns a fresh symbol, which we call . To obtain the transitions of , we must first compute , which is a fresh symbol with transitions and . Thus we also get and .

We have , but we still need to compute . This type normalises to since . Thus is a fresh symbol . To obtain the productions of we must compute . At this point we already have and . We still need to compute , which is a fresh symbol with productions and . In turn, is a fresh symbol with a production . Finally, we get , which means we can write the productions for : and .

Putting all this together, we can finally obtain the simple grammar:

Next, we argue that type equivalence (i.e., bisimilarity on types) corresponds to bisimilarity on the corresponding grammars. This is achieved by the following lemma, that asserts that the LTS of a type and the LTS of the corresponding word of nonterminals have exactly the same transitions.

Lemma 2 (Full abstraction)

Let and the corresponding simple grammar. Suppose also that .

  1. 1.

    If then there exists such that and .

  2. 2.

    If then there exists such that and .

As a consequence of the above result, we get soundness and completeness of the bisimilarity with respect to the bisimilarity . Indeed by Lemma 2, any sequence of transitions starting from can be matched by a sequence of transitions starting from ; and similarly for . Thus iff .

Theorem 3

The type equivalence problem is decidable for types in \(F^{\mu _*;}_\omega \).

For the remainder of this section, we look at the other classes of types in Fig. 2 and examine the computation models they correspond to. Since class \(F^{\mu ;}\) is contained in \(F^{\mu _*;}_\omega \), we can express types without \(\lambda \)-abstractions with simple grammars as well. In this way we recover previous results in the literature [4, 19].

Let us now look at the class \(F^{\mu _*\cdot }_\omega \). In this class we do not have nor sequential composition and message operators are binary ( ) rather than unary. Since we do not have sequential composition, there is no need to consider words of nonterminals, and instead it suffices to translate types into single symbols, i.e., states in an automaton. Moreover, since there is no recursion beyond , only finitely many types can be reached from a given . We can thus adapt our construction as follows for \(F^{\mu _*\cdot }_\omega \). In the definition of the LTS (Fig. 8):

  • discard all rules involving sequential composition;

  • discard rules L-Var1 for \(m>0\) and L-DualVar2 (they were only needed to distinguish types in sequential composition);

  • discard case in rule L-Const (so that no longer has transitions);

  • replace with on the right-hand side of rules L-Var1 with \(m=0\) and L-Const;

  • discard rules L-Msg1 and L-Msg2 and treat like the other constants in rule L-ConstApp.

Also replace the construction of into a construction of , associating to each type a state in a finite-state automata. For each transition we have the corresponding transition . Notice that the resulting automata is deterministic since the original LTS is also deterministic (for each type and label , there is at most one transition ). Since bisimilarity of deterministic finite-state automata can be decided in polynomial time [44], we get the following results.

Theorem 4

 

  1. 1.

    To each type in \(F^{\mu _*\cdot }_\omega \) we can associate a finite-state automata corresponding to the (fragment of the) LTS generated by .

  2. 2.

    The type equivalence problem is polynomial-time decidable for types in \(F^{\mu _*\cdot }_\omega \).

Clearly, Theorem 4 applies to the subclasses of \(F^{\mu _*\cdot }_\omega \): \(F^{\mu }\), \(F^{\mu \cdot }\) and \(F^{\mu _*}_\omega \). In this way we recover previous results in the literature [14, 19, 33].

Finally, we consider the classes \(F^{\mu }_\omega \), \(F^{\mu \cdot }_\omega \) and \(F^{\mu ;}_\omega \) involving arbitrarily-kinded recursion. We shall show that these classes are already powerful enough to simulate deterministic pushdown automata; hence, the type equivalence problem becomes impractical (i.e., no practical implementation of an algorithm is known). We only focus on the simplest case \(F^{\mu }_\omega \), as the others two classes are even more expressive. Instead of looking at deterministic pushdown automata, we look at deterministic first-order grammars, which constitute an equivalent model of computation [46]. This choice simplifies our construction. We say that a first-order grammar is a tuple where:

  • \(\mathcal X \) is a set of variables ; \(\mathcal T \) is a set of terminal symbols ; \(\mathcal N \) is a set of nonterminal symbols

  • each nonterminal has an arity .

  • the set \(\mathcal E \) of expressions over \(\mathcal X \), \(\mathcal N \) is inductively defined by two rules: any variable is an expression; if and are expressions, then so is . Whenever \(m=0\), is called a constant.

  • is an expression over \(\mathcal N \), called the initial expression.

  • \(\mathcal R \) is a set of productions. Each production is a triple , written as , where and the variables in must be taken from .

A first-order grammar is deterministic if, for every and , there is at most one production .

Just as a simple grammar defines an LTS over words of nonterminals, a first-order grammar defines an LTS over the set \(\mathcal E _0\) of closed expressions. For each production we have the labelled transition .

Let denote bisimilarity over closed expressions according to a first-order grammar. We now present a fully abstract (i.e., preserving bisimilarity) translation of a deterministic first-order grammar into a type in \(F^{\mu }_\omega \). Each grammar variable has a corresponding type variable (of kind  ). An expression is represented as a type application . If has arity m and the productions for a range of j, then we write the equation specifying as a record (since the first-order grammar is deterministic, all record labels are distinct, and thus the right-hand side on the equation specifying is well-formed).

figure uj

This gives rise to a system of equations , one for each nonterminal , where the nonterminals may appear in the right-hand sides . Finally, given an initial expression , it is standard how to convert it into a \(\mu \)-type using the system above.

Using the above translation, we are able to simulate a transition of the first-order grammar as a transition on the corresponding types. Therefore, the translation is fully abstract and we get the following result.

Theorem 5

Let and be closed expressions on a first-order grammar and the corresponding types. Then iff .

Let us work on an example to better understand the above translation. Consider the language \(L_3 = \{\ell ^nar^na \mid n\ge 0\} \cup \{\ell ^nbr^nb \mid n\ge 0\}\) over the alphabet \(\{a,b,\ell ,r\}\). \(L_3\) is a typical example of a language that cannot be described with a simple grammar, but can be accepted by a deterministic pushdown automaton [51]. Consider the first-order grammar with nonterminals , initial expression , and productions

figure ux

Note that is a constant without productions. It is easy to see that the traces of this first-order grammar correspond exactly to the words in \(L_3\). By following the steps in the above translation, we arrive at the system of equations

figure uz

Therefore, the initial expression becomes the type

figure vb

whose transitions simulate the transitions of the first-order grammar.

Fig. 10.
figure 10

Terms and types for term constants.

6 The term language and its metatheory

This section briefly introduces a concurrent functional language equipped with \(F^{\mu _*;}_\omega \) types, together with its metatheory. The results mostly follow from those in the literature, although explicit recursion at the term level and the unrestricted bindings in typing contexts are somewhat new in session types. The complete set of rules is to be found in the technical report [20].

The syntax of terms and processes is defined by the grammar in Fig. 10. The same figure introduces types for the constants. The term language is essentially the polymorphic lambda calculus with support for session operators, formulated as in Almeida et al. and Cai et al.  [2, 14]. From System F it comprises terms and type abstractions, records and variants, including constructors and destructors in each case. The support for session operations and concurrency includes channel creation ( ), the different channel operations ( , , , and ) and thread creation ( ). We program at the term level and use processes only for the runtime. Processes include terms as threads, parallel composition and channel creation, all inspired in the pi-calculus with double binders [73].

Fig. 11.
figure 11

Typing (excerpt).

Process typing and an excerpt of term typing is in Fig. 11. A judgement of the form records the fact that term has type under contexts \(\varDelta \) (recording kinds for type variables) and \(\varGamma \) (recording types for term variables). The judgement for processes, , says that is well-typed under context \(\varGamma \). It simplifies that for terms, since processes feature no free type variables and are assigned no particular type. Once again, the rules are adapted from the two above cited works. The difference to Cai et al. is that we work in a linear setting and hence axioms (T-Const and T-Var) work on an empty context, and most of the other rules must split the context accordingly. Rule T-TAbs simplifies that of Cai et al.; we can easily show that both rules are interchangeable. We support exponentials [37] for recursive functions, so that one may write functions that feature more than one recursive call (good for consuming binary trees, for example) and branches that do not use the recursive function (for code that is supposed to terminate). Towards this end, we add an unrestricted binding in term variable contexts, an explicit rule for (as opposed to making a constant as in Cai et al.  [14]) and substructural rules for unrestricted bindings (T-Dereliction, T-Weakening and T-Contraction).

Thanks to the power of System F, most of the session and concurrency operators are expressed as constants. For example, receives a session type with , the payload of the message, an arbitrary type and , the continuation, a session type, and returns a pair of the value received and the continuation channel. As usual abbreviates the type . The exception is the external choice (T-Match) which can not be captured by a type (similarly to T-Case) and hence requires a dedicated typing rule.

Fig. 12.
figure 12

Process reduction.

Process reduction is in Fig. 12. Following Milner [55] we factor out processes by means of a structural congruence relation that accounts for the associative and commutative nature of parallel composition, scope extrusion and exchanging the order of channel bindings. We now address the metatheory of our language, starting with preservation for both terms and processes.

Theorem 6 (Preservation)

  1. 1.

    If and , then .

  2. 2.

    If and , then .

  3. 3.

    If and , then .

Progress for the term language is assured only when the typing context contains channel endpoints only. When \(\varDelta \) is understood from the context we write to mean that \(\varGamma \) contains only types of kind , that is for all types in \(\varGamma \). Well typed terms are values, or else they may reduce or are ready to reduce at the process level. Reduction in the case of session operations— , , , , —is pending a matching counterpart.

Theorem 7 (Progress for the term language)

If , then is a value, reduces, or is stuck in one of the following forms: , , , , , , or .

In order to state our result on the absence of runtime errors we need a few notions on the structure of terms and processes; here we follow Almeida et al.  [2]. The subject of an expression , denoted by , is in the following cases.

figure xd

Two terms and agree on channel , notation , in the following cases (symmetric forms omitted).

figure xi

A closed process is a runtime error if it is structurally congruent to some process that contains a subexpression or subprocess of one of the following forms.

  1. 1.

    where is not a or a and , , , , , ;

  2. 2.

    where in not a and , , , , ;

  3. 3.

    and is not of the form ;

  4. 4.

    and or with \(j \notin I\);

  5. 5.

    or or or or and is not an endpoint ;

  6. 6.

    and ;

  7. 7.

    and , , .

The four cases are standard to system F with records and variants. The support for session types and concurrency in the first two cases (term and type application) are derived from the types of values for such operators (Fig. 10). Item 5 addresses session operators applied to non endpoints. Item 6 is for two concurrent session operators on the same channel end. Finally, Item 7 is for mismatches on two session operations on two endpoints for the same channel.

Theorem 8 (Safety)

If , then is not a runtime error.

An algorithmic typing system can be easily extracted from the declarative system for terms in Fig. 11 via a bidirectional type system, formulated along the lines of Almeida et al.  [2].

7 Related Work

Equirecursion in system F. In first investigations on equirecursive types, the notion of type equivalence is often formulated in a coinductive fashion [5, 11, 18, 29, 38]. Two types are equivalent if they unroll into the same infinite tree. Whenever this unrolling is the only type-level computation, such trees are regular, enabling efficient decision procedures. Some authors have studied equirecursion together with other notions of type-level computation. Solomon considers parameterized type definitions, which correspond to higher-order kinds [63]. These implicitly correspond to \(\lambda \)-terms, since reduction occurs as types are allowed to call other types. Some authors consider equirecursion in system \(F_\omega \), with weaker or stronger notions of equality [1, 12, 14, 41]. Regarding equirecursion in system F, the model of Cai et al.  [14] is the closest to ours, and indeed our results up to \(F^{\mu _*\cdot }_\omega \) can be seen as a generalisation of theirs. However, Cai et al. depart from the usual setting by allowing non-contractive types (which most authors forbid, including this work), requiring a sort of infinitary lambda calculus. Moreover, this work further extends additional equivalence properties by including session types with their distinctive semantics, such as sequential composition and duality.

Session type systems. Session types were introduced in the 90s by Honda et al.  [42, 43, 67]. Equirecursion was the first approach used to construct infinite session types, which often allows type equality to be interpreted according to a coinductive notion of bisimulation [52]. In this vein, Keizer et al.  [48] utilize coalgebras to represent session types. Since the inception of session types, there has been an interest in extending the theory to nonregular protocols [58, 59, 66]. Context-free session types emerged as a natural extension, as it still allowed for practical type equality algorithms [3, 4, 19, 28, 56, 68]. Other approaches that go beyond regular session types include nested session types [24] as well as 1-counter, pushdown and 2-counter session types [33]. However, the more expressive notions are not amenable to practical type equivalence algorithms, just like the higher-order types present in our system \(F^{\mu }_\omega \). Polymorphism in session types has also been a topic of interest, with or without recursion [15, 22, 23, 31, 39].

Dual type operator. This work is, to the best of our knowledge, the first that internalises duality as a type constructor. Other settings, such as the language Alms [72], consider duality for session types as a user-definable, not built in, type function. Our is a type operator, not a type function. The difference is that a type function involves a type-level computation, which converges to a type written without dual. For example, in Alms we would have (as a type-level computation), both sides being the same type. In our setting, is a type on its own, which happens to be equivalent to . At the same time, our setting allows for types such as , or , which do not reduce.

Type equivalence algorithms. Algorithms for deciding the equivalence of types must inherently be related to the computational power of the corresponding type system. This has been used implicitly or explicitly to obtain decidability results. As already explained, if equirecursion is the only type-level computation, types can be represented as finite-state automata (or equivalently, infinite regular trees). Although some exponential time algorithms were first proposed [32], it has been established that the problem can be solved in quadratic time [53], which is to be expected as it matches the corresponding problem of bisimulation of finite-state automata [44]; see also Pierce [57].

The next ‘simplest’ model of computation is that of simple grammars, which intuitively correspond to deterministic pushdown automata with a single state [33]. Almeida et al.  [4] provided a practical algorithm for checking the bisimilarity of simple grammars. By dropping the determinism assumption, we arrive at Greibach normal form grammars, which are equivalent to basic process algebras [6, 7]. Bisimilarity algorithms have been studied extensively in this setting [13, 17, 47, 49]; presently it is known that the complexity of the problem lies between EXPTIME and 2-EXPTIME, which does not exclude the possibility of a polynomial time algorithm for the simpler model of simple grammars.

In this paper we present a reduction from first-order grammars to \(F^{\mu }_\omega \)-types, showing that the more expressive type systems (\(F^{\mu }_\omega \), presented here and in Cai et al.  [14], as well as its extensions) are at least as powerful as deterministic pushdown automata. As far as we know, the closest result to ours is by Solomon [63], which shows conversions between a universe of “context-free types” and deterministic context-free languages. The universe of types studied by Solomon is different from \(F^{\mu }_\omega \). With some work we could prove that Solomon’s types can be embedded into \(F^{\mu }_\omega \), which would entail our result as a corollary. However, it is easier and simpler to prove directly the reduction as we did.

The equivalence problem for deterministic pushdown automata was a notorious open problem for a long time, until Sénizergues showed it to be decidable [61, 62]. Since his proof, many authors have tried to refine the result in an attempt to arrive at an implementable algorithm [46, 64, 65].

Concurrent term languages. The usefulness of a type system is directly related to its capability to be used in a programming language. Type systems such as the ones discussed in this work lend themselves quite readily to functional term languages [45]. For session types, existing term languages are either inspired in the pi calculus [26, 69, 73] or in the lambda calculus [35, 54, 70], or even the two [71]. The system presented in this paper is linear, meaning that resources must be used exactly once [50, 74]. Some authors go beyond linearity by considering unrestricted type qualifiers [48, 73] or manifest sharing [8].

8 Conclusion and future work

This paper introduces an extension of system F which includes equirecursion, lambda abstractions, and context-free session types. We present type equivalence algorithms, and a term language and its metatheory. Although we have defined a rather general system, it turns out that for practical purposes one must restrict recursion to , that is, to type-level monomorphic recursion. In any case, the main system \(F^{\mu _*;}_\omega \) is a non-trivial extension of (the contractive fragment of) \(F^{\mu _*}_\omega \) (studied by Cai et al.  [14]) as well as \(F^{\mu ;}\) (studied by Almeida et al.  [19]).

We have only considered polymorphic types of a functional nature: type must always be of kind . It is worth investigating polymorphism over session types, as it would allow further additional behaviour. For example, we could be interested in streaming values of heterogeneous nature, as in type . It is however unclear whether this extension would still allow a translation into a simple grammar.

We proved that the type equivalence problem for systems \(F^{\mu }_\omega \), \(F^{\mu \cdot }_\omega \), \(F^{\mu ;}_\omega \) is at least as hard as a non-efficiently-decidable problem. We conjecture that these systems have the same power as deterministic pushdown automata (and hence, admit decidable type equivalence), but we do not have a construction to prove this result. In any case, our proof that the type equivalence problem is at least as hard as the bisimilarity of deterministic pushdown automata is enough to justify focus on the significant fragment with restricted recursion.

We study either full recursion (for theoretical results) or recursion limited to kind (for algorithmic results). It would be interesting to study in-between kinds of recursion; the next natural example is . What model of computation would we arrive at if we consider types written with this recursion operator? We conjecture that types \(F^{\mu }_\omega \) and \(F^{\mu \cdot }_\omega \), when restricted to recursion of kind , would still be expressible as simple grammars, whereas such a restriction in the more powerful \(F^{\mu ;}_\omega \) would take us beyond this model, but perhaps without reaching the expressivity of deterministic pushdown automata.