Promise is a very fundamental abstract concept - a promise to do (deliver) something (a value) later, not right now (in the future), but the procedure (a transaction) could be completed (a delegation arranged, a worker dispatched) right now.
This is the basics of a bookkeeping - one completes a transaction and receives a document which is a promise of some value to be delivered later.
This abstraction is somehow faulty, because there is no counterpart in molecular biology, which has no concept of the future (or time, or even counting - in the Universe everything happens right now), but it is useful nevertheless.
In the most generic case a promise is just unevaluated yet expression, wrapped inside a specially type-tagged
Scheme has the
delay special form which wraps an unevaluated expression (copies a corresponding part of a syntax tree).
1 ]=> (define p (delay (+ 1 1))) ;Unspecified return value 1 ]=> p ;Value: #[promise 13 (unevaluated)]
There is the
force special form which "forces" a
1 ]=> (force p) ;Value: 2
Once evaluated, the value is memoized (cached) so it will will be always
2, no matter how many times you
1 ]=> p ;Value: #[promise 13 (evaluated) 2] 1 ]=> (force p) ;Value: 2
This is the most general form of a
Promise - every expression could be
delayed (wrapped into a
Promise), as long as it is a pure function (Same input - same output. Always.) and therefore the value it returns could be memoized (it will be semantically correct, no matter memoized or not).
There is a problem, however, with the side-effecting expressions. If it is a blocking call to a primitive procedure which does IO,
force will block for the first time, but what if we
force it one more time - should a memoized value be returned or a the same IO request issued again?
What if we wish to read a next chunk of data from a handle?
When we have a concept of Future, which means not now, we have an artificial problem (which Nature does not have) - what to do until that future is arrived? The natural answer is - nothing - wait, sleep, block, or do something else.
Every reasonable person would choose to continue to do something else, instead of sitting and waiting for a value to be delivered. In order to continue on has to get something instead of nothing to stop the waiting. That something is traditionally called a thunk - something "concrete to hold onto" which stands for a promise for a value.
Thunks are mere closures (All you need is Lambda!) with an unevaluated expression inside. Once called the expression is evaluated and the resulting value is returned (substituted for the thunk). The next time the same thunk is to be called a cached value is returned instead of re-evaluation of an expression.
Future is a kind of a Thunk. It is a promise to deliver a value when it would be ready (available). An attempt to evaluate a Future before the value is ready would block, because there is still nothing to deliver. A deliveryman is waiting.
Since a future is either deliver its promise or fail it is an Algebraic Data Type - an Either-Of Type - an Option Type.
Futures are composable.
Future blocks when called, because nothing could NOT be substituted for a value.
Future could be viewed as a container, a first-class type-tagged entity, which could be a member of other aggregates (ordered collections).
Since a future blocks until completion or a failure, aggregates (ordered sequences of futures) are implicitly evaluated in order (a sequence implies ordering).
Future could be viewed as a collection (of zero or one element) so it could be for-each-ed, mapped over, reduced or even filtered.