{'fraction_non_alphanumeric': 0.02901675425594035, 'fraction_numerical': 0.008174998877060594, 'mean_word_length': 5.026935571196535, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 6, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 3, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
Advanced Join Patterns for the Actor Model based on CEP Techniques

Context: Actor-based programming languages offer many essential features for developing modern distributed reactive systems. These systems exploit the actor model's isolation property to fulfill their performance and scalability demands. Unfortunately, the reliance of the model on isolation as its most fundamental property requires programmers to express complex interaction patterns between their actors to be expressed manually in terms of complex combinations of messages sent between the isolated actors. Inquiry: In the last three decades, several language design proposals have been introduced to reduce the complexity that emerges from describing said interaction and coordination of actors. We argue that none of these proposals is satisfactory in order to express the many complex interaction patterns between actors found in modern reactive distributed systems. Approach: We describe seven smart home automation scenarios (in which an actor represents every smart home appliance) to motivate the support by actor languages for five radically different types of message synchronization patterns, which are lacking in modern distributed actor-based languages. Fortunately, these five types of synchronisation patterns have been studied extensively by the Complex Event Processing (CEP) community. Our paper describes how such CEP patterns are elegantly added to an actor-based programming language. Knowledge: Based on our findings, we propose an extension of the single-message matching paradigm of contemporary actor-based languages in order to support a multiple-message matching way of thinking in the same way as proposed by CEP languages. Our proposal thus enriches the actor-model by ways of declaratively describing complex message combinations to which an actor can respond. Grounding: We base the problem-statement of the paper on an online poll in the home automation community that has motivated the real need for the CEP-based synchronisation operators between actors proposed in the paper. Furthermore, we implemented a DSL -- called Sparrow -- that supports said operators and we argue quantitatively (in terms of LOC and in terms of a reduction of the concerns that have to be handled by programmers) that the DSL outperforms existing approaches. Importance: This work aims to provide a set of synchronization operators that help actor-based languages to handle the complex interaction required by modern reactive distributed systems. To the best of our knowledge, our proposal is the first one to add advanced CEP synchronization operators to the -- relatively simplistic single-message based matching -- mechanisms of most actor-based languages.


Introduction
The actor model [25] is a well-established paradigm for developing distributed reactive systems. The actor model's concurrency properties are fundamental to achieve the kind of performance and scalability required by such systems in the face of having to handle millions of simultaneous connections [10]. In the original actor model, interaction and coordination of actors are achieved entirely by cleverly encoded asynchronous message exchanges between those actors. Nevertheless, whenever an actor is supposed to execute a method in response to a received set of messages (rather than just a single message) with certain characteristics, mainstream actor languages (e.g., Erlang, Scala, Elixir) require developers to encode the characteristics of the set manually. This is illustrated in listing 1 (lines 4,7,9). In this example, the actor will react only if a particular sequence of messages (MsgA → M s g B → M s g C) is received. To delay the execution of the reaction code (line 11) until the expected messages have been received in the right order, the actor must manually keep track (lines 5,8,13) of previous messages, and validate the progress of subsequent message arrivals in a hard-coded fashion (line 10).
Briefly, the single-message match mechanism of traditional actors complicates the construction of said distributed reactive systems. Developers are forced to manually weave and braid two orthogonal concerns of their actor's interactions and coordinations: when to react (i.e., precisely describe the set of messages that is supposed to give rise to a certain behaviour) and how to react (i.e., the code that describes the actual method to be fired upon reception of said set). For more than three decades, researchers have been developing new programming language features to improve the expressiveness of the interaction and coordination features of actors. In section 5 we will present an extensive overview of the state-ofthe-art in this domain. One notable technique is based on join patterns. This technique was popularised by Haller and Van Cutsem [23], where an extension of Scala is presented featuring join patterns. Join patterns where invented by Benton, Cardelli, and Fournet [9] as part of the join calculus. They were added to the thread-based concurrency model of C# leading to a language called Polyphonic C#. The Scala joins patterns of [23] can be seen as an attempt to transpose the mainly synchronous incarnation of join patterns in Polyphonic C# to the asynchronous world of Scala.
Join patterns allow many interaction and coordination patterns to be expressed very elegantly. For example, line 2 in listing 2 exemplifies a join pattern that expresses the coordination between the two methods (Get and Put) of a Buffer class. In this example, the calling thread of the Get method will be blocked until the asynchronous Put method is invoked. The ampersand (&) symbol expresses declaratively that both threads need to rendezvous before the method's body is executed. In this paper, we use seven smart home automations (in which every home appliance is represented by an actor) to motivate the necessity to easily encode very complex interactions and coordinations between actors. Based on an extensive poll in the smart home community on the internet, we show that these are seven real-world concerns that are on the radar of actual developers of such systems. From these seven automations, we identify five different types of message synchronisation operators. Then, we show that the current state-of-the-art in actor coordination and interaction technologies in general, and join patterns in particular falls short in supporting these operators.
Based on this problem statement, we present Sparrow, a dialect of Elixir 1 that features actors whose complex interaction and coordination patterns can be described in a highly declarative fashion. Sparrow's interaction patterns (which support our identified synchronisation operators) have been harvested from our extensive literature study of the state-of-the-art of Complex Event Processing (CEP). Hence, Sparrow can be seen as an actor language whose actors have been enriched with CEP and join pattern ideas. We use a smart home scenario as an application domain to help steer our research. However, we conjecture Sparrow as a general-purpose actor coordination framework that can be used to express synchronisation patterns for other event-driven domains (e.g., SmartCities [26], SmartBuildings [43]) as well.
The main contributions of this paper are: We showcase current challenges for modern actor languages to coordinate large groups of heterogeneous actors using seven smart home scenarios (see section 2). These use cases are not just synthetic scenarios but have been validated in the smart home community.
We present a novel set of advanced actor synchronisation abstractions by formulating them as a domain-specific Elixir extension, called Sparrow (see section 3). We validate our proposal by implementing the seven smart home scenarios to demonstrate the expressiveness of the resulting code (see section 4). We show the novelty of our approach by contrasting it with the state-of-the-art and related work on actor interaction and coordination techniques (see section 5).

Motivating Scenarios: Coordination of Smart Home Devices
One application domain where complex interactions between different participants of a system occur is the smart home automation domain. In our scenario, each smart device is digitally represented by an actor (also called digital twin [2]). These digital twins can be run on the device itself or any other machine, for instance, on the edge (e.g., raspberry pi) or a cloud-based platform (e.g., SmartThings). The complex interactions between the smart home devices then need to be encoded through message passing between their respective digital twins. For complex scenarios, this will require advanced synchronisation patterns to express these different interactions.
In this section, we present seven home automation scenarios that exemplify five fundamental synchronisation operators required to coordinate a group of actors.  consolidated scenarios highlight different complex coordination patterns between a group of smart devices, which can be abstracted into five primary synchronization operators described at the end of this section. To validate that each of our scenarios is a representative example of the different synchronisation patterns found in the wild, we published an online poll. Each respondent was required to reply with a yes-or-no answer whether or not they had to implement synchronisation patterns similar to each of our seven scenarios. Our poll was published in four smart home community forums (openHAB [7], Hass [5], SmartThings [8], and Hubitat [6]) for one month and, during that time, collected 714 votes. Figure 1 shows the results of this poll. Due to our poll's nonprobabilistic nature, we cannot derive any statistical certainties based on its results. However, it does show that our scenarios are good examples of concerns that present themselves within that community. For example, scenario 1 exemplifies the need for synchronizing multiple independent messages. In this scenario, there are two sensors (motion and light) whose messages need to be synchronized. This concern was identified and raised as a known issue by 662 (92.72 %) of the voters. Scenarios 2, 3, 4, and 5 rely on fairly advanced time-based synchronisation of messages. Furthermore, scenario 5 requires to enforce a particular order in which its constituent messages must match. Our poll shows that timing-constraints and message-order are also two well-recognized concerns within the home automation community. Even the complex scenarios (6 and 7) that require aggregation of multiple messages of the same type over a certain time window were still encountered by more than a quarter of the respondents. 6 The results of our poll do not validate our list of scenarios as exhaustive. However, it does give us the certainty that the listed scenarios represent common synchronisation patterns found in the wild. Despite these results, we found that both current threadbased smart home platforms and actor languages lack synchronisation abstractions to coordinate heterogeneous smart home devices. We base this statement on our experience after implementing all our examples in two popular smart home platforms (openHAB and Hass) and a modern actor language (Elixir). These selected programming environments offer at least the same synchronisation abstractions as their state-of-the-art alternatives (e.g., SmartThings, Akka/Scala). The full implementation of each of the seven scenarios using each of the three programming environments can be found online [4]. Each of our implementations using both openHab and Hass was presented and improved based on feedback from their respective communities.
Listing 3 shows the degree of complexity that users of current smart home platforms have to handle by themselves to implement scenario 5. Our openHAB solution considers two motion sensors, one in the entrance hall (α), and the second one outside the front door (δ). Furthermore, we use a contact sensor (β) to detect when the front door was opened. The occupied-home scene is enabled by the following sequence of messages δ → β → α, and the empty-home scene by α → β → δ. Both scenes will be activated if the three messages occur in a time window of 60 seconds. This implementation also assumes that only one person lives in the smart home. Although our solution was improved thanks to the feedback received from the openHAB community, it exposes a lack of programming abstractions to synchronize messages from multiple devices. First, we are responsible for keeping track of each sensor's last update (β line 9, α line 15, δ line 28). Second, we manually discard messages older than the 60 seconds time window (lines 17,30). Finally, we have to verify that the messages were received in the right order (lines 20, 33) before executing the automation's reaction. In summary, our openHAB solution and its Elixir alternative required more than 20 lines of essential code to express the coordination of only three devices.
Further study of each of the scenarios allowed us to disambiguate five categories of operators that are required to express each of the synchronisation patterns. Selection operators are required to select messages from the set of matching messages after filtering. Traditional actor languages only allow for the consumption of the oldest (first in) message. However, we will require more flexible message selection policies. For example, in scenario 7, we are only interested in the latest Internal Failure message, and the third Floor Heating Failure message. In general, expressing these types of constraints will require selection operators that allow us to select any message from the set of matching messages after filtering.
Correlation operators are required to match a set of different types of messages and to unify their attributes. For example, in scenario 1, we are required to match a motion sensor message and an ambient light sensor message but only match if both are present. Additionally, in scenario 5, we are also required to specify the order in which those messages arrived in order to trigger the right scene. Depending on the order in which sensors detected movement, the person either left the house or arrived home.  Advanced Join Patterns for the Actor Model based on CEP Techniques Accumulation operators are required to aggregate a set of matching messages after filtering. For example, in scenario 6, we are required to aggregate electricity consumption messages of the last three weeks into their sum.
Transformation operators are required to transform (e.g., map) a list of accumulated messages. These messages can be subject to new predicate conditions. For example, in scenario 6, we need to check if the total electricity consumption is greater than 200 kWh before sending a notification. Only if the predicate condition is true all the accumulated messages are consumed.
In this section, we have identified the above five types of operators that are required to express complex synchronisation patterns. Unfortunately, as we show in more detail in section 5, state-of-the-art actor languages and frameworks lack the necessary support for these types of operators. Contrary, the above operators are frequently supported by CEP languages/frameworks (e.g., TESLA [11], ETALIS [3], PARTE [37], FlinkCEP [47], and Esper [27]). In the next section, we explore the integration of the well understood CEP operators into actors to overcome these limitations.

Advanced Join Patterns
In this section, we introduce the main contribution of this paper, to wit a novel join pattern language called Sparrow. Like previous join pattern languages [23,32,33], Sparrow extends the traditional single-message match interface of actors [25] to support multiple-message match. However, in order to support the five synchronisation operators described in the previous section, Sparrow's join pattern language design is heavily inspired by CEP languages and frameworks. Sparrow leverages the macro facilities of the Elixir programming language for its implementation. As such, Sparrow is implemented as a domain-specific language (DSL) that benefits from the integration of the extensive set of Elixir/Erlang libraries in that ecosystem. The conceptual model underlying Sparrow's technical implementation was formalised into an executable operational semantics using PLT-Redex [14]. The description of this operational semantics can be found in a companion technical report [40] for this paper.
The Sparrow programming language can be subdivided into two smaller languages. On the one hand, there is the Sparrow's pattern language (see section 3.1); this is a declarative subset of Sparrow (inspired by CEP languages) that enables the declaration of complex synchronisation patterns (see listing 4, lines 4--14). On the other hand, the Sparrow's reaction language (see section 3.2) is a superset of the Elixir programming language in which the reaction to a matching pattern can be specified (see listing 4, lines 16,17). The react_to with: special form ties both languages together. It determines which reactions will be fired after a complex synchronisation  Figure 2 shows an EBNF-styled grammar for the Sparrow's pattern language. The definition for <expression>, <identifier> <value>, <number> and <boolean> have been omitted and corresponds to ordinary Elixir expressions, identifiers, primitive values, numbers and booleans respectively.

10:9
Advanced Join Patterns for the Actor Model based on CEP Techniques

Sparrow Pattern Language
Similarly to the Erlang/Elixir family of actor languages, in Sparrow, incoming messages are pattern matched against several message patterns. When a match is found, the matching message(s) are consumed from the actor's inbox, and the actor starts a process to react to those messages. This reaction is specified in the Sparrow reaction language (see section 3.2). Sparrow has support for three types of patterns: elementary patterns enable the matching of single messages, composite patterns enable the composition of multiple elementary patterns, and accumulation patterns enable the accumulation and aggregation of multiple messages of the same type.

Elementary Patterns
Elementary patterns are the most basic kind of patterns. They always start with a selector that designates a single message followed by an optional operator and an optional guard expression. Defining a new pattern in Sparrow can be done using the pattern special form. Figure 3 shows the definition of an elementary pattern that matches when an open window is detected at any location (this is part of the implementation for scenario 3). The pattern keyword is used to give a name to the pattern. In Sparrow, patterns are second-class citizens that can be reused and composed to define complex patterns. The as keyword is followed by the selector of the pattern. Similar to Elixir's messages, a selector in Sparrow is represented as a tuple. Its first element determines the type 7 of message to match, and always has a unique constant value (:window in our example). The other selector elements are called attributes, and they can be primitive values (e.g., String, Number) or logic variables. Logic variables represent a dynamic primitive value that is unknown until the matching of the pattern against a message that sits in the actor's inbox. Operators are high-level conditions that further delineate the kind of messages that can be matched by the pattern selector. A selector can optionally be followed by one or more operators enclosed in square brackets. Elementary patterns support three types of operators: negation, debouncing, and extensional sequencing. 8 For example, figure 4 shows an implementation for scenario 2 that uses a negation operator. For this scenario, we want to turn off the lights after two minutes without movement. Detecting the absence of motion events can be implemented in Sparrow by using the negation operator (not) in combination with a timing window. Negated selectors must always be combined with a time window (e.g., in seconds, minutes, or hours). Every time the selector matches a new message, the time window is reset. Once the time window expires, the pattern is automatically matched. Figure 5 shows an implementation of scenario 4 using the debouncing 9 operator. For this scenario, we only want to match doorbell messages if no other doorbell message was matched in the past 30 seconds. A debounced operator does exactly that, the first message that matches the preceding selector automatically matches the entire pattern. Any future messages that follow within the debouncing time are automatically discarded. Finally, figure 6 presents the use of the extensional sequencing operator in order to implement a partial solution for scenario 7. For our partial solution, we are interested in matching every third heating failure event. This can be done in Sparrow by following the pattern selector with an extensional sequencing operator using the every keyword. In our example, only every third message is matched and consumed. All other messages that match the selector are discarded.  Guards are boolean predicates that are executed after a match of the selector and its operators is found. Like with traditional Elixir guards [48], Sparrow guards are only allowed to contain boolean predicate expressions that can always be executed in constant time (and are side-effect free). Figure 7 shows a guarded pattern that will be activated if it matches an open window message from the bedroom or the kitchen. The full pattern is only matched (and the corresponding reaction fired) when the guard expression evaluates to true. If the guard expression evaluates to false, the pattern is not matched, and the messages are not consumed. Composing patterns can be done by linking multiple elementary patterns by means of a logic operator. Sparrow supports both conjunctions (and) and disjunctions (or) of patterns. 10 Figure 9 illustrates the use of composing patterns in order to partially implement smart home scenario 5. For this scenario, we are interested in detecting when the user is arriving home by first detecting motion at the front door, followed by receiving an open door event, followed by detecting motion in the entrance hall. In our 10 Like with traditional actor languages; disjunction can also be achieved by separating each of the patterns in the disjunction. However, as Sparrow also has conjunction, which is usually not supported by traditional actor languages, we also syntactically support disjunction.

10:12
example, the occupied_home pattern is defined as a conjunction of three elementary patterns. The first and third one reuse the motion_sensor pattern to match only motion events from the front door and entrance hall respectively, On the other hand, the second one is an anonymous pattern that matches door opening events. Any composed pattern can be followed by an optional set of operators. By default, a composed pattern does not enforce any particular order in which its constituents' patterns must match. However, that behaviour can be changed using the intensional sequencing operator (seq). In our example of figure 9, we set sequencing to true in order to specify that the occupied_home pattern can only be matched if the matched messages arrive in the same order as they are specified in the pattern definition. Renaming logic variables Sparrow has support for unification of logical variables crossing the constituents of a composed pattern. This is often desirable as it allows for the unification of various attributes across different pattern selectors. However, Sparrow currently unhygienically expands all named patterns into the composed pattern (similar to unhygienic macros). This can potentially lead to unexpected unification of logical variables when composing named patterns. For example, our first implementation of scenario 5 in figure 9 contains a bug as it incorrectly does not match the sequence of messages shown in figure 10.A. Figure 10.B shows the expanded form of the composite pattern defined in figure 9. Each of the selectors of the elementary patterns contains the same logical variable id. However, in this case, it is undesirable to unify these three logical variables as each sensor can have a different id. To circumvent this issue, Sparrow allows developers to manually change the logical variable for an attribute using the aliasing operator (~>). The aliasing operator renames the logical variable on its left-hand side to the logical variable on its right-hand side in the elementary pattern. This operator is not an optimal solution. In future versions of Sparrow, the default behaviour of unification will be changed to facilitate the maintenance of large pattern sets. However, as patterns are not first-class entities, developers can identify shared logic variables by looking at the pattern definition. Figure 10.C presents an improved version of the occupied_home pattern.
Timing constraints on composite patterns Similar to windowing for elementary patterns, composite patterns also support timing-constraints. Developers can specify a time-interval in which the composed set of patterns should be matched. Figure 11 presents a new version of the occupied_home pattern where the 60 seconds time constraint is added. Similar to intentional sequencing, this time constraint (interval) is also defined in the options of a composite pattern.

10:13
Advanced Join Patterns for the Actor Model based on CEP Techniques Composite patterns selection strategy Like most actor languages, Sparrow messages are matched in FIFO order. However, Sparrow enables one to deviate from that default selection strategy. Figure 6 already showed an example of this by only selecting every third message. This particular message selection strategy is useful to synchronize always on the latest messages that may be relevant to a pattern. For example, the implementation of the pattern occupied_home from figure 11 must always check all potential messages received from the three sensors in the last 60 seconds. However, as observed in figure 12.A at t 4 , the pattern should only check the latest message (t 3 ) from the entrance hall's motion sensor and discard the old ones (t 1 , t 2 ).

Accumulation Patterns
Accumulation patterns extend elementary patterns with quantified and unquantified accumulation of sets of the same type of message. Once the set of messages is accumulated, further transformation and filtering can be applied. Accumulation patterns can be constructed from elementary patterns by means of several optional accumulation 10:14 operators. Like other operators (see section 3.1.1), they can be specified between square brackets following the selector.
Quantified accumulation patterns are used to accumulate a fixed number of matching messages. Figure 13.A shows a pattern that uses the count operator to match three heating failure messages. However, similar to reused patterns, the pattern expansion step of Sparrow's macros will unhygienically expand these patterns. In our example, this means that the pattern heating_failure accumulates three heating failure messages from the same boiler and with the same failure code as the logical variables id and code will be unified (see figure 13.B). Although this default behaviour may be useful in some circumstances, it is not always desirable. Sparrow introduces the operators: must be distinct ! and may be distinct @, to specify the expected matching behaviour of logic variables when used in an accumulation pattern. The former guarantees that the constrained attribute must have a distinct value in all the messages accumulated. In contrast, the latter allows the constrained attribute to have any value. For example, the definition of heating_failure pattern shown in figure 13.C will match three messages from the same boiler regardless of the code attribute's value. Unquantified accumulation patterns are used to accumulate any number of messages within a certain time window using the window operator. Figure 14.A shows a pattern that uses the window operator to accumulates all heating failure messages from the last 60 minutes. Messages older than the time constraint are automatically removed by Sparrow's run-time. As a second example of unquantified accumulation patterns, figure 14.B shows a hybrid accumulation pattern that use both count and time-based accumulation operators. In this last case, the first operator that reaches its condition will win and result in a match.
Transformation operators allow developers to transform and filter a group of received messages that have been accumulated. For example, figure 15.B presents a pattern that implements the requirements for scenario 6. Notice that the electricity_alert pattern uses the may be distinct operator (B.

Sparrow Reaction Language
So far, we have only focused on the Sparrow's pattern language. However, once a pattern is matched, the matched messages are consumed, i.e., removed from the actor's inbox, and the actor starts reacting to the matched pattern. In Sparrow, this reaction logic is syntactically decoupled from the pattern definition (similar to [51]). A reaction can be dynamically bound to one or more patterns, and a pattern can have multiple reactions. This behaviour was intentionally designed to facilitate the reuse of both patterns and reactions. Furthermore, it avoids developers to add an extra layer of indirection (middleman) to determine which reactions to execute. The middleman approach circumvents the duplicity definition of both patterns and reactions. However, it also introduces a performance penalty since it is always notified whether a pattern has registered reactions or not. In summary, the decoupling of patterns and reactions allows the programmer to dynamically change the behaviour of a Sparrow actor, which is reminiscent of a become statement in the original actor model. Figure 16 showcases a smart home scenario where the binding of reactions to a pattern is based on the current season of the year (e.g., summer, winter). In this way, developers can avoid duplicated patterns with different reactions to match the season requirements. This figure omits the code related to the dynamic scheduling of the reactions. Figure 16.A shows the definition of two reactions (turn_off_heating, turn_off_cooling) using the reaction special form. Both reactions can be bound to the pattern window_open (see figure 16.B) based on the current season. The definition of a reaction is similar to the definition of a named function that always has the same 10:16 reaction turn_off_heating (mgs, int_res, state), do: # reaction code Binding a reaction to a pattern is done using the react_to special form (see figure 16.C) which expects two arguments: the pattern's name (C.1), and the reaction's name (C.2). When the pattern open_window (see figure 16.B) is successfully matched, the actor will invoke all its reactions in the same order they were bound to the pattern. A reaction can also be unbound from a pattern. The remove primitive function (see figure 16.D) also expects the same two arguments as the react_to primitive function but in reverse order. Finally, the remove_reactions function (see figure 16.E) removes all the reactions of the pattern received as argument.

Implementation
As mentioned in section 3, Sparrow extends the traditional single-message match interface of actors to support multiple-message match. To achieve that, a Sparrow actor has a virtual-inbox that it is used as a knowledge base of an embedded pattern engine. Like the traditional actor's inbox, the virtual-inbox is theoretically unbounded in size. However, unlike traditional actor languages, Sparrow messages have a finite lifetime. Each actor can define a default lifetime for the messages it receives. After a message expires, it is automatically garbage collected by the pattern engine.
The pattern engine builds a directed graph (also known as discrimination network) representing all patterns defined in its actor. Internally, Sparrow's patterns are represented by a special type of node called pattern node. These nodes implement the different synchronisations operators motivated in section 2 and supported by Sparrow. Additionally, each pattern node maintains a history (called buffer) of previous matched messages. Although in figure 17 patterns are represented by a single type of node,  Figure 17 shows a simplified view of the internal representation of an actor in Sparrow and its message matching process. The matching process starts by transferring each received message from the actor's inbox into the virtual-inbox. Later the pattern engine tries to match each new message against a group of patterns for which it is relevant; we call this process a match-cycle. The discrimination network's root node serves as the entry point of new messages for the matching process. This node will determine potential pattern nodes based on the message's type and will forward it to them. After this step, the message will flow through the discrimination network until a pattern node with a successful match of its conditions is found. In that case, the message is consumed by its reaction(s), which will be one after the other executed sequentially by the actor's reaction executor. Otherwise, the message remains in the node's buffer until a successful match is completed or until the message expires. The above process is an essential tool for the engine's incremental matching strategy. This strategy is based on a custom implementation of the RETE algorithm [15]. Furthermore, it implements a single pattern selection and selected message consumption policies [54]. The former guarantees that a pattern matches at most once per match-cycle. The latter guarantees that a pattern can consume a message only once. However, multiple patterns can consume the same message. The details of this implementation can be found in our GitHub repository. 11

Evaluation
In this section, we evaluate Sparrow's expressiveness by comparing its solutions to our seven scenarios against the ones expressed in two popular smart home platforms (openHab, Hass) and one actor language (Elixir). We chose Elixir because it is the host language of our DSL, and it provides synchronisation abstractions similar to other mainstream actor languages (e.g., Erlang, Scala). We followed a quantitative evaluation approach based on an analysis of the lines of code required to express each automation's coordination logic. In other words, our comparison does not consider code not related to the coordination process (e.g., imports, reaction logic of the automations). To obtain a fair comparison between our actor-based solutions and the ones from the smart home platforms, we published these solutions on both community's forums (see openHAB [39] and Hass [38] topics). This allowed us to get feedback, and incrementally arrive at a solution that could be implemented by experts in these communities in our comparison. Figure 18 shows a solution to scenario 5 in openHAB (A), Elixir (B), and Sparrow (C). We decided to analyse this particular solution since it showcases most of the actions that developers have to take care of during the implementation of our scenarios. Due to the similarity between the solutions of openHAB (Jython) and Hass (Python), we omitted the latter. However, the interested reader can inspect this and the rest of our solutions in our GitHub repository [4].
As can be observed in figure 18, for this particular automation, the Sparrow's solution is the most compact of all three. However, we want to compare the relative amount of code that we (as developers) have to write for handling all different concerns related to the synchronisation of messages. To do this analysis, we highlighted four concerns that we frequently found in the implementations of our smart home scenarios. The first one (state management) indicates the code that is used to save temporal data required by the ongoing coordination process. For example, in both openHAB and Elixir implementations, we had to manually track each sensor's most recent message, spending 42.86 % and 40 % respectively of the coordination code on that action. However, Sparrow shields developers from this kind of work. The second concern (windowing management) highlights the code needed to discard messages that do not satisfy the pattern's timing constraints. In contrast, the third one (sequencing control) points the code to enforce a particular message order. The coordination code required for these two actions in automation 5 was relatively simple. However, unlike Sparrow (lines 10, 13), the complexity of these synchronisation operations in openHAB (lines 21,24,33,36) and Elixir (lines 8,9,16,17) is directly proportional to the number of devices involved. The last concern, pattern definition emphasizes the code used to express the type of messages to be synchronized and their content-based conditions. Although it is the most crucial concern for the coordination process, openHAB and Elixir's patterns only define when to react to single messages. The inability to express the whole coordination process by means of declarative patterns forces developers to shift their focus from when to react to how to do it. By contrast, in Sparrow (see figure 18.C), a developer focuses on the declaration of patterns, and he lets the run-time to figure out how to match its constituents. 10 Figure 19 Summary of analyzing different solutions for the seven scenarios Figure 19 summarizes our solutions with a breakdown of the total lines of code (LoC) and their respective relative percentage according to the four identified concerns. For each concern and platform/language, we show the sum of all LoC written in our seven solutions. A detailed breakdown for each solution can be found in appendix A. To populate the values shown in this figure, we manually tagged each LoC related to the four concerns. Later, we retrieve their sum values using a helper script (statistics.exs) located in the root directory of each solution group (e.g., openHAB, Sparrow). Using this comparison, we show that Sparrow seems to succeed at reducing the effects of non-functional concerns that arise during the synchronisation of messages in a complex actor system.

Related Work
Our main goal in this paper was to retrofit CEP operators onto the actor model to improve the expressiveness of encoding the interaction and coordination of actors through join patterns. In this section, we discuss how the related work done in this domain relates to the synchronisation operators described in section 2. We group the below proposals in three main categories: communication model extension, monitor & verification, and local synchronisation. Due to the not mutually exclusive definition of our categories, some proposals may fit in more than one. Table 2 in appendix B summarizes the support for the five types of synchronisation operators by state-ofthe-art actor frameworks and languages. able to match individual messages. In interActors, the sender of a message can wait for two or more reply messages before continuing. On the other hand, AErlang has support for receiver-side synchronisation by allowing the receiver actor to accumulate and filter a finite number of messages of the same type. Our proposal takes this further by allowing the interface of an actor to synchronize different types of messages. Furthermore, our patterns extend the single message transformation of [28] to a list of messages. Contrary to Sparrow, none of the proposals in this category provide abstractions to detect the absence of messages, or enforce a matching order, or enable matching a disjunction of messages using a single pattern.
Monitor & verification Related work in this category [13,16,17,24,29,30,31,35,36,44,46] uses reflection to observe and limit the interaction of one or a group of actors. The coordination process is mostly done by a special type of meta-actors which enforces a particular protocol for incoming and outgoing messages. In contrast, our proposal is based on a local synchronization of messages in the receiver actor. Like Sparrow, synchronization abstractions of the above proposals can filter messages based on their values and time constraints (e.g., [29,35,46]). However, the abstractions provided by [24,31] only supports type-based constraints. Furthermore, only [35] can detect the absence of messages, but it assumes that actors have their local clocks synchronized, and their invocations are scheduled atomically. Additionally, like traditional actors, the synchronisation abstractions of these proposals always match the oldest messages. Contrary to Sparrow, message correlation operators are used only to enforce a particular communication protocol. The message receiver behaves like a traditional actor matching a single message.
Local synchronisation Proposals in this category can be further classified into two main groups. The first one targets synchronisation approaches based on promises/futures or message-passing continuations [18,34,49,50,53]. Unlike Sparrow, their abstractions allow the synchronisation and chaining of individual message invocations. The second group extends the actor interface to match a set of messages instead of individual ones. Sparrow, like the other proposals in this category [23,32,33] expands the matching capabilities of the traditional actor's receive primitive with join patterns [9]. Join pattern languages commonly only support the unification of individual messages, except Activators [18] and Sparrow, where patterns can define disjunctions. However, Activators' patterns cannot mix conjunctions and disjunctions of messages in a single pattern like in Sparrow. Furthermore, unlike Sparrow, joins in none of these languages enforce a particular matching order of their constituents' messages. Languages in this group use pattern-matching techniques to compact filtering expressions. Despite that, only JErlang [33], JCThorn [32], and Sparrow support a non-linear pattern matching mechanism. This mechanism allows join patterns to synchronize the values of shared attributes among multiple messages without guard expressions. The above join languages also force developers to statically bound patterns and reactions during the definition of a pattern. Furthermore, they lack support for timing constraints and accumulation operators to filter messages. 10:22

Conclusions
Complicated message exchange patterns play an important role in the development of modern actor-based applications. We introduce five novel types of synchronisation operators to describe coordination between actors in a modern actor-based language. The operators were inspired by well-established abstractions in the CEP domain. The need for the operators was supported by real-world scenarios coming from the smart home automation community. A poll ran in the said community had endorsed our proposal with fairly large evidence. Even the most exotic operator was on the wish list of no less than 25 percent of the developers. Our proposal has been realised technically in the form of an Elixir dialect called Sparrow. It was validated by implementing the scenarios in Sparrow, in two popular smart home automation platforms (openHab, Hass) and in raw Elixir. By labeling each line of code with the concern it contributes to, we have demonstrated that Sparrow does what it claims to do, namely giving actor programmers the abstractions necessary to declaratively specify complex interaction patterns between actors. Sparrow's join patterns have been implemented using discrimination networks and a variant of the RETE algorithm in order to support performance. At the time of writing, we are benchmarking Sparrow against other actor-based coordination approaches. New versions of our DSL will also mitigate the undesired unification of pattern attributes due to the current unhygienic expansion approach. We are also working on an integration of Sparrow into the Hass smart home platform. Furthermore, we plan to open-source Sparrow to the Elixir/Erlang community, allowing us to harvest more user feedback from both communities.   x --------TOTAM [45] x --------Directors [52] x --------Syndicate [20] x --------AErlang [12] x --x a -----interActors [21] x --x a -----RCF [22] x --------Coordinators [28] x -------x