Futures - Spicery/Nutmeg GitHub Wiki
"Futures" are placeholders for the results of computation that happen 'out of sequence'. This computation is forced to happen on-demand when the actual value is required (e.g. because of a run-time type-check). Technically this makes them implicit futures. If an executing thread needs the value of a future, it will automatically block until that future has been evaluated.
There is a simple syntax for creating futures using $
and $$
. The first version is appropriate when EXPRESSION
is const.
$( EXPRESSION ) # A future that when forced (implicitly) must yield a single value, equivalent to $( EXPRESSION:: _ )
$( EXPRESSION:: PATTERN) # A future that when forced (implicitly) must match the pattern.
PATTERN := $( EXPRESSION ) # Binds the variables of the pattern to futures, the expression must yield a matching result.
The second version can be used for arbitrary expressions:
$$( EXPRESSION ) # A future that when forced (implicitly) must yield a single value, equivalent to $( EXPRESSION:: _ )
$$( EXPRESSION:: PATTERN) # A future that when forced (implicitly) must match the pattern.
PATTERN := $$( EXPRESSION ) # Binds the variables of the pattern to futures, the expression must yield a matching result.
The futures created by this syntax are lazy, meaning that by default their evaluation is deferred as long as possible. On the other hand they can be scheduled for earlier execution using the FUTURE.eval() -> FUTURE
method, which returns the same future as it was given.
There is an important difference between const and non-const futures. Const futures will typically be executed in a completely separate thread. But a non-const future is always executed on the same thread using the same event-loop mechanism as is familiar from Javascript and Python because we want mutable shared state to be accessed in a predictable way. (Unlike Javascript and Python there is no need to use async
/await
markers. All Nutmeg functions check a swap flag at procedure entry, backward jumps and on I/O. This flag is set asynchronously when the runtime system determines that it is time for a task swap. This does require multiple call stacks in general.)
Nutmeg also has concurrently evaluated futures via message sending. Messages are sent asynchronously and their results are represented by futures. These futures are filled-in when the message-handler has finished, as opposed to being evaluated lazily.
Sometimes you want to iterate over a collection of futures in the order they are available. This is made possible via the FutureCollection
.
# Create a collection of futures
val fcollection = FutureCollection( for i in L do $(f(i)) end )
# How many are ready?
val count = fcollection.countReady
# Pick the first one to be ready, removing it from the collection.
val v = fcollection.popReady
# Iterate over a collection
for i in fcollection.inOrder(): ...