Skip to content

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
        };
    };
};