Skip to end of metadata
Go to start of metadata

Problem

Futures and promises block the derefing thread. Once that is somewhat mitigated by notifying promises, we then get callback hell and inversion of control. The same issues exist in Javascript. These issues were addressed by F#, and later C# async/await, the latter of which has been copied by Scala

Proposal

Copy C# async/await. (async ...) establishes a block, within which (await a-future-or-promise) will relinquish control, wiring up a state machine representing the rest of the block to the notification of the future/promise. The async block itself returns a promise.

This will require notifying futures/promises. Relevant reading:

It will also require an ambient thread pool.

 

Implementation Notes

This proposal has been coded up and the result is available here: https://github.com/relevance/clojure/tree/core-futures Note: the implementation of futures should be considered in "draft" phase, it will change radically before an alpha is released. 

There are 4 major steps to this implementation:

1) The expression inside the state-machine macro is transversed

2) For each node an instruction for a virtual machine is recorded

3) These instructions (in a SSA style block format) are analyzed to find temporary variables

4) The instructions are emitted as a state machine. 

5) The state machine is wrapped by functions that pack and unpack tasks


The output of the state machine macro is a function that takes an old state and produces a new state. Thus these state machines are immutable, and side affect free (assuming the code in them is also side-effect free). 

Work to be done:

  • Think about try/catch/finally, doesn't work at the moment
  • Don't overload the deref value, instead we will use await to deref listenable futures
  • Remove generator code, it could be confusing to new users
  • Decide on a good future/promise library (WIP)
Labels:
  1. May 06, 2013

    core-futures is very cool as is. It doesn't seem like try/catch/finally should be too hard to add, but I do thing it would break the ability to do state transitions via recur.

    the straightforward approach would be to add a stack to the state map, and a try/catch would push a \[ExceptionType state\] pair on the stack, run the code, then pop it off. the  fn created  by `emit-state-machine` would have a try/catch around the body (which would break recur) that would catch Throwable, then walk the exception stack looking for a state to transition in to for that exception's type