Futures

Usage

use Futures;

Containers for accessing the results of asynchronous execution.

A Future object is a container that can store the result of an asynchronous operation, which can be retrieved when the result is ready.

Usage

A valid Future object is not created directly. Instead, a future may be created by calling the async() function, which takes as arguments the function to be executed and all arguments to that function.

The following example demonstrates a trivial use of futures. Three computations are executed asynchronously.

use Futures;

config const X = 42;

const A = async(lambda(x: int) { return 2 * x; }, X);
const B = async(lambda(x: int) { return 3 * x; }, X);
const C = async(lambda(x: int) { return 4 * x; }, X);

writeln(A.get());
writeln(B.get());
writeln(C.get());

Validity of Futures

A future that is initialized by a call to async() or Future.andThen() is created in a valid state. Otherwise — for example, when a future is declared but not initialized — the future is in an invalid state and method calls other than Future.isValid() on an invalid future will halt(). If such a future object is subsequently assigned to by a call to async() or Future.andThen(), then the future will become valid.

use Futures;

// F is a valid future
var F = async(lambda(x: int) { return 99; }, 13);
const f = F.get();

// G is currently an invalid future
var G: Future(int);

// ...other things can happen here...

// G is now a valid future
G = async(lambda(x: int) { return 42 + x; }, 23);
const g = G.get();

Task Arguments

The task argument in a call to async() or Future.andThen() may be a first-class function, a lambda function, or a specially-constructed class or record. Such a record must have both a proc this() method for the desired computation and a proc retType type method that returns the return type of the this() method. (The requirement for the retType method is a currently limitation that is intended to be resolved in the future.) For example:

use Futures;

config const X = 99;

proc foo(x: int) {
  return (x:real)/3.1415926;
}

record Bar {
  proc this(x: real) {
    return x / 2;
  }
  proc retType type return real;
}
const bar: Bar;

const A = async(foo, X);
const B = A.andThen(bar);
writeln(B.get());

Future Chaining

A continuation to a future (itself a future) can be created via the Future.andThen() method, which takes as its single argument a function to be invoked asynchronously (with respect to other tasks) but strictly ordered in execution after the result of the parent future is ready. The continuation function takes a single argument, the result of the parent future.

The following examples demonstrate such chaining of futures.

use Futures;

config const X = 42;

const F = async(lambda(x: int) { return 2 * x; }, X)
  .andThen(lambda(x: int) { return x + 7; })
  .andThen(lambda(x: int) { return 3 * x; });

writeln(F.get() == (3 * ((2 * X) + 7))); // prints "true"
use Futures;

config const X = 1234;

var F = async(lambda(x:int) { return x:string; }, X)
  .andThen(lambda(x:string) { return(x.length); });

writeln(F.get()); // prints "4"

Future Bundling

A set of futures can be bundled via Future.waitAll(), which takes a variable number of futures as arguments and returns a new future whose return type is a tuple of the return types of the arguments. The returned future is ready only when all the future arguments are ready.

The following example demonstrate bundling of futures.

use Futures;

config const X = 23;

const A = async(lambda(x: int) { return  5 + x; }, X);
const B = async(lambda(x: int) { return  7 * x; }, X);
const C = async(lambda(x: int) { return 11 - x; }, X);
const D = waitAll(A, B, C);

writeln(D.get()); // prints (28, 161, -12)
record Future

A container that can store the result of an asynchronous operation, which can be retrieved when the result is ready.

A future is not created directly. Instead, one is created by calling the async() function or the Future.andThen() method on an already-existing future.

type retType

The return type of the future.

proc get(): retType

Get the result of a future, blocking until it is available.

If the future is not valid, this call will halt().

proc isReady(): bool

Test whether the result of the future is available.

If the future is not valid, this call will halt().

proc isValid(): bool

Test whether the future is valid. For more, see above.

proc andThen(taskFn)

Asynchronously execute a function as a continuation of the future.

The function argument taskFn must take a single argument of type retType (i.e., the return type of the parent future) and will be executed when the parent future's value is available.

If the parent future is not valid, this call will halt().

Arguments:taskFn -- The function to invoke as a continuation.
Returns:A future of the return type of taskFn
proc async(taskFn)

Asynchronously execute a function (taking no arguments) and return a Future that will eventually hold the result of the function call.

Arguments:taskFn -- A function taking no arguments
Returns:A future of the return type of taskFn
proc async(taskFn, args ...)

Asynchronously execute a function (taking arguments) and return a Future that will eventually hold the result of the function call.

Arguments:
  • taskFn -- A function taking arguments with types matching args...
  • args... -- Arguments to taskFn
Returns:

A future of the return type of taskFn

proc waitAll(futures ...?N)

Bundle a set of futures and return a Future that will hold a tuple of the results of its arguments (themselves futures).

Arguments:futures... -- A variable-length argument list of futures
Returns:A future with a return type that is a tuple of the return type of the arguments