Async-Trampoline

 view release on metacpan or  search on metacpan

lib/Async/Trampoline.pm  view on Meta::CPAN

    });

    $async = await [$x, $y] => sub {
        my (@x_and_y_values) = @_;
        # ...
        return $new_async;
    };

    $async = $x->complete_then($y);
    $async = $x->resolved_or($y);
    $async = $x->resolved_then($y);
    $async = $x->value_or($y);
    $async = $x->value_then($y);

    $async = $x->concat($y);

Generators

    $gen = async_yield async_value(1, 2, 3) => sub {
        # ...
        return $next_generator;
    };

    $gen = $gen->gen_map(sub {
        my (@values) = @_;
        # ...
        return $new_async;
    });

    $async = $gen->gen_foreach(sub {
        my (@values) = @_;
        return async_cancel if not @values;  # like "last" in Perl
        # ...
        return async_value;  # like "next" in Perl
    });

    $async = $gen->gen_collect;

Misc. accessors

    $str = $async->to_string;

    $bool = $async->is_complete;
    $bool = $async->is_cancelled;
    $bool = $async->is_error;
    $bool = $async->is_value;

=head1 DESCRIPTION

Trampolines are a functional programming technique
to implement complex control flow:
Instead of returning a result from a function,
we can return another function that will at some point return a result.
The trampoline keeps invoking the returned function
until a result is returned.
Importantly, such trampolines eliminate tail calls.

This programming style is powerful but inconvenient
because you tend to get callback hell.
This module implements simple Futures with an async/await syntax.
Instead of nesting the callbacks, we can now chain callbacks more easily.

This module was initially created
in order to write recursive algorithms around compiler construction:
recursive-descent parsers and recursive tree traversal.
However, it is certainly applicable to other problems as well.
The module is written in C++ to keep runtime overhead minimal.

=head2 Example: loop

Synchronous/imperative:

    my @items;

    my $i = 5;
    while ($i) {
        push @items, $i--;
    }

=for test
    is "@items", "5 4 3 2 1", q(Synchronous/imperative);

Synchronous/recursive:

    sub loop {
        my ($items, $i) = @_;
        return $items if not $i;
        push @$items, $i--;
        return loop($items, $i);  # may lead to deep recursion!
    }

    my $items = loop([], 5);

=for test
    is "@$items", "5 4 3 2 1", q(Synchronous/recursive);

Async/recursive:

    sub loop_async {
        my ($items, $i) = @_;
        return async_value $items if not $i;
        push @$items, $i--;
        return async { loop_async($items, $i) };
    }

    my $items = loop_async([], 5)->run_until_completion;

=for test
    is "@$items", "5 4 3 2 1", q(Async/recursive);

Async/generators:

    sub loop_gen {
        my ($i) = @_;
        return async_cancel if not $i;
        return async_yield async_value($i) => sub {
            return loop_gen($i - 1);
        };
    }

    my $items = loop_gen(5)->gen_collect->run_until_completion;

=for test
    is "@$items", "5 4 3 2 1", q(Async/generators);

=head1 ASYNC STATES

Each Async exists in one of these states:

=for test ignore

    Async
    +-- Incomplete
        +-- ... (internal)
    +-- Complete
        +-- Cancelled
        +-- Resolved
            +-- Error
            +-- Value

=for test

In B<Incomplete> states, the Async will be processed in the future.
At some point, the Async will transition to a completed state.

In C<async> and C<await> callbacks,
the Async will be updated to the state of the return value of that callback.

B<Completed> states are terminal.
The Asyncs are not subject to further processing.

A B<Cancelled> Async represents an aborted computation.
They have no value.
Cancellation is not an error,
but C<run_until_completion()> will die when the Async was cancelled.
You can cancel a computation via the C<async_cancel> constructor.
Cancellation is useful to abort loops,
or to fall back to an alternative with
C<< $may_cancel->resolved_or($alternative) >>.

B<Resolved> Asyncs are Completed Asyncs that finished their computation
and have a value, either an Error or a Value upon success.

An B<Error> Async indicates that a runtime error occurred.
Error Asyncs can be created with the C<async_error> constructor,
or when a callback throws.
The exception will be rethrown by C<run_until_completion()>.

A B<Value> Async contains a list of Perl values.
They can be created with the C<async_value> constructor.
The values will be returned by C<run_until_completion()>.
To access the values of an Async, you can C<await> it.

=head1 CREATING ASYNCS

=head2 async

    $async = async { ... };

Create an Incomplete Async with a code block.
The callback must return an Async.
When the Async is evaluated,
this Async is updated to the state of the returned Async.

=head2 async_value

    $async = async_value @values;

Create a Value Async containing a list of values.
Use this to return values from an Async callback.

=head2 async_error

    $async = async_error $error;

Create an Error Async with the specified error.
The error may be a string or error object.
Use this to fail an Async.
Alternatively, you can C<die()> inside the Async callback.

=head2 async_cancel

    $async = async_cancel;

Create a Cancelled Async.
Use this to abort an Async without using an error.



( run in 0.620 second using v1.01-cache-2.11-cpan-cdf2f3d4e48 )