This paper was converted on www.awesomepapers.org from LaTeX by an anonymous user.
Want to know more? Visit the Converter page.

\cftpagenumbersoff

figure \cftpagenumbersofftable

A type language for message passing component-based systems

Zorica Savanović IMT School for Advanced Studies, Lucca, ItalyIMT School for Advanced Studies, Lucca, ItalyC4 - University of Beira Interior, Rua Marquês d’Ávila e Bolama, 6201-001, Covilhã, Portugal    Letterio Galletta IMT School for Advanced Studies, Lucca, ItalyC4 - University of Beira Interior, Rua Marquês d’Ávila e Bolama, 6201-001, Covilhã, Portugal    Hugo Torres Vieira C4 - University of Beira Interior, Rua Marquês d’Ávila e Bolama, 6201-001, Covilhã, Portugal
Abstract

Component-based development is challenging in a distributed setting, for starters considering programming a task may involve the assembly of loosely-coupled remote components. In order for the task to be fulfilled, the supporting interaction among components should follow a well-defined protocol. In this paper we address a model for message passing component-based systems where components are assembled together with the protocol itself. Components can therefore be independent from the protocol, and reactive to messages in a flexible way. Our contribution is at the level of the type language that allows to capture component behaviour so as to check its compatibility with a protocol. We show the correspondence of component and type behaviours, which entails a progress property for components.

1 Introduction

Code reusability is an important principle to support the development of software systems in a cost-effective way. It is a key principle in Component-Based Development (CBD) [16], where the idea is to build systems relying on the composition of loosely-coupled and independent units called components.

The motivations behind CBD are, on the one hand, to increase development efficiency and lower the costs (by building a system from pre-existing components, instead of building from scratch), and on the other hand, to improve quality of the software for instance to what concerns software errors (components can be tested over and over again in different contexts). Consider, for example, microservices (see, e.g., [9]) that have been recently adopted by massively deployed applications such as Netflix, eBay, Amazon and Uber, and that are reusable distributed software units. In such a distributed setting, composing software elements necessarily involves some form of communication scheme, for instance based on message passing.

In order for the functionality to be achieved, communication among components should follow a well-defined protocol of interaction, that may be specified in terms of some choreography language like, for example, WS-CDL [19] or the choreography diagrams of BPMN [18]. A component should be able to carry out a certain sequence of I/O actions in order to fulfil its role in the protocol. One way to accomplish this is to implement a component in a way that prescribes a strict sequence of I/O actions, that should precisely match the actions expected by the protocol. However, this choice interferes with reusability, since such a component can be used only in an environment that expects that exact sequence of communication actions. For instance, if a component receives an image and outputs its classification just once, what will happen if we need to use this component in a context that requires the classification is sent multiple times?

In contrast, a more flexible design choice inspired by reactive programming is to design components so that they can respond to external stimulus without any specific I/O sequence. The reactive programming principle for building such components is to consider that as soon as the data is available, it can be received or emitted. For example, we can design a component that is able to output a classification after receiving an image, as long as required. In such a way, reusability is promoted since such components can be used in different environments thanks to the flexibility given by the reactive behaviour. However, such a flexibility at the composition level may be too wild if all components are able to send / receive data as soon as it is available. Hence, there is the need to discipline the interactions at the level of the environment where the composition takes place. What if, for example, we have different images that need to be classified and the classifying component is continuously emitting the result for the first image?

Carbone, Montesi and Vieira [6] proposed a language that supports the development of distributed systems by combining the notions of reactive components with choreographic specifications of communication protocols [17]. The proposal considers components that can dynamically send / receive data as soon as it is available, while considering that an assembly of components is governed by a protocol. Hence, among all the possible reactions that are supported by the composed components, the only ones that will actually be carried out are the ones allowed by the protocol. A composition of components defines itself a component that can be further composed (under the governance of some protocol) also providing a reactive behaviour. This approach promotes reusability thanks to the flexibility of the reactive behaviour. For instance, by abstracting from the number of supported reactions, e.g., if a component can (always) perform a computation reacting to some data inputs, then it can be used in different protocols that require such computation to be carried out a different number of times; by abstracting from message ordering, e.g., if a component needs some values to perform a computation, it may be used with any protocol that provides them in any order.

Component implementations should be hidden, so it shouldn’t be necessary to inspect the inner workings in order to asses if it is usable in a determined context for the purpose of off-the-shelf substitution or reuse. Hence, a component should be characterised with a signature that allows checking its compatibility when used in a composition. In particular, it must be ensured that each component provides (at least) the behaviour prescribed by the protocol in which the component participates. Carbone et al. [6] propose a verification technique that ensures communication safety and progress. However, the approach requires checking the implementation of components each time the component is put in a different context, i.e., each time that the component is used “off-the-shelf” we need to check if the reactions supported by the component are enough to implement its part in the protocol.

In this work we consider a different approach, avoiding the implementation check each time a component is to be used. Firstly, we introduce a type language that characterises the reactive behaviour of components. Secondly, we devise an inference technique that identifies the types of components, based on which we can verify whether the component provides the reactive behaviour required by a context. The motivation is in tune with reusability: once the component’s type is identified, there is no further need to check the implementation, because the type is enough to describe “what the component can do”.

Our types specify the ability of components to receive values of a prescribed basic type. Moreover, they track different kinds of dependencies, for instance that certain values to be emitted always (for each output) require a specific set of inputs (dubbed per each value dependencies). Our types can also describe the fact that a component needs to be, in some sense, initialised by receiving specific values before proceeding with other reactive behaviour (dubbed initial dependencies). Furthermore, our types also identify constraints on the number of values that a component can send. Finally, we ensure the correctness of our type system by proving that our extraction procedures are sound with respect to the semantics of the Governed Components (GC) language [6], considering here the choice-free subset of the GC language and leaving a full account of the language to future work. Moreover, we ensure that whenever a type of a component prescribes an action, a component will not be stuck, i.e., it will eventually carry out the matching action.

The rest of the paper is organised as follows. We first present the GC language in Section 2. Then in Section 3 we intuitively introduce our type language through a motivating example based on AWS Lambda [2] where we point out different scenarios that might occur while composing components and how our types allow to describe certain behavioural patterns. Section 4 introduces the syntax and the semantics of our types. Then, we define the type extraction for base components in Section 4.1, whereas the type extraction for composite components in Section 4.2. In Section 4.3 we present our results. Section 5 concludes, discusses related work and gives some future issues.

2 Background: Governed Components Language

In this section we briefly introduce the GC language, focusing on the main points that allow to grasp the essence of the model and to support a self-contained understanding of this paper. We refer the interested reader to [6] for a full account of the language. The syntax of the (protocol choice-free fragment of the) GC language is given in Table 1. There are two kinds of components (KK): base and composite. Both kinds interact with the external environment by means of input and output ports exposed as the component’s interface. Besides of the interface, components are defined by their implementation.

Components Local Binders Protocol
K::=K::= [x~y~]{L}[{\tilde{x}}\,\rangle\,{\tilde{y}}]\{L\} (base) L::=L::= y=f(x~){y}={\mathit{f}(\tilde{x})} G::=G::= 𝗉𝗊~;G\mathsf{p}\!\!\!\ \mathrel{\stackrel{{\scriptstyle{\;\;\ell\;\;}}}{{\mbox{\rightarrowfill}}}}\ \!\!\!\mathsf{\tilde{q}};G (communication)
[x~y~]{G;R;D;𝗋[F]}[{\tilde{x}}\,\rangle\,{\tilde{y}}]\{G;R;D;\mathsf{r}[F]\} (composite) L,LL,L μ𝐗.G\mu\mathbf{X}.G (recursion)
𝐗\mathbf{X} (recursion variable)
𝐞𝐧𝐝\mathbf{end} (termination)
Role assignments Distribution Binders Forwarders
R::=R::= 𝗉=K\mathsf{p}=K D::=D::= 𝗉.x𝗊.y\mathsf{p}.{x}\mathrel{\stackrel{{\scriptstyle{\;\;\ell\;\;}}}{{\mbox{\leftarrowfill}}}}{\mathsf{q}.{y}} F::=F::= zwz\leftarrow w
R,RR,R D,DD,D F,FF,F
Table 1: Syntax of Governed Components

In the case of a base component the implementation is given by the list of local binders ({L}\{L\}). A local binder specifies a function, denoted as y=f(x~)y=f(\tilde{x}), which is used to compute the output values for port yy relying on values received on list of (input) ports x~\tilde{x}. So, we say that component’s ability to output a value may depend on the ones received, where instead, components are always able to receive values.We abstract from the definition of such functions ff and assume them to be total. Received values are processed in a FIFO discipline, so queues are added to the local binders at runtime (noted as y=f(x~)σ~{y}\,=\,{f(\tilde{x})}\,\langle\,{\tilde{\sigma}}). Each element (σ\sigma) in a queue (σ~\tilde{\sigma}) is a store defined as a partial mapping from input ports to values (σ~=σ_1,σ_2,,σ_k\tilde{\sigma}=\sigma_{\_}1,\sigma_{\_}2,\ldots,\sigma_{\_}k, where in σ_1\sigma_{\_}1 the oldest values received are stored, in σ_2\sigma_{\_}2 the second-oldest values, and so on and so forth up to σ_k\sigma_{\_}k). E.g., if y=f(x_1,x_2)<y=f(x_{\_}1,x_{\_}2)<\cdot and the component receives v_1v_{\_}1 and v_2v_{\_}2 in that order on port x_1x_{\_}1 and v_3v_{\_}3, v_4v_{\_}4 and 55 on port x_2x_{\_}2. The queue at this point has three mappings (x_1v_1,x_2v_3),(x_1v_2,x_2v_4),(x_25)(x_{\_}1\rightarrow v_{\_}1,x_{\_}2\rightarrow v_{\_}3),(x_{\_}1\rightarrow v_{\_}2,x_{\_}2\rightarrow v_{\_}4),(x_{\_}2\rightarrow 5) where two are “complete” to compute the function and one is not.

The implementation of a composite component, represented by {G;R;D;𝗋[F]}\{G;R;D;\mathsf{r}[F]\}, is an assembly of subcomponents whose interaction is governed by a protocol (GG). The set of subcomponents are given in RR together with their roles in the interaction (e.g., 𝗋=K\mathsf{r}=K denotes that component KK is assigned to role 𝗋\mathsf{r}). Composite components also specify a list of (distribution) binders (DD) that provide an association between the messages exchanged in the protocol (\ell) and the ports (x,yx,y) of the components (e.g., 𝗉.x𝗊.y\mathsf{p}.{x}\mathrel{\stackrel{{\scriptstyle{\;\;\ell\;\;}}}{{\mbox{\leftarrowfill}}}}{\mathsf{q}.{y}} states that a message with a label \ell is emitted on port yy of the component assigned to role 𝗊\mathsf{q}, and to be received on port xx of the component assigned to role 𝗉\mathsf{p}). Ports are uniquely associated to message labels (\ell) in such way that each communication step in the protocol has a precise mapping to a port, i.e., all values emitted on a port will be carried in messages with the same label and all values received on a port will be delivered in messages with the same label. E.g., every time a value is emitted from some port yy it will be carried in a message labelled with e.g., \ell and a value delivered on some port xx is delivered on a message with the same label \ell. Some other label cannot be attached to yy, otherwise the association would not be unique. The classclass message label (or some other) cannot be attached to y_py_{\_}p otherwise the association would not be unique. Finally, subterm 𝗋[F]\mathsf{r}[F] is used to specify the externally-observable behaviour: the (only interfacing) subcomponent responsible for the interaction with the external environment is identified (by its role 𝗋\mathsf{r}) and the respective connection between ports is provided by forwarders (FF). The idea is that values received on the (input) ports of the composite component are directly forwarded to the (input) ports of the interfacing subcomponent, and values emitted on the (output) ports of the interfacing subcomponent are forwarded to the (output) ports of the composite component. For example, the term xxx^{\prime}\leftarrow x is for forwarding an input, and the term yyy\leftarrow y^{\prime} is for forwarding an output (xx and yy are the ports of the composite component).

Protocol specifications prescribe the interaction among a set of parties identified by roles. Communication term 𝗉𝗊~;G\mathsf{p}\!\!\!\ \mathrel{\stackrel{{\scriptstyle{\;\;\ell\;\;}}}{{\mbox{\rightarrowfill}}}}\ \!\!\!\mathsf{\tilde{q}};G specifies that role 𝗉\mathsf{p} sends the message labelled \ell to the (nonempty) set of roles 𝗊~\tilde{\mathsf{q}}, after which the protocol continues as specified by GG. The difference between this work and [6] is the absence of branching. Then we have terms μ𝐗.G\mu\mathbf{X}.G and 𝐗\mathbf{X} for specifying recursive protocols. Finally, term 𝐞𝐧𝐝\mathbf{end} defines the termination of the protocol.

We now present the operational semantics of the GC in terms of a labelled transition system (LTS). We denote by KλKK\ \mathrel{\stackrel{{\scriptstyle{\;\;\lambda\;\;}}}{{\mbox{\rightarrowfill}}}}\ K^{\prime} that a component KK evolves in one computational step to KK^{\prime}, where observations are captured by labels defined as follows λ::=x?v|y!v|τ\lambda::=x?v\ |\ y!v\ |\ \tau. Transition label x?vx?v represents an input on port xx of a value vv, label y!vy!v captures an output on port yy of a value vv, and label τ\tau denotes an internal move.

The rules that describe the behaviour of components are presented in two parts, addressing base and composite components separately. We present only the main rules, the full semantics can be found in [6].

Ly!vLyy~𝖮𝗎𝗍𝖡𝖺𝗌𝖾[x~y~]{L}y!v[x~y~]{L}[{\tilde{x}}\,\rangle\,{\tilde{y}}]\{L\}\ \mathrel{\stackrel{{\scriptstyle{\;\;y!{v}{}\;\;}}}{{\mbox{\rightarrowfill}}}}\ [{\tilde{x}}\,\rangle\,{\tilde{y}}]\{L^{\prime}\}\lx@proof@logical@and L\ \mathrel{\stackrel{{\scriptstyle{\;\;y!{v}{}{}\;\;}}}{{\mbox{\rightarrowfill}}}}\ L^{\prime}y\in\tilde{y} Lx?vLxx~𝖨𝗇𝗉𝖡𝖺𝗌𝖾[x~y~]{L}x?v[x~y~]{L}[{\tilde{x}}\,\rangle\,{\tilde{y}}]\{L\}\ \mathrel{\stackrel{{\scriptstyle{\;\;x?v\;\;}}}{{\mbox{\rightarrowfill}}}}\ [{\tilde{x}}\,\rangle\,{\tilde{y}}]\{L^{\prime}\}\lx@proof@logical@and L\ \mathrel{\stackrel{{\scriptstyle{\;\;x?v\;\;}}}{{\mbox{\rightarrowfill}}}}\ L^{\prime}x\in\tilde{x}
Table 2: Semantics of base components

Rules 𝖮𝗎𝗍𝖡𝖺𝗌𝖾\mathsf{OutBase} and 𝖨𝗇𝗉𝖡𝖺𝗌𝖾\mathsf{InpBase} that are given in Table 2 capture base component behaviour, and are defined relying on transitions exhibited by local binders, denoted LλLL\ \mathrel{\stackrel{{\scriptstyle{\;\;\lambda\;\;}}}{{\mbox{\rightarrowfill}}}}\ L^{\prime}. Rule 𝖮𝗎𝗍𝖡𝖺𝗌𝖾\mathsf{OutBase} states that if local binders LL can exhibit an output of value vv on port yy, where yy is part of the component’s interface, then the corresponding output can be exhibited by the base component. Rule 𝖨𝗇𝗉𝖡𝖺𝗌𝖾\mathsf{InpBase} follows the same lines.

Kz!vKF=yz,Fyy~𝖮𝗎𝗍𝖢𝗈𝗆𝗉[x~y~]{G;𝗋=K,R;D;𝗋[F]}y!v[x~y~]{G;𝗋=K,R;D;𝗋[F]}[{\tilde{x}}\,\rangle\,{\tilde{y}}]\{G;\mathsf{r}\!=\!K,R;D;\mathsf{r}[F]\}\ \mathrel{\stackrel{{\scriptstyle{\;\;{y}!{v}{}\;\;}}}{{\mbox{\rightarrowfill}}}}\ [{\tilde{x}}\,\rangle\,{\tilde{y}}]\{G;\mathsf{r}\!=\!K^{\prime},R;D;\mathsf{r}[F]\}K\ \mathrel{\stackrel{{\scriptstyle{\;\;{z}!{v}{}{}\;\;}}}{{\mbox{\rightarrowfill}}}}\ K^{\prime}\qquad F=y\leftarrow z,F^{\prime}\qquad y\in\tilde{y}
Kz?vKF=zx,Fxx~𝖨𝗇𝗉𝖢𝗈𝗆𝗉[x~y~]{G;𝗋=K,R;D;𝗋[F]}x?v[x~y~]{G;𝗋=K,R;D;𝗋[F]}[{\tilde{x}}\,\rangle\,{\tilde{y}}]\{G;\mathsf{r}\!=\!K,R;D;\mathsf{r}[F]\}\ \mathrel{\stackrel{{\scriptstyle{\;\;{x}?v\;\;}}}{{\mbox{\rightarrowfill}}}}\ [{\tilde{x}}\,\rangle\,{\tilde{y}}]\{G;\mathsf{r}\!=\!K^{\prime},R;D;\mathsf{r}[F]\}K\ \mathrel{\stackrel{{\scriptstyle{\;\;{z}?v\;\;}}}{{\mbox{\rightarrowfill}}}}\ K^{\prime}\qquad F=z\leftarrow x,F^{\prime}\qquad x\in\tilde{x}
KτK𝖨𝗇𝗍𝖾𝗋𝗇𝖺𝗅[x~y~]{G;𝗌=K,R;D;𝗋[F]}τ[x~y~]{G;𝗌=K,R;D;𝗋[F]}[{\tilde{x}}\,\rangle\,{\tilde{y}}]\{G;\mathsf{s}\!=\!K,R;D;\mathsf{r}[F]\}\ \mathrel{\stackrel{{\scriptstyle{\;\;\tau\;\;}}}{{\mbox{\rightarrowfill}}}}\ [{\tilde{x}}\,\rangle\,{\tilde{y}}]\{G;\mathsf{s}\!=\!K^{\prime},R;D;\mathsf{r}[F]\}K\ \mathrel{\stackrel{{\scriptstyle{\;\;\tau\;\;}}}{{\mbox{\rightarrowfill}}}}\ K^{\prime}
Ku!vKD=𝗊.z𝗉.u,DG𝗉!vG𝖮𝗎𝗍𝖢𝗁𝗈𝗋[x~y~]{G;𝗉=K,R;D;𝗋[F]}τ[x~y~]{G;𝗉=K,R;D;𝗋[F]}[{\tilde{x}}\,\rangle\,{\tilde{y}}]\{G;\mathsf{p}\!=\!K,R;D;\mathsf{r}[F]\}\ \mathrel{\stackrel{{\scriptstyle{\;\;\tau\;\;}}}{{\mbox{\rightarrowfill}}}}\ [{\tilde{x}}\,\rangle\,{\tilde{y}}]\{G^{\prime};\mathsf{p}\!=\!K^{\prime},R;D;\mathsf{r}[F]\}K\ \mathrel{\stackrel{{\scriptstyle{\;\;{u}!{v}{}\;\;}}}{{\mbox{\rightarrowfill}}}}\ K^{\prime}\qquad D=\mathsf{q}.{z}\mathrel{\stackrel{{\scriptstyle{\;\;\ell\;\;}}}{{\mbox{\leftarrowfill}}}}{\mathsf{p}.{u}},D^{\prime}\qquad G\ \mathrel{\stackrel{{\scriptstyle{\;\;\mathsf{p}!\ell\langle v\rangle\;\;}}}{{\mbox{\rightarrowfill}}}}\ G^{\prime}
Kz?vKD=𝗊.z𝗉.u,DG𝗊?vG𝖨𝗇𝗉𝖢𝗁𝗈𝗋[x~y~]{G;𝗊=K,R;D;𝗋[F]}τ[x~y~]{G;𝗊=K,R;D;𝗋[F]}[{\tilde{x}}\,\rangle\,{\tilde{y}}]\{G;\mathsf{q}\!=\!K,R;D;\mathsf{r}[F]\}\ \mathrel{\stackrel{{\scriptstyle{\;\;\tau\;\;}}}{{\mbox{\rightarrowfill}}}}\ [{\tilde{x}}\,\rangle\,{\tilde{y}}]\{G^{\prime};\mathsf{q}\!=\!K^{\prime},R;D;\mathsf{r}[F]\}K\ \mathrel{\stackrel{{\scriptstyle{\;\;{z}?v\;\;}}}{{\mbox{\rightarrowfill}}}}\ K^{\prime}\qquad D=\mathsf{q}.{z}\mathrel{\stackrel{{\scriptstyle{\;\;\ell\;\;}}}{{\mbox{\leftarrowfill}}}}{\mathsf{p}.{u}},D^{\prime}\qquad G\ \mathrel{\stackrel{{\scriptstyle{\;\;\mathsf{q}?\ell\langle v\rangle\;\;}}}{{\mbox{\rightarrowfill}}}}\ G^{\prime}
Table 3: Semantics of composite components

Notice that the transition of the local binder specifies a final configuration LL^{\prime} which is accounted for in the evolution of the base component. We omit here the rules for local binders (see [6]) and provide only an informal account for them. Essentially, a (runtime) local binder y=f(x~)σ~{y}\,=\,{f(\tilde{x})}\,\langle\,{\tilde{\sigma}} is always receptive to an input x?vx?v: if xx is not used in the function (xx~x\not\in\tilde{x}) then value vv is simply discarded; otherwise, the value is added to the (oldest) entry in mapping queue σ~\tilde{\sigma} that does not have an entry for xx (possibly originating a new mapping at the tail of σ~\tilde{\sigma}). All local binders in LL synchronise on an input, so each local binder will store (or discard) its own copy of the value. Instead, local binder outputs are not synchronised among them: if a local binder outputs a value, other local binders will not react. For this to happen, the oldest mapping in queue σ~\tilde{\sigma} must be complete (i.e., assign values to all of x~\tilde{x}) at which point function ff may be computed, the result which is then carried in the transition label (i.e., the vv in y!vy!v).

We now introduce the rules that capture the behaviour of the composite components, displayed in Table 3. Notice that a composite component may itself be used as a subcomponent of another composition (of a “bigger” component), and base components provide the syntactic leaves. Rules 𝖮𝗎𝗍𝖢𝗈𝗆𝗉\mathsf{OutComp} and 𝖨𝗇𝗉𝖢𝗈𝗆𝗉\mathsf{InpComp} capture the interaction of a composite component with an external environment, realised by the interfacing subcomponent. The role assignment 𝗋=K\mathsf{r}\!=\!K captures the relation between component KK and role rr, which is specified as the interfacing role (𝗋[F]\mathsf{r}[F]). Rule 𝖮𝗎𝗍𝖢𝗈𝗆𝗉\mathsf{OutComp} allows for the interfacing component KK to send a value vv to the external environment via one of the ports of the composite component (yy). Notice that the connection between the port of the interfacing component (zz) and the port of the composite component (yy) is specified in a forwarder (F=yz,FF=y\leftarrow z,F^{\prime}). Rule 𝖨𝗇𝗉𝖢𝗈𝗆𝗉\mathsf{InpComp} follows the same lines to model an externally-observable input. Rule 𝖨𝗇𝗍𝖾𝗋𝗇𝖺𝗅\mathsf{Internal} allows for internal actions in a subcomponent (KK), where the final configuration (KK^{\prime}) is registered in the final configuration of the composite component.

Rules 𝖮𝗎𝗍𝖢𝗁𝗈𝗋\mathsf{OutChor} and 𝖨𝗇𝗉𝖢𝗁𝗈𝗋\mathsf{InpChor} capture the interaction among subcomponents of a composite component. Rule 𝖮𝗎𝗍𝖢𝗁𝗈𝗋\mathsf{OutChor} addresses the case when a component is sending a message to another one. The premises, together with role assignment 𝗉=K\mathsf{p}\!=\!K, establish the connection among sender component KK, the component port uu, sender role 𝗉\mathsf{p}, and message label \ell. Premise Ku!vKK\ \mathrel{\stackrel{{\scriptstyle{\;\;{u}!{v}{}\;\;}}}{{\mbox{\rightarrowfill}}}}\ K^{\prime} says that the sender component (KK) can perform an output of value vv on port uu. Premise D=𝗊.z𝗉.u,DD=\mathsf{q}.{z}\mathrel{\stackrel{{\scriptstyle{\;\;\ell\;\;}}}{{\mbox{\leftarrowfill}}}}{\mathsf{p}.{u}},D^{\prime} says that the distribution binders specify the (unique) relation between port uu of sender role 𝗉\mathsf{p} and message label \ell (receiver role 𝗊\mathsf{q} and associated port zz are not important here). The last premise G𝗉!vGG\ \mathrel{\stackrel{{\scriptstyle{\;\;\mathsf{p}!\ell\langle v\rangle\;\;}}}{{\mbox{\rightarrowfill}}}}\ G^{\prime} realises the component governing the protocol, i.e., saying that the communication is only possible if the protocol prescribes it. Namely, the premise says that the protocol exhibits an output of a value vv carried in message \ell from role 𝗉\mathsf{p}. The rules for protocol transitions are presented in [6]. Naturally which semantics has an impact on our technical development (namely regarding end-point projection in local protocols), but to some extent can be addressed in a modular way (i.e., up to the existence of the end-point projection). Notice that the transitions of component KK and protocol GG specify final configurations KK^{\prime} and GG^{\prime} which are accounted for in the evolution of the composite component.

Rule 𝖨𝗇𝗉𝖢𝗁𝗈𝗋\mathsf{InpChor} is similar, but instead of message sending, it addresses the case when a subcomponent receives a message from another subcomponent. The premises are equivalent to the ones for Rule 𝖮𝗎𝗍𝖢𝗁𝗈𝗋\mathsf{OutChor}, but now regard reception. Namely, by saying that receiving component KK can exhibit the respective input transition, that the distribution binder specifies the relation of message label \ell with receiver role 𝗊\mathsf{q} and port zz, and that protocol GG prescribes the input of a value.

3 Motivating example: microservices for Image Recognition System

In order to further motivate GC and also to introduce our typing approach, we now informally discuss an example inspired by a microservices scenario [2] that addresses an Image Recognition System (𝖨𝖱𝖲\mathsf{IRS}). The basic idea is that users upload images and receive back the resulting classification. Moreover, users can get the current running version of the system whenever desired. The 𝖨𝖱𝖲\mathsf{IRS} is made of two microservices, 𝖯𝗈𝗋𝗍𝖺𝗅\mathsf{Portal} and 𝖱𝖾𝖼𝗈𝗀𝗇𝗂𝗍𝗂𝗈𝗇\mathsf{Recognition} 𝖤𝗇𝗀𝗂𝗇𝖾\mathsf{Engine} (𝖱𝖤\mathsf{RE}), that interact according to a predefined protocol.

The task is achieved according to the following workflow: 𝖯𝗈𝗋𝗍𝖺𝗅\mathsf{Portal} sends the 𝑖𝑚𝑎𝑔𝑒\mathit{image} loaded by a user to 𝖱𝖤\mathsf{RE} to be processed. When 𝖱𝖤\mathsf{RE} service finishes its 𝑐𝑙𝑎𝑠𝑠𝑖𝑓𝑖𝑐𝑎𝑡𝑖𝑜𝑛\mathit{classification}, it sends the 𝑐𝑙𝑎𝑠𝑠\mathit{class} as the result of the 𝑐𝑙𝑎𝑠𝑠𝑖𝑓𝑖𝑐𝑎𝑡𝑖𝑜𝑛\mathit{classification} to 𝖯𝗈𝗋𝗍𝖺𝗅\mathsf{Portal}. We model the scenario in the GC calculus by assigning to each microservice the corresponding role and using components to represent them. We assign role 𝖯𝗈𝗋𝗍𝖺𝗅\mathsf{Portal} to component K_PortalK_{\_}{Portal} and role 𝖱𝖤\mathsf{RE} to component K_REK_{\_}{RE}, where K_PortalK_{\_}{Portal} and K_REK_{\_}{RE} are base components. Interaction between these two components is governed by global protocol GG, that can be described as: 𝖯𝗈𝗋𝗍𝖺𝗅image𝖱𝖤;𝖱𝖤class𝖯𝗈𝗋𝗍𝖺𝗅\mathsf{Portal}\!\!\!\ \mathrel{\stackrel{{\scriptstyle{\;\;image\;\;}}}{{\mbox{\rightarrowfill}}}}\ \!\!\!\mathsf{RE};\mathsf{RE}\!\!\!\ \mathrel{\stackrel{{\scriptstyle{\;\;class\;\;}}}{{\mbox{\rightarrowfill}}}}\ \!\!\!\mathsf{Portal}. This (the part of GG) protocol exactly specifies the workflow described above: 𝖯𝗈𝗋𝗍𝖺𝗅\mathsf{Portal} sends an imageimage to 𝖱𝖤\mathsf{RE} (𝖯𝗈𝗋𝗍𝖺𝗅image𝖱𝖤\mathsf{Portal}\!\!\!\ \mathrel{\stackrel{{\scriptstyle{\;\;image\;\;}}}{{\mbox{\rightarrowfill}}}}\ \!\!\!\mathsf{RE}) that answers with the computed classclass (𝖱𝖤class𝖯𝗈𝗋𝗍𝖺𝗅\mathsf{RE}\!\!\!\ \mathrel{\stackrel{{\scriptstyle{\;\;class\;\;}}}{{\mbox{\rightarrowfill}}}}\ \!\!\!\mathsf{Portal}). If we add the termination (𝐞𝐧𝐝\mathbf{end}) we obtain (complete GG) protocol 𝖯𝗈𝗋𝗍𝖺𝗅image𝖱𝖤;𝖱𝖤class𝖯𝗈𝗋𝗍𝖺𝗅;𝐞𝐧𝐝\mathsf{Portal}\!\!\!\ \mathrel{\stackrel{{\scriptstyle{\;\;image\;\;}}}{{\mbox{\rightarrowfill}}}}\ \!\!\!\mathsf{RE};\mathsf{RE}\!\!\!\ \mathrel{\stackrel{{\scriptstyle{\;\;class\;\;}}}{{\mbox{\rightarrowfill}}}}\ \!\!\!\mathsf{Portal};\mathbf{end}, which may be described as a one-shot protocol, since the interaction is over (𝐞𝐧𝐝\mathbf{end}) after the components exchange the two messages.

We obtain composite component K_IRSK_{\_}{IRS} by assembling K_PortalK_{\_}{Portal} and K_REK_{\_}{RE} together with protocol GG that governs the interaction. Below, we show how it is possible to graphically represent component K_IRSK_{\_}{IRS}, where we represent K_PortalK_{\_}{Portal} and K_REK_{\_}{RE} as its subcomponents:

[Uncaptioned image]

The subcomponent K_PortalK_{\_}{Portal} is the interfacing component (hence is the only one connected to the external environment via forwarders). We can specify K_PortalK_{\_}{Portal} in the GC language as

[x_p,x_py_p,y_p,y_p′′]{y_p=f_u(x_p)<σ~y_p,y_p=f_r(x_p)<σ~y_p,y_p′′=f()<}[{x_{\_}p,x_{\_}p^{\prime}}\,\rangle\,{y_{\_}p,y_{\_}p^{\prime},y_{\_}p^{\prime\prime}}]\{y_{\_}p=f_{\_}u(x_{\_}p)<\tilde{\sigma}^{y_{\_}p},y_{\_}p^{\prime}=f_{\_}r(x_{\_}p^{\prime})<\tilde{\sigma}^{y_{\_}p^{\prime}},y_{\_}p^{\prime\prime}=f()<\cdot\}

As previewed in the graphical illustration, from the specification we can see that K_PortalK_{\_}{Portal} component has two input ports (x_p,x_px_{\_}p,x_{\_}p^{\prime}), three output ports (y_p,y_p,y_p′′y_{\_}p,y_{\_}p^{\prime},y_{\_}p^{\prime\prime}), and three local binders that at runtime are equipped with queues (σ~y_p\tilde{\sigma}^{y_{\_}p}, σ~y_p\tilde{\sigma}^{y_{\_}p^{\prime}} and empty queue \cdot given that the respective binder does not use any input ports). Notice that the queues are only required at runtime and are initially empty.

The idea of our type description is to provide an abstract characterisation of component’s behaviour. Types provide information about the set of input ports, namely the types of values that can be received on them, and about the output ports, namely regarding their behavioural capabilities. In particular, for each output port there are constraints which comprise three pieces of information: what type of values are emitted; what is the maximum number of values that can be emitted; and what are the dependencies on input ports, possibly including the number of currently available values that satisfy the dependency at runtime.

Informally, the type of K_PortalK_{\_}{Portal} announces the following: In the two input ports x_px_{\_}p and x_px_{\_}p^{\prime} the component can receive an 𝑖𝑚𝑎𝑔𝑒\mathit{image} and a 𝑐𝑙𝑎𝑠𝑠\mathit{class}, respectively ({x_p(𝑖𝑚𝑎𝑔𝑒),x_p(𝑐𝑙𝑎𝑠𝑠)}x_{\_}p(\mathit{image}),x_{\_}p^{\prime}(\mathit{class})\}). Also, the type says in y_py_{\_}p the component can emit 𝑖𝑚𝑎𝑔𝑒\mathit{image}s and it can do so an unbounded number of times (denoted by \infty) as the underlying local binder imposes no boundary constraints. In particular, the local binder can send an 𝑖𝑚𝑎𝑔𝑒\mathit{image} as soon as one is received in x_px_{\_}p. Hence, we have a per each value dependency of y_py_{\_}p on x_px_{\_}p. Formally, we write this constraint as y_p(image)::[{x_p:N_p}]y_{\_}p(image):\infty:[\{x_{\_}p\!:\!N_{\_}p\}], where N_pN_{\_}p is the number of values received on x_px_{\_}p that are available to be used to produce the output on y_py_{\_}p. We may describe constraint y_p(𝑐𝑙𝑎𝑠𝑠)::[{x_p:N_p}]y_{\_}p^{\prime}(\mathit{class}):\infty:[\{x_{\_}p^{\prime}\!:\!N_{\_}p^{\prime}\}] in a similar way. In constraint y_p′′(𝑣𝑒𝑟𝑠𝑖𝑜𝑛)::[]y_{\_}p^{\prime\prime}(\mathit{version}):\infty:[\emptyset] there are no dependencies from input ports specified, hence the reading is only that a 𝑣𝑒𝑟𝑠𝑖𝑜𝑛\mathit{version} can be emitted an unbounded number of times. We may specify the type of K_PortalK_{\_}{Portal} as:

T_𝑃𝑜𝑟𝑡𝑎𝑙=<{x_p(image),x_p(class)};{y_p(image)::[{x_p:N_p}],y_p(class)::[{x_p:N_p}],y_p′′(version)::[]}>T_{\_}{\mathit{Portal}}=<\{x_{\_}p(image),x_{\_}p^{\prime}(class)\};\{y_{\_}p(image):\infty:[\{x_{\_}p\!:\!N_{\_}p\}],y_{\_}p^{\prime}(class):\infty:[\{x_{\_}p^{\prime}\!:\!N_{\_}p^{\prime}\}],y_{\_}p^{\prime\prime}(version):\infty:[\emptyset]\}>

Composite component K_IRSK_{\_}{IRS} is an assembly of two base components K_𝑃𝑜𝑟𝑡𝑎𝑙K_{\_}{\mathit{Portal}} and K_REK_{\_}{RE} whose communication is governed by global protocol GG. The description of K_IRSK_{\_}{IRS} in GC language is the following:

K_IRS=[xy,y]{G;𝖯𝗈𝗋𝗍𝖺𝗅=K_Portal,𝖱𝖤=K_RE;D;𝖯𝗈𝗋𝗍𝖺𝗅[F]}K_{\_}{IRS}=[{x}\,\rangle\,{y,y^{\prime}}]\{G;\mathsf{Portal}=K_{\_}{Portal},\mathsf{RE}=K_{\_}{RE};D;\mathsf{Portal}[F]\}

where GG is the already described one-shot protocol

G=𝖯𝗈𝗋𝗍𝖺𝗅image𝖱𝖤;𝖱𝖤class𝖯𝗈𝗋𝗍𝖺𝗅;𝐞𝐧𝐝G=\mathsf{Portal}\!\!\!\ \mathrel{\stackrel{{\scriptstyle{\;\;image\;\;}}}{{\mbox{\rightarrowfill}}}}\ \!\!\!\mathsf{RE};\mathsf{RE}\!\!\!\ \mathrel{\stackrel{{\scriptstyle{\;\;class\;\;}}}{{\mbox{\rightarrowfill}}}}\ \!\!\!\mathsf{Portal};\mathbf{end}

Interfacing component K_𝑃𝑜𝑟𝑡𝑎𝑙K_{\_}{\mathit{Portal}} forwards the values from/to the external environment as specified in the forwarders (F=x_px,yy_p,yy_′′pF=x_{\_}p\leftarrow x,y\leftarrow y_{\_}p^{\prime},y^{\prime}\leftarrow y^{\prime\prime}_{\_}p). The forwarding implies that the characterisation of ports xx, yy and yy^{\prime} in the type of K_IRSK_{\_}{IRS} relies on one of the ports x_px_{\_}p, y_py_{\_}p^{\prime} and y_p′′y_{\_}p^{\prime\prime}, respectively, in the type of K_𝑃𝑜𝑟𝑡𝑎𝑙K_{\_}{\mathit{Portal}}.

The type of K_IRSK_{\_}{IRS} then says that it can always input on xx values of type 𝑖𝑚𝑎𝑔𝑒\mathit{image} accordingly to the input receptiveness principle. The constraint for yy^{\prime} is the same as for y_p′′y_{\_}p^{\prime\prime} since y_p′′y_{\_}p^{\prime\prime} does not depend on the protocol (in fact it has no dependencies). However, this is not the case for yy: in order for a 𝑐𝑙𝑎𝑠𝑠\mathit{class} of an image to be forwarded from y_py_{\_}p^{\prime} there is a dependency (identified in T_𝑃𝑜𝑟𝑡𝑎𝑙T_{\_}{\mathit{Portal}}) on port x_px_{\_}p^{\prime}. Furthermore, component K_𝑃𝑜𝑟𝑡𝑎𝑙K_{\_}{\mathit{Portal}} will only receive a value on x_px_{\_}p^{\prime} accordingly to the protocol specification, in particular upon the second message exchange. Hence, there is also a protocol dependency since the first message exchange has to happen first, so there is a transitive dependency to an 𝑖𝑚𝑎𝑔𝑒\mathit{image} being sent in the first message exchange, emitted from port y_py_{\_}p of component K_𝑃𝑜𝑟𝑡𝑎𝑙K_{\_}{\mathit{Portal}}. Finally, notice that y_py_{\_}p depends on x_px_{\_}p which is linked by forwarding to port xx of component K_IRSK_{\_}{IRS}, thus we have a sequence of dependencies that link yy to xx.

Since we have a one-shot protocol, the communications happens only once, which implies that one 𝑐𝑙𝑎𝑠𝑠\mathit{class} is produced for the first 𝑖𝑚𝑎𝑔𝑒\mathit{image} received. We therefore consider that the dependency of yy on xx is initial (since one value suffices to break the one-shot dependency), and that the maximum number of values that can be emitted on yy is 1. This constraint is formally written as y(class):1:[{x:Ω}]y(class):1:[\{x:\Omega\}]. The constraint for yy^{\prime} is y(version)::[]y^{\prime}(version):\infty:[\emptyset], where the set of dependencies is empty, i.e., it does not depend on any input. We then have the following type for component K_IRSK_{\_}{IRS}:

T_IRS=<{x(image)};{y(class):1:[{x:Ω}],y(version)::[]}>T_{\_}{IRS}=<\{x(image)\};\{y(class):1:[\{x:\Omega\}],y^{\prime}(version):\infty:[\emptyset]\}>

Let us now assume a recursive version of protocol

G=μ𝐗.𝖯𝗈𝗋𝗍𝖺𝗅image𝖱𝖤;𝖱𝖤class𝖯𝗈𝗋𝗍𝖺𝗅;𝐗G^{\prime}=\mu\mathbf{X}.\mathsf{Portal}\!\!\!\ \mathrel{\stackrel{{\scriptstyle{\;\;image\;\;}}}{{\mbox{\rightarrowfill}}}}\ \!\!\!\mathsf{RE};\mathsf{RE}\!\!\!\ \mathrel{\stackrel{{\scriptstyle{\;\;class\;\;}}}{{\mbox{\rightarrowfill}}}}\ \!\!\!\mathsf{Portal};\mathbf{X}

is used instead (i.e., K_IRS=[xy,y]{G;𝖯𝗈𝗋𝗍𝖺𝗅=K_Portal,𝖱𝖤=K_RE;D;𝖯𝗈𝗋𝗍𝖺𝗅[F]}K_{\_}{IRS}^{\prime}=[{x}\,\rangle\,{y,y^{\prime}}]\{G^{\prime};\mathsf{Portal}=K_{\_}{Portal},\mathsf{RE}=K_{\_}{RE};D;\mathsf{Portal}[F]\}). The idea now is that for each 𝑖𝑚𝑎𝑔𝑒\mathit{image} received a 𝑐𝑙𝑎𝑠𝑠\mathit{class} is produced. So, 𝑐𝑙𝑎𝑠𝑠\mathit{class} may be emitted on yy an unbounded number of times and the dependency of yy on xx is of a per each kind. Notice that the chain of dependencies can be described as before, but the one-shot dependency from before is now renewed at each protocol iteration.

The constraint for yy in this settings is y(class)::[{x:N_i}]y(class):\infty:[\{x\!:\!N_{\_}i\}], where N_iN_{\_}i captures the number of values received on xx that are currently available to produce the outputs on yy. The constraint for yy^{\prime} is the same as in the previous case. We then have that the type of K_IRSK_{\_}{IRS}^{\prime} is

<{x(image)};{y(class)::[{x:N_i}],y(version)::[]}><\{x(image)\};\{y(class):\infty:[\{x\!:\!N_{\_}i\}],y^{\prime}(version):\infty:[\emptyset]\}>

Imagine that K_PortalK^{\prime}_{\_}{Portal} is now a composite component that has an initialisation phase such that, first it receives a message about what kind of classification is required (e.g., “classify the image by the number of faces found on it”), then it sends it to K_REK^{\prime}_{\_}{RE}, after which the uploading and classification of the images can start (all other characteristics remain). Let x_1x_{\_}1 be the port of K_IRSK^{\prime}_{\_}{IRS} on which this message is received. Let us consider the following protocol

G′′=𝖯𝗈𝗋𝗍𝖺𝗅kind𝖱𝖤;μ𝐗.𝖯𝗈𝗋𝗍𝖺𝗅image𝖱𝖤;𝖱𝖤class𝖯𝗈𝗋𝗍𝖺𝗅;𝐗G^{\prime\prime}=\mathsf{Portal}\!\!\!\ \mathrel{\stackrel{{\scriptstyle{\;\;kind\;\;}}}{{\mbox{\rightarrowfill}}}}\ \!\!\!\mathsf{RE};\mu\mathbf{X}.\mathsf{Portal}\!\!\!\ \mathrel{\stackrel{{\scriptstyle{\;\;image\;\;}}}{{\mbox{\rightarrowfill}}}}\ \!\!\!\mathsf{RE};\mathsf{RE}\!\!\!\ \mathrel{\stackrel{{\scriptstyle{\;\;class\;\;}}}{{\mbox{\rightarrowfill}}}}\ \!\!\!\mathsf{Portal};\mathbf{X}

where after component K_PortalK^{\prime}_{\_}{Portal} sends the required kind of classifications (labelled as 𝑘𝑖𝑛𝑑\mathit{kind}), the communication between K_PortalK^{\prime}_{\_}{Portal} and K_REK^{\prime}_{\_}{RE} is governed by a recursive protocol as described in the previous example. The type of the component K_IRSK^{\prime}_{\_}{IRS} is similar to the type from the previous example, but now announces that the output on yy requires an initial value to be received on port x_1x_{\_}1, as the image classification process can only start after that. We then have the type of K_IRSK^{\prime}_{\_}{IRS}

<{x(image)};{y(class)::[{x:N_i,x_1:Ω}],y(version)::[]}><\{x(image)\};\{y(class):\infty:[\{x\!:\!N_{\_}i,x_{\_}1:\Omega\}],y^{\prime}(version):\infty:[\emptyset]\}>

4 A type language for the components

In this section we define the type language that captures the behaviour of components in an abstract way, starting with the presentation of the syntax which is followed by the operational semantics. Then we present two procedures that define how to extract the type of a component. The first procedure is for base, and the second one is for composite components.

Syntax

The syntax of types is presented in Table 4 and some explanations follow. A type TT consists of two elements: a (possibly empty) set of input ports, where each one is associated with a basic type bb (i.e., int, string, etc.), and a (possibly empty) set of constraints C, one for each output port. Basic types (ranged over by b,b_1,b_2,bx,by,b,b,b_{\_}1,b_{\_}2,b^{x},b^{y},b^{\prime},\ldots) specify the type of the values that can be communicated through ports, so as to ensure that no unexpected values arise. Each constraint in C contains a triple of the form y(b):B:[D]y(b):\textbf{B}:[\textbf{D}], which describes the type (bb) of values sent via yy, the capability (B) of yy and the dependencies (D) of yy on the input ports. The set of constraints C is ranged over C_1,C_2,,C,C′′,Cy,\textbf{C}_{\_}1,\textbf{C}_{\_}2,\dots,\textbf{C}^{\prime},\textbf{C}^{\prime\prime},\textbf{C}^{y},\dots (likewise other syntactic categories like NN, B, D,\textbf{D},\dots). Capability B identifies the upper bound on the number of values that can be sent from the output port: a natural number NN denotes a bounded capability, whereas \infty an unbounded one. Dependencies are of two kinds: per each value dependencies are of the form x:Nx\!:\!N and initial dependencies are given by x:Ωx:\Omega. A dependency x:Nx\!:\!N says that each value emitted on yy requires the reception of one value on xx, and furthermore NN provides the (runtime) number of values available on xx (hence, initially N=0N=0). Instead, a dependency x:Ωx:\Omega says that yy initially depends on a (single) value received on xx, hence the dependency is dropped after the first input on xx.

Note that there are only two kinds of dependencies: a per each value dependency and an initial one. Since we aim at static typing, the dependencies that appear after the extraction of a type are either x:0x\!:\!0 or x:Ωx:\Omega, but for the sake of showing our results, we need to capture these values in the evolution of the types. So, we need to capture the number of values available on the input ports, hence we have dependencies of the kind x:1,x:2,x\!:\!1,x\!:\!2,\dots (for N=1,N=2N=1,N=2\dots).

Remark.

In this work we investigate the mathematical model for the purpose of showing our results, but we may already point towards practical applications. In particular, for the purpose of the (static) type verification we are aiming at, the counting required for the theoretical model would not be involved and the component type information available for developers would be as follows:

  • set of input ports with their basic types

  • set of constraints for each output port with the following information

    1. 1.

      the basic type associated to the output port

    2. 2.

      one of two possibilities for output port capability: bounded or unbounded

    3. 3.

      one of two possibilities for each kind of dependency: per each value or initial

Hence, for the sake of static type checking and from a developers perspective, apart the expected information regarding basic (value) types, the type information would be y:bounded or y:unbounded to what concerns output port capabilities and x:pereach or x:initial to what concerns dependencies.

Types and input interfaces Dependency kinds Boundaries
T=Δ<X_b;C>T\overset{\Delta}{=}<X_{\_}b;\textbf{C}>X_b=Δ{x_1(b_1),,x_k(b_k)}X_{\_}b\overset{\Delta}{=}\{x_{\_}1(b_{\_}1),\dots,x_{\_}k(b_{\_}k)\} M::=N|ΩM::=N\ |\ \Omega B::=N|\textbf{B}::=N\ |\ \infty
Constraints Dependencies
C=Δ{y_1(b_1):B_1:[D_1],,y_k(b_k):B_k:[D_k]}\textbf{C}\overset{\Delta}{=}\{y_{\_}1(b_{\_}1):\textbf{B}_{\_}1:[\textbf{D}_{\_}1],\dots,y_{\_}k(b_{\_}k):\textbf{B}_{\_}k:[\textbf{D}_{\_}k]\} D=Δ{x_1:M_1,,x_k:M_k}\textbf{D}\overset{\Delta}{=}\{x_{\_}1\!:\!M_{\_}1,\dots,x_{\_}k\!:\!M_{\_}k\} k0N_0k\geq 0\quad N\in\mathbb{N}_{\_}0
Table 4: Type Syntax

Semantics

We now define the operational semantics of the type language, that is required to show that types faithfully capture component behaviour. The semantics is given by the LTS shown in Table 5. There are four kinds of labels λ\lambda described by the following grammar: λ::=x?|x?(b)|y!(b)|τ\lambda::=x?\ |\ x?(b)\ |\ y!(b)\ |\ \tau. Label x?x? denotes an input on xx; whereas, label x?(b)x?(b) denotes an input of a value of type bb; then, label y!(b)y!(b) represents an output of a value of type bb; finally, τ\tau captures an internal step.

We briefly describe the rules shown in Table 5. Rules [T1,T2,T3] describe inputs of a (single) constraint, while [T4, T5, T6] capture type behaviour. Rule [T1] says a constraint for yy can receive (and discard) an input on xx in case yy does not depend on xx, i.e., if xx is not in the domain of D (dom(D)={x|D={x:M}D}dom(\textbf{D})=\{x\ |\ \textbf{D}=\{x\!:\!M\}\uplus\textbf{D}^{\prime}\}), leaving the constraint unchanged. Rule [T2] addresses the case of an initial dependency, where after receiving the value on xx the dependency is removed. Rule [T3] captures the case of a per each value dependency, where after the reception the number of values available on xx for yy is incremented.

With respect to type behaviour, Rule [T4] says that the type can exhibit an internal step and remain unchanged, used to mimic component internal steps (which have no impact on the interface). Rule [T5] states that if all type constraints can exhibit an input on xx and xx is part of the type input interface, then the type can exhibit the input on xx considering the respective basic type. Notice that rules [T4, T5, T6] say that constraints can always exhibit an input (simply the effect may be different). Finally, Rule [T6] says that if one of the constraints has all of the dependencies met, i.e., has at least one value for each xx for which there is a dependency, and also that the boundary has not been reached (i.e., it is greater than zero), then the type can exhibit the corresponding output implying the decrement of the boundary and of the number of values available in dependencies. Notice that in order for a port to output a value, there can be no initial dependencies present (which are dropped once satisfied), only per each value dependencies.

xdom[D][T1]y(b):B:[D]x?y(b):B:[D]y(b):\textbf{B}:[\textbf{D}]\xrightarrow{\text{$x?$}}y(b):\textbf{B}:[\textbf{D}]x\notin dom[\textbf{D}] [T2]y(b):B:[{x:Ω}D]x?y(b):B:[D]y(b):\textbf{B}:[\{x:\Omega\}\uplus\textbf{D}]\xrightarrow{\text{$x?$}}y(b):\textbf{B}:[\textbf{D}] [T3]y(b):B:[{x:N}D]x?y(b):B:[{x:N+1}D]y(b):\textbf{B}:[\{x\!:\!N\}\uplus\textbf{D}]\xrightarrow{\text{$x?$}}y(b):\textbf{B}:[\{x\!:\!N+1\}\uplus\textbf{D}] [T4]T𝜏TT\xrightarrow{\text{$\tau$}}T i1,2,,ky_i(b_i):B_i:[D_i]x?y_i(b_i):B_i:[D_i][T5]<{x(bx)X_b};{y_i(b_i):B_i:[D_i]|i1,,k}>x?(bx)<{x(bx)X_b};{y_i(b_i):B_i:[D_i]|i1,,k}><\{x(b^{x})\uplus X_{\_}b\};\{y_{\_}i(b_{\_}i):\textbf{B}_{\_}i:[\textbf{D}_{\_}i]|i\in 1,\dots,k\}>\xrightarrow{\text{$x?(b^{x})$}}<\{x(b^{x})\uplus X_{\_}b\};\{y_{\_}i(b_{\_}i):\textbf{B}_{\_}i:[\textbf{D}^{\prime}_{\_}i]|i\in 1,\dots,k\}>\lx@proof@logical@and\forall i\in 1,2,\dots,ky_{\_}i(b_{\_}i):\textbf{B}_{\_}i:[\textbf{D}_{\_}i]\xrightarrow{\text{x?}}y_{\_}i(b_{\_}i):\textbf{B}_{\_}i:[\textbf{D}_{\_}i^{\prime}] i1,2,,kN_i1B>0[T6]<X_b;{y(by):B:[{x_i:N_i|i1,,k}]}C>y!(by)<X_b;{y(by):B1:[{x_i:N_i1|i1,,k}]}C><X_{\_}b;\{y(b^{y}):\textbf{B}:[\{x_{\_}i\!:\!N_{\_}i|i\in 1,\dots,k\}]\}\uplus\textbf{C}>\xrightarrow{\text{$y!(b^{y})$}}<X_{\_}b;\{y(b^{y}):\textbf{B}-1:[\{x_{\_}i\!:\!N_{\_}i-1|i\in 1,\dots,k\}]\}\uplus\textbf{C}>\lx@proof@logical@and\forall i\in 1,2,\dots,kN_{\_}i\geq 1\textbf{B}>0

Table 5: Type Semantics

In the following example and in the rest of the paper (where appropriate) we adopt the following notation: ii abbreviates the 𝑖𝑚𝑎𝑔𝑒\mathit{image} type, cc abbreviates the 𝑐𝑙𝑎𝑠𝑠\mathit{class} type and vv abbreviates the 𝑣𝑒𝑟𝑠𝑖𝑜𝑛\mathit{version} type.

Example 4.1.

We revisit the type of component K_𝑃𝑜𝑟𝑡𝑎𝑙K_{\_}{\mathit{Portal}} shown in Section 3

<{x_p(i),x_p(c)};{y_p(i)::[{x_p:N_1}],y_p(c)::[{x_p:N_2}],y_p′′(v)::[]}><\{x_{\_}p(i),x_{\_}p^{\prime}(c)\};\{y_{\_}p(i):\infty:[\{x_{\_}p\!:\!N_{\_}1\}],y_{\_}p^{\prime}(c):\infty:[\{x_{\_}p^{\prime}\!:\!N_{\_}2\}],y_{\_}p^{\prime\prime}(v):\infty:[\emptyset]\}>

for some N_1N_{\_}1 and N_2N_{\_}2. Recall also type <{x(i)};{y(c):1:[{x:Ω}],y(v):1:[]}><\{x(\mathit{i})\};\{y(\mathit{c}):1:[\{x:\Omega\}],y^{\prime}(\mathit{v}):1:[\emptyset]\}> that may evolve upon the reception of an input on xx as follows:

[T2]y(c):1:[{x:Ω}]x?y(c):0:[]xdom[D][T1]y(v):1:[]x?y(v):1:[][T5]<{x(i)};{y(c):1:[{x:Ω}],y(v):1:[]}>x?(i)<{x(i)};{y(c):0:[],y(v):1:[]}><\{x(i)\};\{y(c):1:[\{x:\Omega\}],y^{\prime}(v):1:[\emptyset]\}>\xrightarrow{\text{$x?(i)$}}<\{x(i)\};\{y(c):0:[\emptyset],y^{\prime}(v):1:[\emptyset]\}>\lx@proof@logical@and y(c):1:[\{x:\Omega\}]\xrightarrow{\text{$x?$}}y(c):0:[\emptyset]y^{\prime}(v):1:[\emptyset]\xrightarrow{\text{$x?$}}y^{\prime}(v):1:[\emptyset]x\notin dom[\textbf{D}]

The type language serves as a means to capture component behaviour, and types for components may be obtained (inferred) as explained below. The results presented afterwards ensure that when the type extraction is possible, then each behaviour in the component is explained by a behaviour in the type, and that each behaviour in the type can eventually be exhibited by the component.

4.1 Type extraction for base components

We describe the procedure that allows to (automatically) extract the type of a component, focusing first on the case of base components, remembering their reactive flavour. The goal is to identify the basic types associated to the communication ports, as well as the dependencies between them, while checking that their usage is consistent throughout.

In order to extract the type of a base component we need to define two auxiliary functions. First, we assume that from each function f(x~)f(\tilde{x}) used in a local binder, we can infer the respective function type. We introduce the notation γ()\gamma(\cdot) to represent a mapping from basic elements (such as values, ports, or functions) to their respective types. We also use γ\gamma for lists of elements in which case we obtain the list of respective types (e.g., γ(1,𝚑𝚎𝚕𝚕𝚘)=𝑖𝑛𝑡𝑒𝑔𝑒𝑟,𝑠𝑡𝑟𝑖𝑛𝑔\gamma(1,\mathtt{hello})=\mathit{integer},\mathit{string}). Second, given a local binder y=f(x~)<σ~y=f(\tilde{x})<\tilde{\sigma}, we need to count the number of values that yy has available at runtime for each of the ports in x~\tilde{x}. This corresponds to the number of elements in σ~\tilde{\sigma} that have a mapping for a port xx to a value, which we denote by count(x,σ~)\textit{count}(x,\tilde{\sigma}) defined as follows. Let XX be the set of ports and Σ\Sigma a set whose elements are the lists of mappings from ports to values. Then function count:X×Σ_0count:X\times\Sigma\rightarrow\mathbb{N}_{\_}0 is defined as follows:

count(x,σ~)=\textit{count}(x,\tilde{\sigma})= {jif σ~=σ_1,,σ_j,σ_j+1,,σ_lx_1ij𝖽𝗈𝗆(σ_i)x_j+1il𝖽𝗈𝗆(σ_i)0otherwise\begin{cases}j&\mbox{if }\ \tilde{\sigma}=\sigma_{\_}1,\dots,\sigma_{\_}j,\sigma_{\_}{j+1},\ldots,\sigma_{\_}l\wedge x\in\bigcap_{\_}{1\leq i\leq j}\mathsf{dom}(\sigma_{\_}i)\ \wedge\ x\notin\bigcup_{\_}{j+1\leq i\leq l}\mathsf{dom}(\sigma_{\_}i)\\ 0&\mbox{otherwise}\end{cases}

Notice that mappings in σ~\tilde{\sigma} are handled following a FIFO discipline, so the first (oldest) mappings are the ones that need to be accounted for. We may now define our type extraction procedure for base components:

Definition 4.1 (Type Extraction for a Base Component).
\thlabel

extractbase

Let [x~>y~]{y_1=f_y_1(x~y_1)<σ~y_1,,y_k=f_y_k(x~y_k)<σ~y_k}[\tilde{x}>\tilde{y}]\{y_{\_}1=f_{\_}{y_{\_}1}(\tilde{x}^{y_{\_}1})<\tilde{\sigma}^{y_{\_}1},\dots,y_{\_}k=f_{\_}{y_{\_}k}(\tilde{x}^{y_{\_}k})<\tilde{\sigma}^{y_{\_}k}\} be a base component, where y~=y_1,y_2,,y_k\tilde{y}=y_{\_}1,y_{\_}2,\dots,y_{\_}k. If there exists γ\gamma such that γ(x~)=b~\gamma(\tilde{x})=\tilde{b} and γ(y_1)=b_1,,γ(y_k)=b_k\gamma(y_{\_}1)=b_{\_}1^{\prime},\dots,\gamma(y_{\_}k)=b_{\_}k^{\prime} and provided that γ(f_y_i)=b~y_ib_i\gamma(f_{\_}{y_{\_}i})=\tilde{b}^{y_{\_}i}\rightarrow b_{\_}i^{\prime} for any i1,,ki\in 1,\dots,k and that b~y_i=γ(x~y_i)\tilde{b}^{y_{\_}i}=\gamma(\tilde{x}^{y_{\_}i}) for any i1,,ki\in 1,\dots,k then the extracted type of the base component is <X_b;C><X_{\_}b;\textbf{C}> where X_b={x(b)|xx~b=γ(x)}X_{\_}b=\{x(b)\ |\ x\in\tilde{x}\wedge b=\gamma(x)\} and

C={y_i(b_i)::D_y_i|i1,,kb_i=γ(y_i)D_y_i={x:count(x,σ~y_i)|xx~y_i}}\textbf{C}=\{y_{\_}i(b_{\_}i’):\infty:\textbf{D}_{\_}{y_{\_}i}\ |\ i\in 1,\dots,k\wedge b_{\_}i^{\prime}=\gamma(y_{\_}i)\wedge\textbf{D}_{\_}{y_{\_}i}=\{x:\textit{count}(x,\tilde{\sigma}^{y_{\_}i})\ |\ x\in\tilde{x}^{y_{\_}i}\}\}

In \threfextractbase the list of local binders is specified in such a way that each function (f_y_if_{\_}{y_{\_}i}), its parameters (x~y_i\tilde{x}^{y_{\_}i}) and the list of mappings (σ~y_i\tilde{\sigma}^{y_{\_}i}) are indexed with the output port that is associated to them (y_iy_{\_}i), so as to allow for a direct identification. Moreover, notice that each list of arguments x~y_i\tilde{x}^{y_{\_}i} (of function f_y_if_{\_}{y_{\_}i}) is a permutation of list x~\tilde{x}, as otherwise they would be undefined. Notice also that every output port of the interface of the component has a local binder associated to it and that there is no local binder y_t=f_y_t(x~y_t)<σ~y_ty_{\_}t=f_{\_}{y_{\_}t}(\tilde{x}^{y_{\_}t})<\tilde{\sigma}^{y_{\_}t} such that y_ty_{\_}t is not part of the component interface, i.e., we do not type components that have undefined output ports or that declare unused local binders, respectively. We also rely in \threfextractbase on (the existence of) γ\gamma to ensure consistency. Namely, we consider γ\gamma provides the list of basic types for the input ports (γ(x~)=b~\gamma(\tilde{x})=\tilde{b}) and for the output ports (γ(y_1)=b_1,,γ(y_k)=b_k\gamma(y_{\_}1)=b_{\_}1^{\prime},\dots,\gamma(y_{\_}k)=b_{\_}k^{\prime}). Then, we require that γ(f_y_i)\gamma(f_{\_}{y_{\_}i}), for each f_y_if_{\_}{y_{\_}i}, specifies the function type where the return type matches the one identified for y_iy_{\_}i (i.e., b_ib_{\_}i^{\prime}). Furthermore, we require that the types of the parameters given in the function type (b~y_i\tilde{b}^{y_{\_}i}) match the ones identified for the respective (permutation of) input port parameters (γ(x~y_i)\gamma(\tilde{x}^{y_{\_}i})).

We then have that the extracted type of a base component is a composition of two elements. The first one (X_bX_{\_}b) is a set of input ports which are associated with their basic types. The second one is a set of constraints C, one for each output port and of the form y_i(b_i)::[D_y_i]y_{\_}i(b_{\_}i^{\prime}):\infty:[\textbf{D}_{\_}{y_{\_}i}]. The constraint specifies the basic type (b_ib_{\_}i^{\prime}) which is associated to the output port, and the maximum number of values that can be output on y_iy_{\_}i is unbounded (\infty), since local binders can potentially perform computations indefinitely. The third element of the constraint (D_y_i\textbf{D}_{\_}{y_{\_}i}) is a set of per each value dependencies (of port y_iy_{\_}i) on the input port parameters x~y_i\tilde{x}^{y_{\_}i}, capturing that each value produced on y_iy_{\_}i depends on a value being received on all of the ports in x~y_i\tilde{x}^{y_{\_}i}. Notice that the number of values that y_iy_{\_}i has available (at runtime) for each xx in x~y_i\tilde{x}^{y_{\_}i} is given by count(x,σy_i)\textit{count}(x,\sigma^{y_{\_}i}).

From an operational perspective, \threfextractbase can be implemented by first considering the type inferred for the functions in the local binders and then propagating (while ensuring consistency of) this information.

Example 4.2.

Consider our running example from Section 3, in particular, component K_PortalK_{\_}{Portal} specified as [x_p,x_py_p,y_p,y_p′′]{y_p=f_u(x_p)<σ~y_p,y_p=f_r(x_p)<σ~y_p,y_p′′=f()<}[{x_{\_}p,x_{\_}p^{\prime}}\,\rangle\,{y_{\_}p,y_{\_}p^{\prime},y_{\_}p^{\prime\prime}}]\{y_{\_}p=f_{\_}u(x_{\_}p)<\tilde{\sigma}^{y_{\_}p},y_{\_}p^{\prime}=f_{\_}r(x_{\_}p^{\prime})<\tilde{\sigma}^{y_{\_}p^{\prime}},y_{\_}p^{\prime\prime}=f()<\cdot\}.

Let us take γ\gamma such that γ(x_p,x_p)=i,c\gamma(x_{\_}p,x_{\_}p^{\prime})=i,c and γ(y_p)=i\gamma(y_{\_}p)=i, γ(y_p)=c\gamma(y_{\_}p^{\prime})=c and γ(y_p′′)=v\gamma(y_{\_}p^{\prime\prime})=v. We know that function f_uf_{\_}u takes an 𝑖𝑚𝑎𝑔𝑒\mathit{image} (ii) and gives an 𝑖𝑚𝑎𝑔𝑒\mathit{image} in return, hence γ(f_u)=ii\gamma(f_{\_}u)=i\rightarrow i. Similarly, we also know that function f_rf_{\_}r is typed as γ(f_r)=cc\gamma(f_{\_}r)=c\rightarrow c. Function ff does not have any parameters hence γ(time)=()v\gamma(time)=()\rightarrow v. The extracted set of input ports with their types is X_b={x_p(i),x_p(c)}X_{\_}b=\{x_{\_}p(i),x_{\_}p^{\prime}(c)\}. Assume that the component is in the initial (static) state, so the queues of lists of mappings are empty (i.e., σ~y_p==σ~y_p\tilde{\sigma}^{y_{\_}p}=\cdot=\tilde{\sigma}^{y_{\_}p^{\prime}}). Hence, we have that count(x_p,σ~y_p)=0\textit{count}(x_{\_}p,\tilde{\sigma}^{y_{\_}p})=0 and count(x_p,σ~y_p)=0\textit{count}(x_{\_}p^{\prime},\tilde{\sigma}^{y_{\_}p^{\prime}})=0. The extracted set of constraints is then C={y_p(i)::[{x_p:0}],y_p(c)::[{x_p:0}],y_p′′(v)::[]}\textbf{C}=\{y_{\_}p(i):\infty:[\{x_{\_}p\!:\!0\}],y_{\_}p^{\prime}(c):\infty:[\{x_{\_}p^{\prime}\!:\!0\}],y_{\_}p^{\prime\prime}(v):\infty:[\emptyset]\} and the extracted type of the component K_PortalK_{\_}{Portal} is <X_b;C><X_{\_}b;\textbf{C}>.

4.2 Type extraction for composite components

Extracting the type of a composite component is more challenging than for a base component. The focus of the extraction procedure is on the interfacing subcomponent, which interacts both via forwarders and via the protocol. For the purpose of characterising how components interact in a given protocol, we introduce local protocols LPLP which result from the projection of a (global) protocol to a specific role that is associated to a component. We reuse the projection operation from [6], where message labels are mapped to communication ports (thanks to distribution binders DD) and also to basic types that describe the communicated values (that can be inferred from the ones of the ports). The syntax of local protocols LPLP is:

LP:=x?:b.LP|y!:b.LP|μ𝐗.LP|𝐗|𝐞𝐧𝐝LP:=x?\!:\!b.LP\ |\ y!\!:\!b.LP\ |\ \mu\mathbf{X}.LP\ |\ \mathbf{X}\ |\ \mathbf{end}.

Term x?:b.LPx?\!:\!b.LP denotes a reception of a value of a type bb on port xx, upon which protocol LPLP is activated. Term y!:b.LPy!\!:\!b.LP describes an output in similar lines. Then we have standard constructs for recursion and for specifying termination (𝐞𝐧𝐝\mathbf{end}). Our local protocols differ from the ones used in [6] since here we only consider choice-free global protocols. To simplify the setting, we consider global protocols that have at most one recursion (consequently also the projected local protocols). We also consider that message labels can appear at most once in a global protocol specification (up to unfolding of recursion), hence also ports occur only once in projected local protocols (also up to unfolding).

We omit the definition of projection and present the intuition via an example.

Example 4.3.

Let GG be the (one-shot) protocol G=𝖯𝗈𝗋𝗍𝖺𝗅image𝖱𝖤;𝖱𝖤class𝖯𝗈𝗋𝗍𝖺𝗅;𝐞𝐧𝐝G=\mathsf{Portal}\!\!\!\ \mathrel{\stackrel{{\scriptstyle{\;\;image\;\;}}}{{\mbox{\rightarrowfill}}}}\ \!\!\!\mathsf{RE};\mathsf{RE}\!\!\!\ \mathrel{\stackrel{{\scriptstyle{\;\;class\;\;}}}{{\mbox{\rightarrowfill}}}}\ \!\!\!\mathsf{Portal};\mathbf{end} from Section 3 and let γ(image,class)=i,c\gamma(image,class)=i,c be a function that given a list of a message labels returns a list of their types.

Then, the projection of protocol GG to role 𝖯𝗈𝗋𝗍𝖺𝗅\mathsf{Portal}, denoted by G_𝖯𝗈𝗋𝗍𝖺𝗅G\downarrow_{\_}{\mathsf{Portal}} is protocol y_p!:i.x_p?:c.𝐞𝐧𝐝y_{\_}p!\!:\!i.x_{\_}p^{\prime}?\!:\!c.\mathbf{end} and the projection of GG to role 𝖱𝖤\mathsf{RE} is local protocol x_re?:i.y_re!:c.𝐞𝐧𝐝x_{\_}{re}?\!:\!i.y_{\_}{re}!\!:\!c.\mathbf{end}, where ports x_p,y_p,x_re,y_rex_{\_}p^{\prime},y_{\_}p,x_{\_}{re},y_{\_}{re} are obtained via distribution binders 𝖱𝖤.x_reimage𝖯𝗈𝗋𝗍𝖺𝗅.y_p,𝖯𝗈𝗋𝗍𝖺𝗅.x_pclass𝖱𝖤.y_re\mathsf{RE}.{x_{\_}{re}}\mathrel{\stackrel{{\scriptstyle{\;\;image\;\;}}}{{\mbox{\leftarrowfill}}}}{\mathsf{Portal}.{y_{\_}p}},\mathsf{Portal}.{x_{\_}p^{\prime}}\mathrel{\stackrel{{\scriptstyle{\;\;class\;\;}}}{{\mbox{\leftarrowfill}}}}{\mathsf{RE}.{y_{\_}{re}}}. Essentially, the local protocol of 𝖯𝗈𝗋𝗍𝖺𝗅\mathsf{Portal} describes that first it emits an image on y_py_{\_}p and then receives a classification on x_px_{\_}p^{\prime}, and the local protocol of 𝖱𝖤\mathsf{RE} says that it first receives an image on x_rex_{\_}{re} and then outputs a result of a classification on y_rey_{\_}{re}.

We introduce some notation useful for the definition of the type extraction for composite components. We use the language context for local protocols (excluding recursion), denoted by 𝒞\mathcal{C}, so as to abstract from the entire local protocol and focus on specific parts and we define it as: 𝒞[]::=x?:b.𝒞[]|y!:b.𝒞[]|\mathcal{C}[\ \cdot\ ]::=x?:b.\mathcal{C}[\ \cdot\ ]\ |\ y!:b.\mathcal{C}[\ \cdot\ ]\ |\ \cdot. We denote the set of ports appearing in a local protocol by 𝑓𝑝(LP)\mathit{fp}(LP) and by 𝑟𝑒𝑝(LP)\mathit{rep}(LP) the set of ports that occur in a recursion (e.g. in LPLP for recursion μ𝐗.LP\mu\mathbf{X}.LP). Considering a list of forwarders FF, we define two sets: by FiF^{i} we denote the set of (internal) input ports and by FoF^{o} the set of (internal) output ports which are specified in FF (e.g., if F=x_pxF=x_{\_}p\leftarrow x\ then Fi={x_p}F^{i}=\{x_{\_}p\}).

We now introduce the important notions that are used in our type extraction, namely that account for values flowing in a protocol and for the kinds of dependencies involved in composite components. Finally, we address the boundaries for the output ports.

Values flowing

Our types track the dependencies between output and input ports, including per each value dependencies that specify how many values received on the input port are available to the output port. As discussed in the previous section, for base components this counter is given by the number of values available in the local binder queues. For composite components, as preliminary discussed in Section 3, per each value dependencies might actually result from a chain of dependencies that involve subcomponents and the protocol. So, in order to count how many values are available in such case, we need to take into account how many values are in the subcomponents (which is captured by their types) and also if a value is flowing in the protocol. We can capture the fact that a value is flowing by inspecting the structure of the protocol. In particular we are interested in values that flow from yy to xx when an output on yy precedes an input in xx in a recursive protocol, hence when the protocol is of the form 𝒞[μ𝐗.𝒞[y!:b.𝒞′′[x?:b.LP]]\mathcal{C}[\mu\mathbf{X}.\mathcal{C}^{\prime}[y!\!:\!b^{\prime}.\mathcal{C}^{\prime\prime}[x?\!:\!b.LP^{\prime}]]. The value is flowing when the output has been carried out but the input is yet to occur, which we may conclude if the protocol is also of the form 𝒞′′′[x?:b.LP′′]\mathcal{C}^{\prime\prime\prime}[x?\!:\!b.LP^{\prime\prime}] where x,y𝑓𝑝(𝒞′′′[])x,y\notin\mathit{fp}(\mathcal{C}^{\prime\prime\prime}[\ \cdot\ ]). We denote by 𝑣𝑓(LP,x,y)\mathit{vf}(LP,x,y) that there is a value flowing from yy to xx in LPLP, in which case 𝑣𝑓(LP,x,y)=1\mathit{vf}(LP,x,y)=1, otherwise 𝑣𝑓(LP,x,y)=0\mathit{vf}(LP,x,y)=0. We will return to this notion in the context of the extraction of the dependencies of the output ports, discussed next.

Kinds of dependencies

Composite components comprise two kinds of dependencies between output ports and input ports, illustrated in Figure 2 and Figure 2, which are dubbed direct and transitive, respectively.

Refer to caption
Figure 1: Direct Dependency
Refer to caption
Figure 2: Transitive Dependency

We gather the set of direct dependencies, i.e., when external output ports directly depend on external input ports (see Figure 2), in D_d(C,F,y)\textbf{D}_{\_}d(\textbf{C},F,y) which is defined as follows:

D_d(C,F,y){x:M|C={y(by):B:[{x:M}D]}CxFiyFo}\textbf{D}_{\_}d(\textbf{C},F,y)\triangleq\{x\!:\!M\ |\ \textbf{C}=\{y(b^{y}):\textbf{B}:[\{x\!:\!M\}\uplus\textbf{D}]\}\uplus\textbf{C}^{\prime}\wedge x\in F^{i}\wedge y\in F^{o}\}

Hence, in D_d(C,F,y)\textbf{D}_{\_}d(\textbf{C},F,y) we collect all the dependencies for yy given in (internal) constraint C whenever both ports are external and preserving the kind of dependency MM so as to lift it to the outer interface.

For transitive dependencies (see Figure 2) to exist there are three necessary conditions. The first condition is to have in the description of a local protocol at least one output action, say on port yy^{\prime}, that precedes at least one input action, say on port xx^{\prime}. The second condition is that such output port yy^{\prime} depends on some external input port xx and the third condition is that there exists some external output port yy that depends on the input on xx^{\prime}. In such cases, we say that yy depends on xx in a transitive way.

We introduce a relation that allows to capture the first condition above. Let LPLP be the local protocol that is prescribed for an interfacing component. Two ports xx^{\prime} and yy^{\prime} are in relation _LPi\diamond^{LP}_{\_}i for some local protocol LPLP if x,y𝑓𝑝(LP)x^{\prime},y^{\prime}\in\mathit{fp}(LP) and where i{1,2,3}i\in\{1,2,3\} as follows: y_LP1xy^{\prime}\diamond^{LP}_{\_}1x^{\prime} if LP=𝒞[y!:by.𝒞[x?:bx.LP]]LP=\mathcal{C}[y^{\prime}!\!:\!b^{y^{\prime}}.\mathcal{C}^{\prime}[x^{\prime}?\!:\!b^{x^{\prime}}.LP^{\prime}]] and x,y𝑟𝑒𝑝(LP)x^{\prime},y^{\prime}\notin\mathit{rep}(LP); y_LP2xy^{\prime}\diamond^{LP}_{\_}2x^{\prime} if LP=𝒞[y!:by.𝒞[μ𝐗.𝒞′′[x?:bx.LP]]]LP=\mathcal{C}[y^{\prime}!\!:\!b^{y^{\prime}}.\mathcal{C}^{\prime}[\mu\mathbf{X}.\mathcal{C}^{\prime\prime}[x^{\prime}?\!:\!b^{x^{\prime}}.LP^{\prime}]]] and y𝑟𝑒𝑝(LP)y^{\prime}\notin\mathit{rep}(LP); y_LP3xy^{\prime}\diamond^{LP}_{\_}3x^{\prime} if LP=𝒞[μ𝐗.𝒞[y!:by.𝒞′′[x?:bx.LP]]]LP=\mathcal{C}[\mu\mathbf{X}.\mathcal{C}^{\prime}[y^{\prime}!\!:\!b^{y^{\prime}}.\mathcal{C}^{\prime\prime}[x^{\prime}?\!:\!b^{x^{\prime}}.LP^{\prime}]]]. We distinguish three cases: when both the output and the input are non-repetitive, when only the input is repetitive, and when both the output and the input are repetitive.

We may now characterise the transitive dependencies. Let [x~y~]{G;r=K,R;D;𝗋[F]}[{\tilde{x}^{\prime}}\,\rangle\,{\tilde{y}^{\prime}}]\{G;r=K,R;D;\mathsf{r}[F]\} be the composite component, T_r=<X_b,C>T_{\_}r=<X_{\_}b,\textbf{C}> the type of interfacing component KK and LPLP its local protocol. The set of transitive dependencies on yy, denoted D_t(C,F,LP,y)\textbf{D}_{\_}t(\textbf{C},F,LP,y), is defined relying on an abbreviation η\eta as follows:

η\eta == C={y(b_1):B:[{x:M}D],y(b_2):B:[{x:M}D]}C\textbf{C}=\{y(b_{\_}1):\textbf{B}:[\{x^{\prime}\!:\!M^{\prime}\}\uplus\textbf{D}^{\prime}],y^{\prime}(b_{\_}2):\textbf{B}^{\prime}:[\{x\!:\!M\}\uplus\textbf{D}]\}\uplus\textbf{C}^{\prime}
xFiyFoy_LPix\wedge\ x\in F^{i}\wedge y\in F^{o}\wedge y^{\prime}\diamond^{LP}_{\_}ix^{\prime}
D_t(C,F,LP,y)\textbf{D}_{\_}t(\textbf{C},F,LP,y) \triangleq {x:Ω|ηi{1,2}M0}\big{\{}x:\Omega\ \ |\ \ \eta\wedge i\in\{1,2\}\wedge M\ngtr 0\}
\bigcup
{x:Ω|ηi=3(M=Ω(M=ΩM=0𝑣𝑓(LP,x,y)=0))}\big{\{}x:\Omega\ \ |\ \ \eta\wedge i=3\wedge(M=\Omega\lor(M^{\prime}=\Omega\wedge M=0\wedge\mathit{vf}(LP,x^{\prime},y^{\prime})=0))\big{\}}
\bigcup
{x:(N+N+𝑣𝑓(LP,x,y))|ηi=3M=NM=N}\big{\{}x\!:\!(N+N^{\prime}+\mathit{vf}(LP,x^{\prime},y^{\prime}))\ \ |\ \ \eta\wedge i=3\wedge M=N\wedge M^{\prime}=N^{\prime}\big{\}}

In η\eta we gather a conjunction of conditions that must always hold in order for a transitive dependency to exist: namely that the (internal) constraint C specifies dependencies between yy and xx^{\prime} and between yy^{\prime} and xx and also that yy and xx are external ports while yy^{\prime} precedes xx^{\prime} in the protocol. To simplify presentation of the definition of D_t(C,F,LP,y)\textbf{D}_{\_}t(\textbf{C},F,LP,y) we rely on the (direct) implicit matching in η\eta of the several mentioned elements.

There are two kinds of transitive dependencies that are gathered in D_t(C,F,LP,y)\textbf{D}_{\_}t(\textbf{C},F,LP,y), namely initial (x:Ωx:\Omega) and per each value (x:Nx\!:\!N). For initial dependencies there are two separate cases to consider. The first case is when the protocol specifies that the output on yy^{\prime} is non-repetitive (i{1,2}i\in\{1,2\}), hence will be provided only once. Condition M0M\ngtr 0 says that no values are already available for that initial output to take place (internally to the component that provides them as specified in η\eta), hence either M=x:ΩM=x:\Omega or M=0M=0.

The second case for an initial transitive dependency is when both yy^{\prime} and xx^{\prime} are repetitive in the protocol (i=3i=3) but at least one of the internal dependencies (between yy^{\prime} and xx and between yy and xx^{\prime}, given by MM and MM^{\prime} respectively) is an initial dependency. This means that, regardless of the protocol, such a dependency is dropped as soon as a value is provided which implies that the transitive dependency is also dropped. Since MM is at the beginning of the dependency chain, if it is initial then no further conditions are necessary. However, if MM^{\prime} is initial we need to ensure that there is no value already flowing (𝑣𝑓(LP,x,y)=0\mathit{vf}(LP,x^{\prime},y^{\prime})=0) or already available to be output on yy^{\prime} (M=0M=0), since only in such case (an initial) value is required from the external context (i.e., otherwise if 𝑣𝑓(LP,x,y)=1\mathit{vf}(LP,x^{\prime},y^{\prime})=1 or M0M\geq 0 then the chain of dependencies is already “internally” satisfied).

Finally, we have the case of per each value transitive dependency, that can only be when both yy^{\prime} and xx^{\prime} are repetitive in the protocol (i=3i=3) and internal dependencies MM and MM^{\prime} are both per each value dependencies (M=NM=N and M=NM^{\prime}=N^{\prime}), which means that the dependency chain is persistent. The number of values available of (external) xx for yy is the sum of the values available in the internal dependencies (NN and NN^{\prime}) plus one if there is a value flowing (zero otherwise). Notice that the definition of value flowing presented previously focuses exclusively in the case when yy^{\prime} and xx^{\prime} are repetitive in the protocol, since this is the only case where values might be flowing and the dependency is still present in the protocol structure (i.e., y_LP3xy^{\prime}\diamond^{LP}_{\_}3x^{\prime} holds). In contrast, a dependency y_LPixy^{\prime}\diamond^{LP}_{\_}ix^{\prime} for i{1,2}i\in\{1,2\} is no longer (structurally) present as soon as the value is flowing (i.e., a non-repetitive yy^{\prime} no longer occurs in the protocol after an output).

It might be the case that one output port depends in multiple ways on the same input port. For that reason we introduce a notion of priority among dependencies, denoted by pr(,)\textbf{pr}(\ ,\ ) that gives priority to per each value dependencies (with respect to “initial”). The priority builds on the property that if multiple per each value dependencies (including direct and transitive) are collected (e.g., x:N_1,,x:N_kx:N_{\_}1,\ldots,x:N_{\_}k) then the number of available values specified in them is the same (i.e., N_1==N_kN_{\_}1=\ldots=N_{\_}k). The list of dependencies for port yy is then given by the (prioritised) union of direct and transitive dependencies:

D(C,F,LP,y)=pr(D_d(C,F,y)D_t(C,F,LP,y))\textbf{D}(\textbf{C},F,LP,y)=\textbf{pr}(\textbf{D}_{\_}d(\textbf{C},F,y)\cup\textbf{D}_{\_}t(\textbf{C},F,LP,y))

Boundaries

The last element that we need to determine in order to extract the type of a composite component is the boundary of output ports. The type of the interfacing component already specifies a (internal) boundary, however this value may be further bound by the way in which the component is used in the composition. In particular, if an output port depends on input ports that are not used in the protocol nor are linked to external ports, then no (further) values are received in them and the potential for the output port is consequently limited. We distinguish three cases for three possible limitations:

B_1={N|C=y(b_1):B:[{x:N}D]Cx(𝑓𝑝(LP)Fi)}B_{\_}1=\{N^{\prime}\ |\ \textbf{C}=y(b_{\_}1):\textbf{B}:[\{x^{\prime}\!:\!N^{\prime}\}\uplus\textbf{D}^{\prime}]\uplus\textbf{C}^{\prime}\wedge x^{\prime}\notin(\mathit{fp}(LP)\cup F^{i})\}

B_2={0|C={y(b_1):B:[{x:Ω}D]}Cx(𝑓𝑝(LP)Fi)}B_{\_}2=\{0\ |\ \textbf{C}=\{y(b_{\_}1):\textbf{B}:[\{x^{\prime}:\Omega\}\uplus\textbf{D}^{\prime}]\}\uplus\textbf{C}^{\prime}\wedge x^{\prime}\notin(\mathit{fp}(LP)\cup F^{i})\}

B_3={(N+1)|C={y(b_1):B:[{x:N}D]}Cx𝑓𝑝(LP)x(𝑟𝑒𝑝(LP)Fi)}B_{\_}3=\{(N^{\prime}+1)\ |\ \textbf{C}=\{y(b_{\_}1):\textbf{B}:[\{x^{\prime}\!:\!N^{\prime}\}\uplus\textbf{D}^{\prime}]\}\uplus\textbf{C}^{\prime}\wedge x^{\prime}\in\mathit{fp}(LP)\wedge x^{\prime}\notin(\mathit{rep}(LP)\cup F^{i})\}

In B_1B_{\_}1 and B_2B_{\_}2 we capture the case when there is a dependency on a port that is not used in the protocol (x𝑓𝑝(LP)x^{\prime}\notin\mathit{fp}(LP)) nor linked externally (xFix^{\prime}\notin F^{i}), where the difference is in the kind of dependency. For per each value dependencies (if any), the minimum of the internally available values is identified as the potential boundary, while for initial dependencies (if present) the potential boundary is zero (or the empty set). In B_3B_{\_}3 we capture a case similar to B_1B_{\_}1 where the port is used in the protocol but in a non-repetitive way, hence only one (further) value can be provided.

The final boundary determined for yy, denoted by B(y,LP,C)\textbf{B}(y,LP,\textbf{C}), is the minimum number among the internal boundary of yy (i.e., B if C=y(b):B:[D]C\textbf{C}=y(b):\textbf{B}:[\textbf{D}]\uplus\textbf{C}^{\prime}) and possible boundaries B_1,B_2B_{\_}1,B_{\_}2 and B_3B_{\_}3 described above.

B(y,LP,C)=min({B}B_1B_2B_3)\textbf{B}(y,LP,\textbf{C})=min(\{\textbf{B}\}\cup B_{\_}1\cup B_{\_}2\cup B_{\_}3)

We may now present the definition of type extraction of a composite component relying on a renaming operation. Since the type extraction of a composite component focuses on the interfacing subcomponent, we single out the ports that are linked via forwarders to the external environment. To capture such links, we introduce renaming operation ren(,)\textbf{ren}(\ ,\ ) that renames the ports of the interfacing subcomponent to the outer ones by using the forwarders as a guideline. For example, if we have that F=x_pxF=x_{\_}p\leftarrow x\ than ren(F,x_p)=x\textbf{ren}(F,x_{\_}p)=x.

Definition 4.2 (Type Extraction for a Composite Component).
\thlabel

compositeextraction

Let [x~y~]{G;r=K,R;D;𝗋[F]}[{\tilde{x}}\,\rangle\,{\tilde{y}}]\{G;r=K,R;D;\mathsf{r}[F]\} be a composite component and LP=G_rLP=G\downharpoonright_{\_}r the local protocol for component KK. If T_r=<X_rb;Cr>T_{\_}r=<X^{r}_{\_}b;\textbf{C}^{r}> is the type of component KK, then the extracted type from LPLP and T_rT_{\_}r is

T(LP,T_r,F)=ren(F,<X_b;C>)T(LP,T_{\_}r,F)=\textbf{ren}(F,<X_{\_}b;\textbf{C}>)

where: X_b={x(b)|x(b)X_rbxFi}X_{\_}b=\{x(b)\ |\ x(b)\in X^{r}_{\_}b\wedge x\in F^{i}\}

C={y(b):B(y,LP,Cr):[D(Cr,F,LP,y)]|Cr={y(b):B:[D]}CyFo}\ \ \ \ \ \ \ \textbf{C}=\{y(b^{\prime}):\textbf{B}(y,LP,\textbf{C}^{r}):[\textbf{D}(\textbf{C}^{r},F,LP,y)]\ |\ \textbf{C}^{r}=\{y(b^{\prime}):\textbf{B}^{\prime}:[\textbf{D}^{\prime}]\}\uplus\textbf{C}^{\prime}\wedge y\in F^{o}\}.

Example 4.4.

Let us extract the type of component K_IRSK_{\_}{IRS} from Section 3 considering protocol G=𝖯𝗈𝗋𝗍𝖺𝗅image𝖱𝖤;𝖱𝖤class𝖯𝗈𝗋𝗍𝖺𝗅;𝐞𝐧𝐝G=\mathsf{Portal}\!\!\!\ \mathrel{\stackrel{{\scriptstyle{\;\;image\;\;}}}{{\mbox{\rightarrowfill}}}}\ \!\!\!\mathsf{RE};\mathsf{RE}\!\!\!\ \mathrel{\stackrel{{\scriptstyle{\;\;class\;\;}}}{{\mbox{\rightarrowfill}}}}\ \!\!\!\mathsf{Portal};\mathbf{end}. The type of interfacing component K_PortalK_{\_}{Portal} is

T_Portal=<{x_p(i),x_p(c)};{y_p(i)::[{x_p:N_p}],y_p(c)::[{x_p:N_p}],y_p′′(v)::[]}>T_{\_}{Portal}=<\{x_{\_}p(i),x_{\_}p^{\prime}(c)\};\{y_{\_}p(i):\infty:[\{x_{\_}p\!:\!N_{\_}p\}],y_{\_}p^{\prime}(c):\infty:[\{x_{\_}p^{\prime}\!:\!N_{\_}p^{\prime}\}],y_{\_}p^{\prime\prime}(v):\infty:[\emptyset]\}>

We have that local protocol is LP=y_p!:i.x_p?:c.𝐞𝐧𝐝LP=y_{\_}p!\!:\!i.x_{\_}p^{\prime}?\!:\!c.\mathbf{end} and sets of external ports Fi={x_p}F^{i}=\{x_{\_}p\} and Fo={y_p,y_p′′}F^{o}=\{y_{\_}p^{\prime},y_{\_}p^{\prime\prime}\}, where ren(F,x_p)=x\textbf{ren}(F,x_{\_}p)=x, ren(F,y_p)=y\textbf{ren}(F,y_{\_}p^{\prime})=y and ren(F,y_p′′)=y\textbf{ren}(F,y_{\_}p^{\prime\prime})=y^{\prime}. This immediately gives us the set of input ports that is in the description of the type of component K_IRSK_{\_}{IRS} which is X_b={x(i)}\ X_{\_}b=\{x(i)\}.

Let us now determine the constraints of the output ports. Since port y_p′′y_{\_}p^{\prime\prime} has no dependencies also port yy^{\prime} will not have any, and moreover has the same boundary (\infty). So, the extracted constraint for yy^{\prime} will be ren(y_p′′(v)::[])\textbf{ren}(y_{\_}p^{\prime\prime}(v):\infty:[\emptyset]), which is y(v)::[]y^{\prime}(v):\infty:[\emptyset]. Port y_py_{\_}p^{\prime} instead depends on port x_px_{\_}p^{\prime} which is used in the protocol (x_p𝑓𝑝(LP)x_{\_}p^{\prime}\in\mathit{fp}(LP)). Since the protocol is not recursive we have the consequent limited boundary (case B_3B_{\_}3 explained above), namely the boundary of y_py_{\_}p^{\prime} is min(N_p+1,)=N_p+1min(N_{\_}p^{\prime}+1,\infty)=N_{\_}p^{\prime}+1. Furthermore. we have that y_p_LP1x_py_{\_}p\diamond^{LP}_{\_}1x_{\_}p^{\prime} and that y_py_{\_}p has per each value dependency x_p:N_px_{\_}p\!:\!N_{\_}p. If N_p>0N_{\_}p>0 then y_py_{\_}p^{\prime} does not transitively depend on x_px_{\_}p, otherwise there is an initial dependency. Let us consider the initial (static) state where no image has been receive yet, i.e., N_p=0N_{\_}p=0. In such case we have that the resulting constraint for y_py_{\_}p^{\prime} is y_p(c):N_p:[x_p:Ω]y_{\_}p^{\prime}(c):N_{\_}p^{\prime}:[x_{\_}p:\Omega], which after renaming for yy is y(c):N_p:[x_p:Ω]y(c):N_{\_}p^{\prime}:[x_{\_}p:\Omega]. So, the extracted type of K_IRSK_{\_}{IRS} is the following

{x(i)};{y(c):N_p:[x_p:Ω],y(v)::[]}\{x(i)\};\{y(c):N_{\_}p^{\prime}:[x_{\_}p:\Omega],y^{\prime}(v):\infty:[\emptyset]\}

4.3 Type Safety

In this section we present our main results that show a tight correspondence between the behaviour of components and of their extracted types. Apart from the conditions already involved in the type extraction, for a component to be well-typed we must also ensure that any component that interacts in a protocol can actually carry out the communication actions prescribed by the protocol.

For this reason we introduce the conformance relation, denoted by \bowtie, that asserts compatibility between the type of a component and the local protocol that describes the communication actions prescribed for the component. For the purpose of ensuring compatibility, in particular for the interfacing component, we also introduce an extension of our type language, dubbed modified types 𝒯\mathcal{T} (see Appendix A). The idea for modified types is to allow to abstract from input dependencies from the external environment, namely by considering such dependencies can (always) potentially be fulfilled, allowing conformance to focus on internal compatibility. By 𝒯(F,T)\mathcal{T}(F,T) we denote the modified type that results from abstracting such external dependencies in TT, relying on forwarders FF, namely by considering per each value dependencies for external input ports are unbounded (\infty) and dropping initial dependencies. The rules for the semantics of modified types are the same as the ones shown in Section 4, the only implicit difference for modified types is that decrementing an unbounded dependency has no effect.

The definition of the conformance relation (see Appendix B) is given by induction on the structure of the local protocol and it is characterised by judgments of the form Γ𝒯LP\Gamma\vdash\mathcal{T}\bowtie LP, where Γ\Gamma is a type environment that handles protocol recursion (i.e., Γ\Gamma maps recursion variables to modified types). We report and comment here only on the rules for input and output:

𝒯x?(b)𝒯Γ𝒯LP[InpConf]Γ𝒯x?:b.LP𝒯y!(b)𝒯Γ𝒯LP[OutConf]Γ𝒯y!:b.LP\Gamma\vdash\mathcal{T}\bowtie x?\!:\!b.LP\lx@proof@logical@and\mathcal{T}\xrightarrow{\text{$x?(b)$}}\mathcal{T}^{\prime}\Gamma\vdash\mathcal{T}^{\prime}\bowtie LP\ \ \ \ \ \ \ \ \ \ \Gamma\vdash\mathcal{T}\bowtie y!\!:\!b.LP\lx@proof@logical@and\mathcal{T}\xrightarrow{\text{$y!(b)$}}\mathcal{T}^{\prime}\Gamma\vdash\mathcal{T}^{\prime}\bowtie LP

Rule [InpConfInpConf] states that a modified type 𝒯\mathcal{T} is conformant with protocol x?:b.LPx?\!:\!b.LP, if 𝒯\mathcal{T} can input a value of type bb on port xx and if continuation 𝒯\mathcal{T}^{\prime} is conformant with the continuation of protocol LPLP. Rule [OutConfOutConf] is similar but deals with the output of a value on port yy.

We can now formally define when a component KK has type TT, in which case we say KK is well-typed.

Definition 4.3.

Let KK be a component, we say that KK has a type TT, denoted by KTK\Downarrow T:

  1. 1.

    If KK is a base component, KTK\Downarrow T when TT is obtained by \threfextractbase.

  2. 2.

    If K=[x~>y~]{G;𝗋_𝟣=K_1,,𝗋_𝗄=K_k;D;𝗋_𝟣[F]}K=[\tilde{x}>\tilde{y}]\{G;\mathsf{r_{\_}1}=K_{\_}1,\dots,\mathsf{r_{\_}k}=K_{\_}k;D;\mathsf{r_{\_}1}[F]\} then KTK\Downarrow T when

    • T_r_i|K_iT_r_i,for i=1,2,,k\exists T_{\_}{r_{\_}i}\ |\ K_{\_}i\Downarrow T_{\_}{r_{\_}i},\text{for }i=1,2,\dots,k;

    • TT is extracted type from T_1T_{\_}{1} and G_r_1G\downharpoonright_{\_}{r_{\_}1} by \threfcompositeextraction;

    • 𝒯(F,T_r_i)G_r_ifor i=1,2,,k\mathcal{T}(F,T_{\_}{r_{\_}i})\bowtie G\downharpoonright_{\_}{r_{\_}i}\text{for }i=1,2,\dots,k;

Notice that the definition relies on modified types for conformance, but for any type TT not associated with the interfacing component we have that 𝒯(F,T)=T\mathcal{T}(F,T)=T since there can be no links to external ports (assuming that all ports have different identifiers).

We can now our type safety results given in terms of Subject Reduction and Progress, which provide the correspondence between the behaviours of well-typed components and their types. In the statements we rely on notation λ(v)\lambda(v) that represents x?(v)x?(v), y!(v)y!(v) or τ\tau and λ(b)\lambda(b) that represents x?(b)x?(b), y!(b)y!(b) or τ\tau.

Theorem 4.1 (Subject Reduction).
\thlabel

SubjectReduction If KTK\Downarrow T and Kλ(v)KK\xrightarrow{\textit{$\lambda$(v)}}K^{\prime} and vv has type bb then Tλ(b)TT\xrightarrow{\textit{$\lambda(b)$}}T^{\prime} and KTK^{\prime}\Downarrow T^{\prime}.

Proof.

By induction on the derivation of Kλ(v)KK\xrightarrow{\text{$\lambda(v)$}}K^{\prime}. ∎

\thref

SubjectReduction says that if a well-typed component KK performs a computation step to KK^{\prime}, then its type TT can also evolve to type TT^{\prime} which is the type of component KK^{\prime}. Moreover, the theorem ensures that if KK carries out an input or an output of a value vv, type TT performs the corresponding action at the level of types. \threfSubjectReduction thus attests that well-typed components always evolve to well-typed components, and furthermore that any component evolution can be described by an evolution in the types.

The progress result does not describe a strong correspondence like for Subject Reduction since we need to abstract from internal computations in components. For that reason, in the Progress statement we rely on Kλ(v)KK\xRightarrow{\textit{$\lambda$(v)}}K^{\prime} to denote a sequence of transitions K𝜏K′′λ(v)K′′′𝜏KK\xrightarrow{\tau}{\cdots}K^{\prime\prime}\xrightarrow{\lambda(v)}K^{\prime\prime\prime}\xrightarrow{\tau}{\cdots}K^{\prime}, i.e, that component KK may perform a sequence of internal moves, then an I/O action, after which another sequence of internal moves leading to KK^{\prime}.

Theorem 4.2 (Progress).
\thlabel

Progress If KTK\Downarrow T and Tλ(b)TT\xrightarrow{\textit{$\lambda(b)$}}T^{\prime} and λ(b)τ\lambda(b)\neq\tau then bb is the type of a value vv and Kλ(v)KK\xRightarrow{\textit{$\lambda$(v)}}K^{\prime} and KTK^{\prime}\Downarrow T^{\prime}.

Proof.

By induction on the structure of KK. ∎

\thref

Progress says that if type TT of component KK can evolve by exhibiting an I/O action to type TT^{\prime}, then KK can eventually (up to carrying out some internal computations) exhibit a corresponding action leading to KK^{\prime}, and where KK^{\prime} has type TT^{\prime}. \threfProgress thus ensures that the behaviours of types can eventually be carried out by the respective components, which entails components are not stuck and allows, together with \threfSubjectReduction, to attest that types faithfully capture component behaviour. Intuitively, our types can be seen as promises of behaviour in the sense that whatever they prescribe as possible behaviours, the components will eventually deliver. For the sake of addressing any possible component configuration, in particular when components have already all the dependencies (internally) satisfied in order to provide some behaviour, it is crucial that types capture the number of (internally) available resources.

5 Concluding Remarks

In this paper we introduce a type language for the choice-free subset of the GC language [6] that characterises the reactive behaviour of components and allows to capture “what components can do”. In particular, our types describe the ability of components to receive and send values, while tracking different kinds of dependencies (per each value and initial ones) and specifying constraints on the boundary of the number of values that a component can emit. We show how types of components can be extracted (inferred) and prove that types faithfully capture component behaviour by means of Subject Reduction and Progress theorems. Typing descriptions such as ours are crucial to promote component reusability, since to use a component we should only need to analyse its type and not its implementation (like in [6]). For instance, for the sake of ensuring the behaviour of a component is compatible with a governing communication protocol, where such compatibility is attested in our case by the conformance of the type to the (local) protocol.

We place our approach in the behavioural types setting (cf. [11]) since our types evolve in order to explain component behaviour (cf. \threfSubjectReduction), in contrast with classic subject reduction results where the type is preserved. In the realm of behavioural types, we distinguish Multiparty Asynchronous Session Types [10] which actually lay the basis for the protocol language of our target model [6]. The model builds on the idea that protocols can be used to directly program the interaction, and not only serve as a specification/verification mechanism, following the approach of choreographic programming [5, 17].

We discuss some closely related work, starting by Open Multiparty Sessions [4] which to some extent shares the same goals and the same background (cf. [10]). The approach in [4] targets the composition of protocols by considering that one of the participants can actually be instantiated by an external environment. Two protocols can then be connected if there is a participant in each that can serve as the interface to the other interaction. So protocols can be viewed as the units of composition instead of components like in our case, and reusing such protocols in other compositions requires compatibility between the I/O actions which are prescribed for the interfacing role. The main difference is therefore that we consider components that are potentially more reusable considering the I/O flexibility provided the reactive flavour.

We also identify the CHOReVOLUTION [12]project where the assembly of services via a choreography is addressed. The I/O flexibility is provided by adapters at assembly time that can solve I/O interface mismatches between service and choreography. We remark that the CHOReVOLUTION approach is at a very mature state (including tool support [3]), where however an assembly of services cannot be provided as a unit of reuse (like our composite components). Differently, our type-based approach aims at abstracting from the implementation and provides more general support for component substitution and reuse. Similar to our work, the model of service based architectures (SOA’s) [14] exposes components that exchange services between each other via ports. However, the authors do not present the type language, where some ideas we presented for extracting a type of a component could be used for the model they present.

We may also find the notion of distributed components is the Signal Calculus (SC) [7], where in each component a process is located. The type-based approach presented in [8] addresses the issue of ensuring SC local processes are composed and interact in a way such that they follow a well-defined protocol of interaction. Our model embeds the protocol in the operational semantics so such coordination is ensured by construction. Our types focus on a different purpose of ensuring data dependencies are met in order to ensure components are not stuck in the sense of the progress result.

In the work about Interactive verification of ADPs  [15] the authors introduce a notion of component type which characterizes components with a certain behaviour. One core difference between [15] and our work is that the authors do not provide any means to automatically extract types from given components.

We believe the ideas reported in this paper can contribute to the theoretical basis for providing support for component-based development in distributed systems. Immediate directions for future work include the support for protocols with branching, and providing a characterisation of the substitution principle [13] based in our types. Further challenges remain at the level of conveying the theoretical model to concrete applications, in particular regarding component deployment and the support for their persistent reuse.

Acknolegements

We thank the anonymous reviewers for their suggestions and comments which helped us to improve the paper.

References

  • [1]
  • [2] Amazon Web Services, Inc (2019): AWS Lambda: Developer Guide. Available at https://docs.aws.amazon.com/en_pv/lambda/latest/dg/lambda-dg.pdf#welcome.
  • [3] Marco Autili, Amleto Di Salle, Francesco Gallo, Claudio Pompilio & Massimo Tivoli (2019): CHOReVOLUTION: Automating the Realization of Highly–Collaborative Distributed Applications. In Hanne Riis Nielson & Emilio Tuosto, editors: 21th International Conference on Coordination Languages and Models (COORDINATION), Coordination Models and Languages LNCS-11533, Springer International Publishing, pp. 92–108, 10.1007/978-3-030-22397-7_6. Part 2: Tools (1).
  • [4] Franco Barbanera & Mariangiola Dezani-Ciancaglini (2019): Open Multiparty Sessions. In Massimo Bartoletti, Ludovic Henrio, Anastasia Mavridou & Alceste Scalas, editors: Proceedings 12th Interaction and Concurrency Experience, ICE 2019, Copenhagen, Denmark, 20-21 June 2019, EPTCS 304, pp. 77–96, 10.4204/EPTCS.304.6.
  • [5] Marco Carbone & Fabrizio Montesi (2013): Deadlock-freedom-by-design: multiparty asynchronous global programming. In Roberto Giacobazzi & Radhia Cousot, editors: The 40th Annual ACM SIGPLAN-SIGACT Symposium on Principles of Programming Languages, POPL ’13, Rome, Italy - January 23 - 25, 2013, ACM, pp. 263–274, 10.1145/2429069.2429101.
  • [6] Marco Carbone, Fabrizio Montesi & Hugo Torres Vieira (2018): Choreographies for Reactive Programming. CoRR abs/1801.08107. Available at http://arxiv.org/abs/1801.08107.
  • [7] Gian Luigi Ferrari, Roberto Guanciale & Daniele Strollo (2006): JSCL: A Middleware for Service Coordination. In Elie Najm, Jean-François Pradat-Peyre & Véronique Donzeau-Gouge, editors: Formal Techniques for Networked and Distributed Systems - FORTE 2006, 26th IFIP WG 6.1 International Conference, Paris, France, September 26-29, 2006, Lecture Notes in Computer Science 4229, Springer, pp. 46–60, 10.1007/11888116_4.
  • [8] GianLuigi Ferrari, Roberto Guanciale, Daniele Strollo & Emilio Tuosto (2007): Coordination Via Types in an Event-Based Framework. In John Derrick & Jüri Vain, editors: Formal Techniques for Networked and Distributed Systems - FORTE 2007, 27th IFIP WG 6.1 International Conference, Tallinn, Estonia, June 27-29, 2007, Proceedings, Lecture Notes in Computer Science 4574, Springer, pp. 66–80, 10.1007/978-3-540-73196-2_5.
  • [9] Nicola Dragoniand Saverio Giallorenzo, Alberto Lluch-Lafuente, Manuel Mazzara, Fabrizio Montesi, Ruslan Mustafin & Larisa Safina (2017): Microservices: Yesterday, today, and tomorrow. In Manuel Mazzara & Bertrand Meyer, editors: Present and Ulterior Software Engineering, Springer, pp. 195––216, 10.1007/978-3-319-67425-412.
  • [10] Kohei Honda, Nobuko Yoshida & Marco Carbone (2016): Multiparty Asynchronous Session Types. J. ACM 63(1), pp. 9:1–9:67, 10.1145/2827695.
  • [11] Hans Hüttel, Ivan Lanese, Vasco T. Vasconcelos, Luís Caires, Marco Carbone, Pierre-Malo Deniélou, Dimitris Mostrous, Luca Padovani, António Ravara, Emilio Tuosto, Hugo Torres Vieira & Gianluigi Zavattaro (2016): Foundations of Session Types and Behavioural Contracts. ACM Comput. Surv. 49(1), pp. 3:1–3:36, 10.1145/2873052.
  • [12] S. Keller, M. Autili & M. Tivoli (2014): CHOReVOLUTION project. http://www.chorevolution.eu.
  • [13] Barbara Liskov & Jeannette M. Wing (1994): A Behavioral Notion of Subtyping. ACM Trans. Program. Lang. Syst. 16(6), pp. 1811–1841, 10.1145/197320.197383.
  • [14] A. Malkis & D. Marmsoler (2015): A Model of Service-Oriented Architectures. In: 2015 IX Brazilian Symposium on Components, Architectures and Reuse Software (SBCARS), IEEE Computer Society, Los Alamitos, CA, USA, pp. 110–119, 10.1109/SBCARS.2015.22.
  • [15] Diego Marmsoler & Habtom Kashay Gidey (2019): Interactive verification of architectural design patterns in FACTum. Formal Aspects of Computing 31(5), pp. 541–610, 10.1007/s00165-019-00488-x. Alessandra Russo, Andy Schuerr, and Heike Wehrheim.
  • [16] M. Douglas Mcllroy (1969): Mass Produced Software Components. In: Software Engineering: Report of a conference sponsored by the NATO Science Committee, Garmisch, pp. 138–155.
  • [17] Fabrizio Montesi (2013): Choreographic Programming. Ph.D. thesis, IT University of Copenhagen. Available at http://fabriziomontesi.com/files/choreographic_programming.pdf.
  • [18] Object Management Group, Inc. (OMG) (2014): Business Process Model and Notation, specification version 2.0.2. Available at https://www.omg.org/spec/BPMN/2.0.2/.
  • [19] W3C WS-CDL Working Group (2004): Web Services Choreography Description Language Version 1.0. Available at http://www.w3.org/TR/2004/WD-ws-cdl-10-20040427/.

Appendix A Modified type 𝒯\mathcal{T}

Now we introduce the modified type denoted by 𝒯\mathcal{T}. The interfacing component of the composite one, beside its interaction with other components, also interacts with an external environment. In this case the crucial part is that it is able to receive in any moment values that are input externally. For the purpose of observing if a type of a component can perform actions required by the protocol, we need to modify the type according to the possible inputs that a (interfacing) component can receive from the external context without any constraints. The modified type of a type TT, taking into account the list of corresponding list of forwarders, is denoted by 𝒯(F,T)\mathcal{T}(F,T). If TT is the type of the interfacing component, each dependency on the external input ports is per each value dependency and the number of values available is unbounded (assuming that whenever the value is available it is received on the external input ports). The syntax of 𝒯\mathcal{T}-type is given in the Table 6. It is similar to a syntax of the types which we have already shown, with the difference in the number of values received, that in the modified type can be unbounded (infinite). Moreover, the rules defining the semantics of modified type are the same as the ones shown for our typing language (Table 7).

𝒯\mathcal{T}-Type syntax

Types Constraints Dependencies
𝒯=Δ<X_b;𝒞>\mathcal{T}\overset{\Delta}{=}<X_{\_}b;\mathcal{C}>
X_b=Δ{x_1(b_1),,x_k(b_k)}X_{\_}b\overset{\Delta}{=}\{x_{\_}1(b_{\_}1),\dots,x_{\_}k(b_{\_}k)\} 𝒞=Δ{y_1(b_1):B_1:[𝒟_1],,y_k(b_k):B_k:[𝒟_k]}\mathcal{C}\overset{\Delta}{=}\{y_{\_}1(b_{\_}1):\textbf{B}_{\_}1:[\mathcal{D}_{\_}1],\dots,y_{\_}k(b_{\_}k):\textbf{B}_{\_}k:[\mathcal{D}_{\_}k]\} 𝒟=Δ{x_1:_1,,x_k:_k}\mathcal{D}\overset{\Delta}{=}\{x_{\_}1\!:\!\mathcal{M}_{\_}1,\dots,x_{\_}k\!:\!\mathcal{M}_{\_}k\}
Kinds of Dependencies Boundaries
::=𝒩|Ω\mathcal{M}::=\mathcal{N}\ |\ \Omega B::=N|\textbf{B}::=N\ |\ \infty k0;N_0k\geq 0;N\in\mathbb{N}_{\_}0
𝒩::=N|\mathcal{N}::=N\ |\ \infty
Table 6: 𝒯\mathcal{T}-Type syntax

𝒯\mathcal{T}-Type semantics

xdom[𝒟][𝒯1]y(b):B:[𝒟]x?y(b):B:[𝒟]y(b):\textbf{B}:[\mathcal{D}]\xrightarrow{\text{$x?$}}y(b):\textbf{B}:[\mathcal{D}]x\notin dom[\mathcal{D}] [𝒯2]y(b):B:[{x:Ω}𝒟]x?y(b):B:[𝒟]y(b^{\prime}):\textbf{B}:[\{x:\Omega\}\uplus\mathcal{D}]\xrightarrow{\text{$x?$}}y(b^{\prime}):\textbf{B}:[\mathcal{D}] [𝒯3]y(b):B:[{x:𝒩}𝒟]x?y(b):B:[{x:𝒩+1}𝒟]y(b^{\prime}):\textbf{B}:[\{x\!:\!\mathcal{N}\}\uplus\mathcal{D}]\xrightarrow{\text{$x?$}}y(b^{\prime}):\textbf{B}:[\{x\!:\!\mathcal{N}+1\}\uplus\mathcal{D}] [𝒯4]𝒯𝜏𝒯\mathcal{T}\xrightarrow{\text{$\tau$}}\mathcal{T} i1,2,,ky_i(b_i):B_i:[𝒟_i]x?y_i(b_i):B_i:[𝒟_i][𝒯5]<{x(bx)X_b};{y_i(b_i):B_i:[𝒟_i]|i1,,k}>x?(bx)<{x(bx)X_b};{y_i(b_i):B_i:[𝒟_i]|i1,,k}><\{x(b^{x})\uplus X_{\_}b\};\{y_{\_}i(b_{\_}i):\textbf{B}_{\_}i:[\mathcal{D}_{\_}i]|i\in 1,\dots,k\}>\xrightarrow{\text{$x?(b^{x})$}}<\{x(b^{x})\uplus X_{\_}b\};\{y_{\_}i(b_{\_}i):\textbf{B}_{\_}i:[\mathcal{D}^{\prime}_{\_}i]|i\in 1,\dots,k\}>\lx@proof@logical@and\forall i\in 1,2,\dots,ky_{\_}i(b_{\_}i):\textbf{B}_{\_}i:[\mathcal{D}_{\_}i]\xrightarrow{\text{x?}}y_{\_}i(b_{\_}i):\textbf{B}_{\_}i:[\mathcal{D}_{\_}i^{\prime}] B>0𝒩_i1[𝒯6]<X_b;{y(by):B:[{x_i:𝒩_i|i1,,k}]}𝒞>y!(by)<X_b;{y(by):B1:[{x_i:𝒩_i1|i1,,k}]}𝒞><X_{\_}b;\{y(b^{y}):\textbf{B}:[\{x_{\_}i\!:\!\mathcal{N}_{\_}i|i\in 1,\dots,k\}]\}\uplus\mathcal{C}>\xrightarrow{\text{$y!(b^{y})$}}<X_{\_}b;\{y(b^{y}):\textbf{B}-1:[\{x_{\_}i\!:\!\mathcal{N}_{\_}i-1|i\in 1,\dots,k\}]\}\uplus\mathcal{C}>\lx@proof@logical@and\textbf{B}>0\mathcal{N}_{\_}i\geq 1

Table 7: 𝒯\mathcal{T} Semantics
Definition A.1.
\thlabel

defmod If T_r=<X_b;C>T_{\_}r=<X_{\_}b;\textbf{C}> is a type of interfacing subcomponent K¯\overline{K} of composite component [x~>y~]{G;r=K¯,R;D;r[F]}[\tilde{x}>\tilde{y}]\{G;r=\overline{K},R;D;r[F]\} then 𝒯(F,T_r)\mathcal{T}(F,T_{\_}r) is the T_rT_{\_}r-modified type where:

𝒯(F,<X_b,C>)\mathcal{T}(F,<X_{\_}b,\textbf{C}>) \triangleq <X_b;𝒯(F,C)><X_{\_}b;\mathcal{T}(F,\textbf{C})>
𝒯(F,{y(b):B:[D]}C)\mathcal{T}(F,\{y(b):\textbf{B}:[\textbf{D}]\}\uplus\textbf{C}) \triangleq 𝒯(F,{y(b):B:[D]})𝒯(F,C)\mathcal{T}(F,\{y(b):\textbf{B}:[\textbf{D}]\})\uplus\mathcal{T}(F,\textbf{C})
𝒯(F,{y(b):B:[D]})\mathcal{T}(F,\{y(b):\textbf{B}:[\textbf{D}]\}) \triangleq {y(b):B:[𝒯(D)]}\{y(b):\textbf{B}:[\mathcal{T}(\textbf{D})]\}
𝒯(F,{x:M}D)\mathcal{T}(F,\{x\!:\!M\}\uplus\textbf{D}) \triangleq 𝒯(F,{x:M})𝒯(D)\mathcal{T}(F,\{x\!:\!M\})\uplus\mathcal{T}(\textbf{D}) where M{N,Ω}M\in\{N,\Omega\}
𝒯(F,x:M)\mathcal{T}(F,x\!:\!M) \triangleq {x:M}\{x\!:\!M\} if xFix\notin F^{i}, where M{N,Ω}M\in\{N,\Omega\}
𝒯(F,x:M)\mathcal{T}(F,x\!:\!M) \triangleq {x:}\{x\!:\!\infty\} if xFix\in F^{i}
𝒯(F,x:Ω)\mathcal{T}(F,x:\Omega) \triangleq \emptyset if xFix\in F^{i}

Note that for K=[x~>y~]{G,r_1=K_1,r_2=K_2,,r_n=K_n;D;r_1[F]}K=[\tilde{x}>\tilde{y}]\{G,r_{\_}1=K_{\_}1,r_{\_}2=K_{\_}2,\dots,r_{\_}n=K_{\_}n;D;r_{\_}1[F]\} we have that

𝒯(F,T_r_2)=T_r_2,,𝒯(F,T_r_k)=T_r_k\mathcal{T}(F,T_{\_}{r_{\_}2})=T_{\_}{r_{\_}2},\dots,\mathcal{T}(F,T_{\_}{r_{\_}k})=T_{\_}{r_{\_}k}

since the only component that forwards the values from/to external environment is component K_1K_{\_}1.

Appendix B Conformance relation

𝒯x?(b)𝒯Γ𝒯LP[InpConf]Γ𝒯x?:b.LP\Gamma\vdash\mathcal{T}\bowtie x?\!:\!b.LP\mathcal{T}\xrightarrow{\text{$x?(b)$}}\mathcal{T}^{\prime}\Gamma\vdash\mathcal{T}^{\prime}\bowtie LP      𝒯y!(b)𝒯Γ𝒯LP[OutConf]Γ𝒯y!:b.LP\Gamma\vdash\mathcal{T}\bowtie y!\!:\!b.LP\lx@proof@logical@and\mathcal{T}\xrightarrow{\text{$y!(b)$}}\mathcal{T}^{\prime}\Gamma\vdash\mathcal{T}^{\prime}\bowtie LP
[EndConf]Γ𝒯𝐞𝐧𝐝\Gamma\vdash\mathcal{T}\bowtie\mathbf{end} 𝒯𝒯[VarConf]Γ,X:𝒯𝒯X\Gamma,X:\mathcal{T}^{\prime}\vdash\mathcal{T}\bowtie X\mathcal{T}^{\prime}\leq\mathcal{T} Γ,X:𝒯𝒯LP[RecConf]Γ𝒯recX.LP\Gamma\vdash\mathcal{T}\bowtie recX.LP\Gamma,X:\mathcal{T}\vdash\mathcal{T}\bowtie LP
Table 8: Conformance
Definition B.1.
\thlabel

modevolve 𝒯𝒯\mathcal{T}^{\prime}\leq\mathcal{T} if exists a (possibly empty) set of typed input ports {x_1(b_1),x_2(b_2),,x_k(b_k)}\{x_{\_}1(b_{\_}1),x_{\_}2(b_{\_}2),\dots,x_{\_}k(b_{\_}k)\} such that 𝒯x_1?(b_1)x_k?(b_k)𝒯\mathcal{T}^{\prime}\xrightarrow{\text{$x_{\_}1?(b_{\_}1)$}}\cdots\xrightarrow{\text{$x_{\_}k?(b_{\_}k)$}}\mathcal{T}.

Rule [InpConfInpConf] ensures that a modified type 𝒯\mathcal{T} is conformant with the protocol, where it can receive an input of a matching type with a continuation as a protocol LPLP, if a modified type can receive a value on port xx, and assuming that port xx receives a values of type bb and the evolved type is conformant with LPLP. Similar reasoning is for an output. Rule [EndConfEndConf] states that a modified type is always conformant with the termination protocol. Finally, we have two rules [VarConfVarConf] and [RecConfRecConf] for the recursion. The premise of Rule [VarConfVarConf] requires that the type associated with the recursion variable by assumption and the type under consideration are related as 𝒯𝒯\mathcal{T}^{\prime}\leq\mathcal{T} (\threfmodevolve). Observing the semantics of modified types, the possible difference between types 𝒯\mathcal{T}^{\prime} and 𝒯\mathcal{T} is that some initial dependencies might be dropped or that the number of values available on some input ports for some outputs might increase. Rule [RecConfRecConf] states that 𝒯\mathcal{T} is conformant with a protocol rec𝐗.LPrec\mathbf{X}.LP, provided that the type is conformant with the body of the recursion under the environment extended with assumption 𝐗:𝒯\mathbf{X}:\mathcal{T}.