Future Types

A future is a strongly typed variable that represents a value that might or might not have been calculated yet, but is promised to be (made) available when needed.

A future is expressed by the future keyword, followed by a type:

var count: future Integer;

Future values can be used interchangeably with their underlying type, including as parameters to Method Calls or even in arithmetic or logical Expressions. The first time the value of a future is accessed, execution will wait for the future's value to become available.

The futureAssigned() System Function can be used to check if a future itself is assigned or not (i.e. is nil). Note that a future can be assigned, but still have a determined value of nil, of course.

Synchronous and Asynchronous Futures

Future types can be synchronous or asynchronous. By default, futures are synchronous, and will be evaluated the first time they are used.

When futures are used in combination with an Async Expression, they become asynchronous, and will execute to determine their value on a background thread. When an asynchronous future is first accessed, its value might or might not have been determined yet. If it has not, one of two things can happen:

  • If execution of the future has already started, access will block the current thread and wait for that execution to finish and the value to become available.
  • If execution of the future has not started yet, it will be executed inline within the current thread.

Both of these scenarios happen transparently to the calling code.

Futures and Exceptions

Any exception that occurs while calculating the future will be caught, cached, and re-thrown whenever the future's value is accessed.

Example

Consider the following snippet of pseudo code that calculates the Sum of values in a binary tree:

method ThreeNode.Sum: Integer;
begin
  var l := Left.Sum;
  var r := Right.Sum;
  result := l+r;
end;

This code first calculates the Sum of the left part of the subtree, then that of the right one. Finally, the two values are combined using the + operator. Calculating the value of both l and r might take a relatively long time, yet the value of l is not actually needed until the very last line of the method. This is an ideal candidate for a future:

method ThreeNode.Sum: Integer;
begin
  var l: future Integer := async Left.Sum;
  var r: Integer := Right.Sum;
  result := l+r;
end;

Instead of calculating the value of l in place as before, l is now defined as a future Integer, declaring that the variable does not actually hold the value of Left.Sum, but just the promise that, at some point in the future, it will. This first line of code will execute in virtually no time, and the method can move on to calculating r, which is unchanged and will happen inline, as before.

Note how the value assigned to l has been changed to include the async keyword, making it an async expression that will be spawned in the background. In fact, it's this use of the async keyword that makes the future useful in this case.

The result of an async expression is always a future, so the code would behave the same without explicit type declarations:

method ThreeNode.Sum: Integer;
begin
  var l := async Left.Sum; // l will become a future Integer
  var r := Right.Sum;      // r is still a regular Integer
  result := l+r;
end;

The actual value of the future, l in this example, will not be accessed until it is used in code. In the code above, this happens on the last line of the method, when l is used with the + operator to combine with with r.

When the value is accessed, one of three things can happen:

  • If the future is already done executing in the background, its value will be available immediately, just as if it were a plain non-future type that is being accessed.
  • If the future is not finished executing at that point, execution will hold and block the current thread until the future is done.
  • If any exception occurred while executing the future in the background, that exception will be re-thrown as the future value is accessed.

Note how in the example above, the code does not need to worry about whether the value of the future l has already been determined or not when execution reaches the last line and the value is required. The code can simply treat l as if it were a regular Integer.

Type-Less Futures

Futures can also be type-less, which is also referred to as Void Futures. Such a type-less future does not represent a value, but merely a certain action that will be run in the background.

A type-less future can be called, like a statement, in order to wait for its action to be finished. But because a type-less future has no value, it cannot be used as an expression, only as a statement.

var f := async begin // goes off and does a couple of things in the background
  DoSomething();
  DoSomethingElse();
end;

DoSomeMore(); // meanwhile, do more work on the current thread

f(); // wait for the type-less future to finish, if it hasn't already.

var x := f; // Compiler error: f has no value.

As with typed futures, if any exception occurred in the background while executing the future, that exception will be re-thrown if and when the future is being called into.

Non-Asynchronous Futures

While asynchronous futures are the most common use case, a future type in itself does not imply background execution – it merely implies a value that may or may not exist, and will be made available when needed.

If a future is declared and initialized with an expression that is not an async expression, the value will be calculated the first time it is accessed.

method ThreeNode.Sum: Integer;
begin
  var valueA := SomeCostlyOperation();
  var valueB := SomeOtherCostlyOperation();

  // ...

  if x > 10 then
    result := SomeCostlyOperation + SomeOtherCostlyOperation; // SomeCostlyOperation will only
  else                                                    // be calculated if we hit this line
    result := SomeOtherCostlyOperation;

  result := result*SomeOtherCostlyOperation; // in any case, SomeOtherCostlyOperation is only
end;                                         // calculated once

A Future Executes only Once

Both typed and type-less futures will only execute a single time.

The value of a typed future may be accessed multiple times during the flow of execution; subsequent access will simply yield the value directly.

Similarly, the first time you call into a type-less future, execution will wait if needed; subsequent calls will be guaranteed to just return immediately.

method ThreeNode.WeirdSum: Integer;
begin
  var l := async Left.Sum;
  var r := Right.Sum;
  result := l+r+l; // l will only be calculated once, even though it's being accessed twice
end;

See Also