Futures and promises
Adapted from Wikipedia · Discoverer experience
In computer science, futures, promises, delays, and deferreds are special tools used to help programs run smoothly, especially when doing many things at once. These tools act like stand-ins for results that aren’t known yet because the computer is still working on them.
The idea of a promise was first suggested in 1976 by Daniel P. Friedman and David Wise. Around the same time, in 1977, Henry Baker and Carl Hewitt introduced a similar idea called a future.
Although these terms are often used interchangeably, they can mean slightly different things. A future is like a read-only placeholder—it shows a value that will be set later. A promise, on the other hand, is like a writable container that decides the value of the future. Importantly, a future can be set by different promises, but each future can only be set once. In many cases, a future and a promise are created together, with the future holding the value and the promise being the function that sets that value. This process of setting the value is also called resolving, fulfilling, or binding the future.
Applications
Futures and promises started in functional programming and similar methods to separate a value from how it is calculated. This makes computing more flexible, including doing it at the same time.
Later, they were used in distributed computing to lower delays from sending messages back and forth. They also help in writing programs that do not wait for steps to finish before moving on, making them easier to write.
Implicit vs. explicit
Futures can be used in two ways: implicit or explicit. In implicit use, the future gets its value automatically, like a normal reference. In explicit use, the user needs to call a function, such as the get method in Java, to obtain the value. Getting the value of an explicit future is sometimes called stinging or forcing.
The original paper by Baker and Hewitt talked about implicit futures, which work well in the actor model and in object-oriented languages like Smalltalk. The paper by Friedman and Wise focused on explicit futures, likely because making implicit futures work on regular computers is tricky. For example, a simple addition like 3 + _future_ factorial(100000) is hard for normal hardware to handle. But in actor or object-oriented languages, this can be solved by sending the future a message to add 3 to itself and return the result. This message passing works no matter when the future finishes its calculation, and no extra steps are needed to get the value.
Promise pipelining
Futures can help make things faster in systems that are spread out over a network. They allow something called promise pipelining, used in languages like E and Joule.
When you use regular ways to talk between distant computers, each step must wait for a reply before moving on. This can take a lot of time.
But with futures, you can write things in a way that starts many steps at once, without waiting for each reply. This saves time and makes things work faster.
Thread-specific futures
Some programming languages, like Alice ML, have special tools called futures that are tied to a specific part of the program that works on them. These futures can start working right away or wait until their result is needed for the first time. When they wait, they act like a delayed task.
In Alice ML, there are also tools called promises that any part of the program can finish. This use of promises is different from how other languages might use the same word. In Alice ML, promises work a bit differently and don’t allow certain advanced tricks that futures can do.
Blocking vs non-blocking semantics
When we want to get the result of a future in some computer programs, we can do it in two main ways.
In some cases, we can ask for the result and wait until it is ready. This is like sending a message and waiting for a reply. This works well in systems where we only ever wait like this.
But sometimes, a program might try to get the result right away. When this happens, programmers have to decide what to do. They can choose to make the program stop and wait until the result is ready. Or, they can choose to say that trying to get the result this way is an error. Some systems even allow getting the result only if it is already ready, but this can cause problems if not handled carefully.
For example, in the C++11 programming language, a part of the program can stop and wait for the result when needed by using special commands. This can also have a time limit to avoid waiting forever.
Related constructs
Futures are a special kind of tool called an "event" that can only finish once. Normally, events can be reset and finished many times.
In some languages, an I-var is a type of future that waits for a result before moving on. An I-structure is a group of these I-vars. Another tool, called an M-var, can be updated many times with new values. M-vars let you take or add values, and taking a value resets the M-var to start over.
There are also tools called concurrent logic variables that work like futures but can change more than once by matching values together. The dataflow variables in the language Oz work this way too. Other tools, called concurrent constraint variables, let you narrow down possible values over time and can run special tasks when new limits are added.
Relations between the expressiveness of different forms of future
In computer programming, there are different ways to handle tasks that take time to finish. One way is to use "eager thread-specific futures," which can be made from "non-thread-specific futures" by starting a new thread to calculate the result. This helps keep things simple for the user.
Another method uses "implicit lazy thread-specific futures," which need a way to know when the result is needed for the first time. Some systems have special tools for this, like the WaitNeeded construct. By using messages between threads, one type of future can be made from another, but this might add extra steps that aren't always needed. The best approach often combines different types of futures and tools to make things work smoothly.
Evaluation strategy
Further information: Call by future
The way futures work is not fixed: the value of a future is worked out at some point between when it is made and when it is needed, but we don't know exactly when this will be. It might happen right away (eager evaluation) or only when the value is needed (lazy evaluation). Once the value is found, it is remembered and not worked out again, similar to how some tasks are saved for later use in call by need.
A lazy future always works in a lazy way: the value is only worked out when it is first needed, like in call by need. Lazy futures are helpful in languages where the usual way is not lazy. For example, in C++11, such lazy futures can be made by using a special setting with std::async and giving the function that computes the value.
Semantics of futures in the actor model
In the actor model, a special kind of expression called a "future" works by sending a new actor to handle requests while also starting a new task. This actor checks if it already has a result. If it does, it either sends the result or an error message back to the requester. If it doesn’t have the result yet, it stores the request until the result is ready.
Sometimes, futures can help run tasks in parallel. For example, adding 1 to the result of another future calculation works well. But in some cases, like checking if a number is bigger than the result of another future calculation, the task has to wait until the result is ready before it can continue.
History
The ideas behind futures and promises were first used in programming languages like MultiLisp and Act 1. Similar ideas appeared in logic programming languages such as Prolog and Concurrent ML.
In 1988, scientists Barbara Liskov and Liuba Shrira created a way to use futures to make programs faster, and others invented the same idea around 1989 for a project called Project Xanadu. After the year 2000, many popular programming languages started to include futures and promises to help make programs respond quickly and work well on the web.
List of implementations
Some programming languages support special tools that help manage tasks happening at the same time. These tools are called futures, promises, and similar names. They act like placeholders for results that aren’t known yet because the work to find them isn’t finished.
Many languages include these tools directly or through extra libraries they come with. For example:
- C++ starting with C++11 uses
std::futureandstd::promise - Dart uses
FutureandCompleterclasses withawaitandasynckeywords - JavaScript added support in ECMAScript 2015, with
asyncandawaitsince ECMAScript 2017 - Python added support in version 3.2, with
asyncandawaitin Python 3.5 - Scala uses the scala.concurrent package
Other languages also have libraries that add these tools, such as Java, Lua, and Ruby, among many others.
Futures can also be created using special programming methods called coroutines or generators. They can also be made using channels, which are like pathways for passing information between parts of a program.
Main article: Channel (programming)
Related articles
This article is a child-friendly adaptation of the Wikipedia article on Futures and promises, available under CC BY-SA 4.0.
Safekipedia