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.
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.
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
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
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.
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.
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;