Notify
NPL is intended as a domain-specific language, and therefore is unsuitable for performing operations such as database connections, or high performance calculations. Notifications exist to interact with external systems (but not with other protocols).
Defining notifications
A notification is defined with the keyword notification
followed by the unique notification name (in camelCase) and
its input parameters. The return type of a notification is the expected type of the callback.
notification moneyTransfer(fromAccount: Text, toAccount: Text, amount: Number) returns Text;
These definitions may only exist outside of protocols.
Emitting notifications
Notifications are emitted using the notify
keyword. Notifications always originate from protocols, and hold the input
values, the invoking party (the party executing the statement), and a reference to the originating protocol. This
provides a context for potential recipients.
notification moneyTransfer(fromAccount: Text, toAccount: Text, amount: Number) returns Text;
protocol[client, bank] Account(var iban: Text) {
var balance: Number = 0;
permission[client] pay(amount: Number, toAccount: Text) {
// Logic...
notify moneyTransfer(this.iban, toAccount, amount);
};
};
Callbacks using notify-resume
Once a notification has been replied to, the return type may be passed back into the protocol. No special mechanisms exist to process these, and therefore ordinary protocol actions can be used.
Because notifications are meant to facilitate protocol interaction with external systems, burdening the external system
with the need to be aware of every notifying protocol and its callback permissions would be unwise. External systems
will not always have specific knowledge of individual protocols types, and may be used to service protocols of different
types. As such, NPL offers the notify
-resume
construct that captures the callback context on the notification
itself.
The resume
keyword is used to specify an abstract callback mechanism, and is immediately followed by the name of the
callback action. Notifications emitted using resume
contain the callback method name, and the recipient no longer
needs to be directly aware of the protocol and its actions.
notify moneyTransfer(this.iban, toAccount, amount) resume callback;
When a callback is specified, the compiler enforces that the defined action exists. Furthermore, each party that can
cause the notify
to be emitted must have access to this permission. It is assumed that whoever triggers the
notification will also call back into the protocol. The compiler rules for party access are such that the resume
permission must be at least as permissive as the trigger. For notify outside of actions, this means each protocol party
must have access.
The callback signature for a resume
requires a single argument of type NotifyResult<T>
, where T
must be compatible
with the return type of the notification. See NotifyResult for
further details.
For the example above, the full implementation is shown below. Note that T
in NotifyResult<T>
must be of type Text
as it is the notification's return type.
protocol[client, bank] AnotherAccount(var iban: Text) {
var balance: Number = 0;
permission[client] pay(amount: Number, toAccount: Text) {
// Logic...
notify moneyTransfer(this.iban, toAccount, amount) resume callback;
};
// Callback is invoked by the notifying party
permission[client] callback(res: NotifyResult<Text>) {
// Process result of notification
match (res) {
is NotifySuccess<Text> -> {
// Process success
var transactionId = res.result;
}
is NotifyFailure -> {} // Process failure
};
};
};