The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

Promises::Cookbook::ScalaFuturesComparison - A comparison of Scala Futures with Promises

VERSION

version 1.04

DESCRIPTION

Here is the example Scala code, it assumes a function called fetch which when given a URL will return a Future.

def getThumbnail(url: String): Future[Webpage] = {
    val promise = new Promise[Webpage]
    fetch(url) onSuccess { page =>
        fetch(page.imageLinks(0)) onSuccess { p =>
            promise.setValue(p)
        } onFailure { exc =>
            promise.setException(exc)
        }
    } onFailure { exc =>
        promise.setException(exc)
    }
    promise
}

If we take this and translate this into Perl code using the Mojo::UserAgent library, the fetch function would look like this:

sub fetch {
    state $ua = Mojo::UserAgent->new;
    my $url   = shift;
    my $d     = deferred;
    $ua->get($url => sub {
        my ($ua, $tx) = @_;
        $d->resolve( $tx );
    });
    $d->promise;
}

And if we were to take the get_thumbnail function and translate it exactly, we would end up with this:

sub get_thumbnail {
    my $url = shift;
    my $d   = deferred;
    fetch( $url )->then(
        sub {
            my $tx = shift;
            fetch( $tx->res->dom->find('img')->[0]->{'src'} )->then(
                sub { $d->resolve( $_[0] ) },
                sub { $d->reject( $_[0] ) },                
            )
        },
        sub { $d->reject( $_[0] ) }
    );
    $d->promise;
}

Scala Futures have a method called flatMap, which takes a function that given value will return another Future. Here is an example of how the getThumbnail method can be simplified by using it.

def getThumbnail(url: String): Future[Webpage] =
    fetch(url) flatMap { page =>
         fetch(page.imageLinks(0))
    }

But since our then method actually creates a new promise and wraps the callbacks to chain to that promise, we don't need this flatMap combinator and so this, Just Works.

sub get_thumbnail {
    my $url = shift;
    fetch( $url )->then(
        sub {
            my $tx = shift;
            fetch( $tx->res->dom->find('img')->[0]->{'src'} );
        }        
    );
}

Scala Futures also have a rescue method which can serve as a kind of catch block that potentially will return another Future.

val f = fetch(url) rescue {
    case ConnectionFailed =>
      fetch(url)
}

Just as with flatMap, since our callbacks are wrapped and chained with a new Promise, we can do a rescue just by using the error callback The Promise returned by fetch will get chained and so this will depend on it.

sub get_thumbnail {
    my $url = shift;
    fetch( $url )->then(
        sub {
            my $page = shift;
            fetch( $page->image_links->[0] );
        },
        sub {
            given ( $_[0] ) {
                when ('connection_failed') {
                    return fetch( $url );
                }
                default {
                    return "failed";
                }
            }
        }
    );
}

TODO ... figure out how retry can be generic ...

SEE ALSO

Systems Programming at Twitter - http://monkey.org/~marius/talks/twittersystems/

AUTHOR

Stevan Little <stevan.little@iinteractive.com>

COPYRIGHT AND LICENSE

This software is copyright (c) 2020, 2019, 2017, 2014, 2012 by Infinity Interactive, Inc.

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.