8.6 Placeholders and Pattern Matching
The variables in patterns are called placeholders because they occupy the places in a pattern that are "open." Matching is a game of trying to fill the open places in a pattern with values (also called "objects") so that the instance of the pattern is identical to the event or poset that we are trying to match.
8.6.1 Matching Basic Event Patterns
A basic event pattern matches an event if when its placeholders are replaced by objects, the resulting instance of the pattern is identical to the event. Replacing placeholders with objects to make a match is subject to two conditions.
The type of object that replaces a placeholder must be the same as, or a subtype of, the type of the placeholder.
A placeholder must be replaced by the same object at all of its positions in a pattern in any one match.
For a basic pattern to match an event, it must have the same action name.
8.6.2 Placeholder Bindings
The result of a successful match of a pattern to an event (or more generally, a poset) is an association of placeholders with objects that replaced them in the instance that matched the event. This is called a binding of placeholders to objects.
A binding is usually represented as a set of pairs consisting of a placeholder and an object, <placeholder value>, meaning "replace the placeholder by the value."
Example 1: Matching bids in an RFQ process
// basic pattern (Vendor VId, BidId Offer) Bid(VId, Offer, EngineSpec#10, $2,000, 5000) // event Bid(Vendor#5, RF#20, EngineSpec#10, $2,000, 5000) // binding that results in a match {<VId Vendor#5>, <Offer RF#20>}
The pattern has the action name Bid in our supply chain events (see the declaration of Bid line in Section 8.3.4, Example 3). It has a placeholder, VId, for the Vendor parameter and a placeholder, Offer, for the BidId parameter of the Bid action. The other parameters have constant values for the ProdSpec, Price, and Quantity, so the pattern will match Bid events that have those constant values. The example shows an event and the placeholder binding that results in a match. If we replace VId by Vendor#5 and Bid by RF#20, we get an instance of the pattern that is identical to the event.
Notice in this example that constants in patterns are very restrictive. Often we want to match a range of values, say, for price or quantity, rather than one value. We will see how to do this with context guards later.
Example 2: Using a predefined attribute in a pattern
// Pattern (ATM Machine M; Dollars D) Deposit(origin is M, D, Accnt#123) // event Deposit(origin is ATM3, 1000, Accnt#123) // binding resulting in a match {<M ATM3>, <D 1000>}
This pattern matches Deposit events from our ATMUse actions (see Section 8.3.4, Example 2). The pattern contains a placeholder, M, for the predefined attribute, origin. It uses a parameter-naming notation (is), which we will discuss later.
We are assuming that in the ATMUse system, the actual ATM can be an origin recorded in the predefined origin attribute in the events it creates. This pattern will match events from any ATM that deposit any amount to a fixed account, Accnt#123. The event in the example is a deposit originating at ATM3 of $1,000 to Accnt#123. The binding shows that M must be replaced by ATM3 and D by $1,000 to make the pattern match the event.
The golden rule about matching is that in order to match a pattern, a placeholder can be bound to only one object in all its occurrences in the pattern. So, if a placeholder occurs more than once in a pattern, a matching event or poset must have the same data at those positions.
Different matches of a pattern usually (but need not) result in different placeholder bindings.
Here are some examples of basic patterns using placeholders.
Example 3: Placeholders in basic patterns
// 1. Any transfer of any amount from and to the same account (Dollars D, Account Type A) Transfer(D, A, A); // 2. Any event originating from ATM3 (event E) E(origin is ATM3);
The first pattern has the same placeholder, A, as both the From Accnt and To Accnt parameters of a Transfer action in ATMUse (see Section 8.3.4, Example 2). So, it will match events in which some unspecified amount of money is transferred from any account to the same account. It will match events such as the following:
Transfer(10, Accnt#123, Accnt#123), if the binding is {<D $10>, <A Accnt#123>}, Transfer(25, Accnt#47, Accnt#47), if the binding is {<D $25>, <A Accnt#47>},
The second pattern shows a powerful use of a placeholder of the pre-defined event type. It matches any event generated by ATM3. E will be bound to the event, whether it is a Deposit, Transfer, or Withdraw. This is a succinct way to write a pattern to monitor a particular ATM. If the event is
Transfer(origin is ATM3, 10, Accnt#47, Accnt#123),
the binding is
{<E ß Transfer(origin is ATM3, 10, Accnt#47, Accnt#123)>}
8.6.3 Notation to Aid in Writing Patterns
To emphasize the role of placeholders, Rapide-EPL allows a "?" as a prefix to a placeholder. The use of "?" is optional. It helps distinguish the variable parts of a pattern from the constant parts. Some of the previous examples of basic patterns can be written as follows:
(Dollars ?D, Account Type ?A) Transfer(?D, ?A, ?A); (Vendor ?VId, BidId ?Offer) Bid(?VId, ?Offer, EngineSpec#10, $2,000, 5000)
Naming Parameters
A common error in writing a basic pattern is misordering the placeholders in the list of parameters of the action name. That is, the order of the placeholder parameters in the pattern is not consistent with the order of the parameters in the action declaration. To prevent this kind of error, each parameter in a basic pattern can be explicitly associated with the name of an action's formal parameter. You just use the parameter from the action declaration to name the parameter in the pattern. The notation for doing this is
action parameter name is pattern parameter
This is called naming the parameters in the pattern and is an optional notation.
Example 1: A basic pattern written in named parameter form
(Dollars ?D, Account Type ?A, ?B) Transfer(To Accnt is ?A, Amount is ?D, From Accnt is ?B );
Look at the Transfer action declaration in ATMUse (see Section 8.3.4, Example 2). The placeholder parameters in the pattern here are written in a different order from the order in which the action's parameters are declared. But it doesn't matter because we have associated each placeholder with the action's parameter that it corresponds to. So in this pattern, the To Accnt is ?A, the Amount is ?D, and the From Accnt is ?B.
Omitting Parameters
A useful feature in writing patterns is to omit a parameter whose binding is irrelevant to the matches you want. That means that you don't care about the omitted parameters, so any value will match them. But to do this without ambiguity, you must name the parameters you do use in the pattern so that it is obvious which of an action's parameters you want to include.
Example 2: Omitting action parameters
Deposit(account is Accnt#123); (Account Type ?A) Transfer(From Accnt is ?A, To Accnt is ?A);
Here the Deposit pattern matches any Deposit event to account Accnt#123. We are not interested in the amount. The Transfer pattern matches any transfer from and to the same account. We are not interested in the amount of the transfer, but only the accounts where such a transfer happens.
Using an Event's Public Attributes
The public attributes of events (see Section 8.4) can be used to write more precise patterns. The attribute name is used in the named parameter form. For example, there's an attribute called the origin of an event. It denotes the component in the target system that generated the event. That component may be an object or a module or a thread, depending upon the system. The origin attribute's value can be either a name or a reference to that component.
Example 3: Monitoring all withdrawals generated at a particular ATM
Withdraw(origin is ATM3)
Another useful public attribute is the timestamp of an event. Because timestamps are used frequently in patterns, there are special notations such as at and after, for referring to timestamps that are described later in Section 8.8.3.
Example 4: Filtering out supply chain events according to their timing
(OrderId ?Order) Order(Id is ?Order, end is 12:00) (RFQ ?R) RFQ(Id is ?R, Spec is EngineSpec) after 12:00
The Order pattern will match those order events that happen over a time interval that ends at 12:00. The binding will contain the OrderId of those events.
The RFQ pattern will match those RFQ events for engine specifications that happen after 12:00 and will bind the Id of the RFQ.