Proposal: Add a Future type to the language


(Thomas Visser) #1

Hi all,

Futures (aka Promises) are a well known concept that aid in writing asynchronous code. I’d love to have a Future type that comes with the language.

The way I’d see this work is similar to how Optionals are currently implemented. There’d be a Future<T> type in the standard library, but most developers will choose not to use it directly. Instead, there are some new keywords and syntactic sugar that make it easy to work with the Future concept.

There’d be a new ‘async’ keyword indicating that a type (including return type) is asynchronous:

func blurImage(image: UIImage, radius: Int) -> async UIImage {
  var blurredImage: async UIImage
  dispatch_async(globalQueue) {
    blurredImage = // expensive blur operation
  }
  return blurredImage
}

At the origin of an asynchronous value is something like GCD to actually execute the work on a different thread. (This is not the responsibility of the Future.)

An async type should largely just work like its ‘regular’ synchronous counterpart. The compiler would be able to generate code for methods on that type (e.g. hasPrefix(_:slight_smile: on an async string returns an async Bool) or methods (including operators) that take that type as a parameter (E.g. concatenating two async strings should be as easy as: string1 + string2).

To unwrap a Future, i.e. getting its value, for cases that do not accept asynchronous values (e.g. UI updates), there’d be a new ‘when let’ statement with a control flow that is similar to the guard statement:

when let image = blurImage(image) else {
  throw ImageBlurringFailed()
}

imageView.image = image

This is all very brief, but should give you an idea of my proposal. You can find more details in a blog post I wrote recently: http://www.thomasvisser.me/2015/11/26/async-swift/. This description is probably rather simplistic compared to the work needed to actually support something like this, but I hope it can serve as a starting point.

Context:
- There is a proposal by Nadav Rotem from September 2015 that describes a lot of the foundational work that would have to be done before we can have a safe Future type: https://github.com/apple/swift/blob/5eaa3c43d069d5bd401e7879b43f6290823d180d/docs/proposals/Concurrency.rst. While I think a Future type could exist without language support for concurrency (e.g. using GCD directly, see https://github.com/Thomvis/BrightFutures, full disclosure: I wrote that library), I agree it makes a lot of sense to only do it once it can be done in a safe way.
- There are other concepts that have to do with asynchronous programming such as channels/signals/observables (Rx) and goroutines. I think there’s a place & audience for more than one of these concepts in Swift, but these efforts should of course be coordinated.

I realize this is not in scope for Swift 3, but I’d love to get feedback from the community and hear how it can be incorporated in the discussion when concurrency will be on the agenda (for Swift 4 perhaps)?

Best,
Thomas Visser


(Dan Stenmark) #2

Like you mentioned, the Swift team has made it very clear that concurrency is out of scope for Swift 3, and we probably won’t see it until at least Swift 4 at a bare minimum. That said, while your proposal is a much better alternative to the completion handler pyramids of doom we have today, I’ve personally never been a huge fan of Futures from an intuitiveness standpoint. I would much rather see the language adopt something more akin to C#’s Async-Await, where the compiler handles restructuring synchronous-looking code into something asynchronous and non-blocking.

I guess in my perfect world, the following Swift 2 code:

func bar(completionHandler : (Int) -> Void) -> Void {
  dispatch_after( dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 1), dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ) ) {
    completionHandler(42)
  }
}

func barPlusBar(completionHandler : (Int) -> Void) -> Void {
  var barResult = 0

  bar() { (let result) -> Void in
    barResult += result

    bar() { (let result) -> Void in
      barResult += result
      completionHandler( barResult )
    }
  }
}

func foo() -> Void {
  barPlusBar() { (let result) in
    print( result )
  }
}

would be replaced (and perhaps even become more optimizable) with:

async func bar() -> Int {
  after( NSEC_PER_SEC * 1 )
  return 42
}

async func barPlusBar() -> Int {
  return bar() + bar()
}

func foo() -> Void {
  async {
    print( barPlusBar() )
  }
}

Again, this is pretty much all moot right now as the Swift team has much bigger fish to fry, but it sure is nice to dream.

Dan

···

On Dec 22, 2015, at 2:48 AM, Thomas Visser via swift-evolution <swift-evolution@swift.org> wrote:

Hi all,

Futures (aka Promises) are a well known concept that aid in writing asynchronous code. I’d love to have a Future type that comes with the language.

The way I’d see this work is similar to how Optionals are currently implemented. There’d be a Future<T> type in the standard library, but most developers will choose not to use it directly. Instead, there are some new keywords and syntactic sugar that make it easy to work with the Future concept.

There’d be a new ‘async’ keyword indicating that a type (including return type) is asynchronous:

func blurImage(image: UIImage, radius: Int) -> async UIImage {
  var blurredImage: async UIImage
  dispatch_async(globalQueue) {
    blurredImage = // expensive blur operation
  }
  return blurredImage
}

At the origin of an asynchronous value is something like GCD to actually execute the work on a different thread. (This is not the responsibility of the Future.)

An async type should largely just work like its ‘regular’ synchronous counterpart. The compiler would be able to generate code for methods on that type (e.g. hasPrefix(_:slight_smile: on an async string returns an async Bool) or methods (including operators) that take that type as a parameter (E.g. concatenating two async strings should be as easy as: string1 + string2).

To unwrap a Future, i.e. getting its value, for cases that do not accept asynchronous values (e.g. UI updates), there’d be a new ‘when let’ statement with a control flow that is similar to the guard statement:

when let image = blurImage(image) else {
  throw ImageBlurringFailed()
}

imageView.image = image

This is all very brief, but should give you an idea of my proposal. You can find more details in a blog post I wrote recently: http://www.thomasvisser.me/2015/11/26/async-swift/. This description is probably rather simplistic compared to the work needed to actually support something like this, but I hope it can serve as a starting point.

Context:
- There is a proposal by Nadav Rotem from September 2015 that describes a lot of the foundational work that would have to be done before we can have a safe Future type: https://github.com/apple/swift/blob/5eaa3c43d069d5bd401e7879b43f6290823d180d/docs/proposals/Concurrency.rst. While I think a Future type could exist without language support for concurrency (e.g. using GCD directly, see https://github.com/Thomvis/BrightFutures, full disclosure: I wrote that library), I agree it makes a lot of sense to only do it once it can be done in a safe way.
- There are other concepts that have to do with asynchronous programming such as channels/signals/observables (Rx) and goroutines. I think there’s a place & audience for more than one of these concepts in Swift, but these efforts should of course be coordinated.

I realize this is not in scope for Swift 3, but I’d love to get feedback from the community and hear how it can be incorporated in the discussion when concurrency will be on the agenda (for Swift 4 perhaps)?

Best,
Thomas Visser
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Thomas Visser) #3

I would much rather see the language adopt something more akin to C#’s Async-Await, where the compiler handles restructuring synchronous-looking code into something asynchronous and non-blocking.

Agreed. The ‘when let’ syntax in my ‘proposal’ is a play on C#’s await. I think it’d be great if the compiler could turn that into code that uses the Future type to achieve said asynchronous and non-blocking behavior.

Thomas