Overview

The continuations library (/mufl/mufl_stdlib/continuation.mm) is a part of the MUFL Standard Library, providing users with advanced functionality to control the order of execution of specific transactions across different ADAPT nodes. This library plays a crucial role when sequential execution of transactions is needed between different network participants.

To provide a more tangible understanding of the continuations library, let’s consider a scenario involving a user and a data storage system. This process involves three primary actions:

  • Action 1 - The user sends a request to retrieve specific data.
  • Action 2 - The data storage sends the requested data to the user.
  • Action 3 - The user saves the received data locally.

You could implement the process without using the continuations library by defining a transaction for each action. For example:

library user uses transactions
{
    fn save_data (_) {} // placeholder for a function saving the data

    trn request_data data_storage_packet_id: global_id
    {   
        // request data from the storage (Action 1)
        return ::transaction::success [
            ::transaction::action::send data_storage_packet_id ($name -> "::data_storage::on_data_request", $targ -> _get_container_id())
        ].
    } 
    
    trn on_data_received data
    {
        save_data data. // suppose the 'save_data' function is implemented and it saves received data locally (Action 3)
        return ::transaction::success [].
    }
}

application data_storage uses transactions
{
    fn get_data (_) {} // placeholder for a function loading the data from the local storage

    trn on_data_request user_packet_id: global_id
    {
        data = get_data (). // suppose the 'get_data' function is implemented and it returns data to be sent
    
        // send data to the user (Action 2)
        return ::transaction::success [
            ::transaction::action::send user_packet_id ($name -> "::user::on_data_received", $targ -> data)
        ]. 
    }
}

While this implementation of the given scenario is valid, as the number of actions grows, so does the number of transactions, making the code less readable and harder to maintain.

A better approach is to use the continuations library to implement the process using only two transactions regardless of the number of actions. For example::

library user loads library continuation uses transactions
{
    fn save_data (_) {} // placeholder for a function saving the data

    trn request_data data_storage_packet_id: global_id
    {
        // define a continuation callback
        fn on_data_received (data)
        {
            // save the data (Action 3)
            // this code executes only when a continuation with a given key is invoked
            save_data data. 
        }
    
        // create a unique continuation ID
        continuation_id = continuation::add_continuation on_data_received.
    
        // send the request to the storage along with a continuation ID (Action 1)
        return ::transaction::success [
            ::transaction::action::send data_storage_packet_id ($name -> "::data_storage::on_data_request", $targ -> ($continuation_id -> continuation_id, $user_packet_id -> _get_container_id()))
        ].
    }
}

application data_storage loads libraries continuation uses transactions
{
    fn get_data (_) {} // placeholder for a function loading the data from the local storage

    trn on_data_request _:($continuation_id -> continuation_id: global_id, $user_packet_id -> user_packet_id: global_id)
    {
        data = get_data ().
    
        // send the data to the user (Action 2) 
        return ::transaction::success [
            ::transaction::action::send user_packet_id (
                $name -> "::continuation::continue_transaction", 
        //                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ note that the data storage sends another transaction to the user 
                $targ -> ($id -> continuation_id, $args -> data))
        ].
    }
}

This example provides a clear and detailed overview of how the continuations library can be used to streamline the sequencing of actions in a multi-action process.