[Concurrency] A slightly different perspective

I've been following the conversations around Chris Lattner's intriguing
async/await proposal and would like to offer my own take. I feel that the
proposal as written is almost perfect. My suggestions for improving it are
not highly original -- I think they have all come up already -- but I'd
like to present them from my own perspective.

1. Fixing "queue confusion" *must* be part of this proposal. The key bit
of "magic" offered by async/await over continuation passing is that you're
working in a single scope. A single scope should execute on a single queue
unless the programmer explicitly requests otherwise. Queue hopping is a
surprising problem in a single scope, and one that there's currently no
adequate solution for.

Also consider error handling. In this code it's really hard to reason
about what queue the catch block will execute on! And what about the defer
block?

startSpinner()
defer { stopSpinner() }
do {
  try await doSomeWorkOnSomeQ()
  try await doSomeMoreWorkOnSomeOtherQ()
} catch {
  // Where am I?
}

Please, please, PLEASE don't force us to litter our business logic with
gobs of explicit queue-hopping code!

2. The proposal should include some basic coordination mechanism. The
argument against returning a Future every time `await` is called is
convincing, so my suggestion is to do it from `beginAsync`. The Future
returned should only be specified by protocol. The protocol can start with
minimal features -- perhaps just cancellation and progress. There should
be a way for programmers to specify their own, more featureful, types. (The
proposal mentions the idea of returning a Bool, which is perhaps the
least-featureful Future type imaginable. :slight_smile:

To make this a little more concrete. Let's look at how these proposals can
clean up Jakob Egger's excellent example of more realistic usage of async.
Here's the original "minimal" example that Jakob identified:

class ImageProcessingTask {
  var cancelled = false
  func process() async -> Image? { … }
}

var currentTask: ImageProcessingTask?

@IBAction func buttonDidClick(sender:AnyObject) {
  currentTask?.cancelled = true
  let task = ImageProcessingTask()
  currentTask = task
  beginAsync {
    guard let image = await task.process() else { return }
    DispatchQueue.main.async {
      guard task.cancelled == false else { return }
      imageView.image = image
    }
  }
}

Here's how it could look with the changes I'm proposing. I will start with
the bare code to demonstrate the clarity, then add some comments to explain
some subtle points:

func processImage() async -> Image? { ... }
weak var currentTask: Future<Void>?

@IBAction func buttonDidClick(sender:AnyObject) {
    currentTask?.cancel()
    currentTask = beginAsync { task in
        guard let image = await processImage() else { return }
        if !task.isCancelled {
            imageView.image = image
        }
    }
}

IMHO this is a very clean expression of the intended behavior. Each line
is business logic -- there is no extraneous code in service of the
execution model. Here's the annotated version:

func processImage() async -> Image? { ... }
// If ARC can be taught to keep an executing future alive we can
// make this weak and not worry about cleanup.  See below.
weak var currentTask: Future<Void>?

@IBAction func buttonDidClick(sender:AnyObject) {
    currentTask?.cancel()
    // `beginAsync` creates a future, returns it, and passes it into
    // the closure's body. Can use beginAsync(MyCustomFutureType)
    // for your own future type
    currentTask = beginAsync { task in
        /* You can imagine there's implicitly code like this here
        task.retain();  defer { task.release() }
        */
        guard let image = await processImage() else { return }
        // Guaranteed back on the main queue here w/o boilerplate.
        if !task.isCancelled {
            imageView.image = image
        }
    }
}

Note that if `beginAsync` is spelled `async` (which I do like) this idea
dovetails nicely with the proposal to use `async` in place of `await` to
get "future-lite" behavior. My personal belief is that there's no such
thing as "future-lite" and the ideas are largely equivalent.

This got pretty long so thanks for reading! I look forward to hearing your
comments.

Thanks,
-n8

···

--
Functional Programmer, iOS Developer, Surfs Poorly
http://twitter.com/n8gray

You're not the only one to make these observations. What do you think about the modifications I just posed here?

https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170828/039349.html

-Joe

···

On Aug 31, 2017, at 3:04 PM, Nathan Gray via swift-evolution <swift-evolution@swift.org> wrote:

I've been following the conversations around Chris Lattner's intriguing async/await proposal and would like to offer my own take. I feel that the proposal as written is almost perfect. My suggestions for improving it are not highly original -- I think they have all come up already -- but I'd like to present them from my own perspective.

1. Fixing "queue confusion" *must* be part of this proposal. The key bit of "magic" offered by async/await over continuation passing is that you're working in a single scope. A single scope should execute on a single queue unless the programmer explicitly requests otherwise. Queue hopping is a surprising problem in a single scope, and one that there's currently no adequate solution for.

Also consider error handling. In this code it's really hard to reason about what queue the catch block will execute on! And what about the defer block?

startSpinner()
defer { stopSpinner() }
do {
  try await doSomeWorkOnSomeQ()
  try await doSomeMoreWorkOnSomeOtherQ()
} catch {
  // Where am I?
}

Please, please, PLEASE don't force us to litter our business logic with gobs of explicit queue-hopping code!

2. The proposal should include some basic coordination mechanism. The argument against returning a Future every time `await` is called is convincing, so my suggestion is to do it from `beginAsync`. The Future returned should only be specified by protocol. The protocol can start with minimal features -- perhaps just cancellation and progress. There should be a way for programmers to specify their own, more featureful, types. (The proposal mentions the idea of returning a Bool, which is perhaps the least-featureful Future type imaginable. :slight_smile:

To make this a little more concrete. Let's look at how these proposals can clean up Jakob Egger's excellent example of more realistic usage of async. Here's the original "minimal" example that Jakob identified:

class ImageProcessingTask {
  var cancelled = false
  func process() async -> Image? { … }
}

var currentTask: ImageProcessingTask?

@IBAction func buttonDidClick(sender:AnyObject) {
  currentTask?.cancelled = true
  let task = ImageProcessingTask()
  currentTask = task
  beginAsync {
    guard let image = await task.process() else { return }
    DispatchQueue.main.async {
      guard task.cancelled == false else { return }
      imageView.image = image
    }
  }
}

Here's how it could look with the changes I'm proposing. I will start with the bare code to demonstrate the clarity, then add some comments to explain some subtle points:

func processImage() async -> Image? { ... }
weak var currentTask: Future<Void>?

@IBAction func buttonDidClick(sender:AnyObject) {
    currentTask?.cancel()
    currentTask = beginAsync { task in
        guard let image = await processImage() else { return }
        if !task.isCancelled {
            imageView.image = image
        }
    }
}

IMHO this is a very clean expression of the intended behavior. Each line is business logic -- there is no extraneous code in service of the execution model. Here's the annotated version:

func processImage() async -> Image? { ... }
// If ARC can be taught to keep an executing future alive we can 
// make this weak and not worry about cleanup.  See below.
weak var currentTask: Future<Void>?

@IBAction func buttonDidClick(sender:AnyObject) {
    currentTask?.cancel()
    // `beginAsync` creates a future, returns it, and passes it into
    // the closure's body. Can use beginAsync(MyCustomFutureType) 
    // for your own future type
    currentTask = beginAsync { task in
        /* You can imagine there's implicitly code like this here
        task.retain();  defer { task.release() }
        */
        guard let image = await processImage() else { return }
        // Guaranteed back on the main queue here w/o boilerplate.
        if !task.isCancelled {
            imageView.image = image
        }
    }
}

Note that if `beginAsync` is spelled `async` (which I do like) this idea dovetails nicely with the proposal to use `async` in place of `await` to get "future-lite" behavior. My personal belief is that there's no such thing as "future-lite" and the ideas are largely equivalent.

This got pretty long so thanks for reading! I look forward to hearing your comments.

In my understanding, this is the advantage of using Actors, as calling `await` on actor functions is guaranteed to continue on the queue of the calling actor.

Only if you call „legacy“ non-actor async functions, you have to take care of queue hopping by yourself - as you do today.

···

Am 01.09.2017 um 00:04 schrieb Nathan Gray via swift-evolution <swift-evolution@swift.org>:

1. Fixing "queue confusion" *must* be part of this proposal. The key bit of "magic" offered by async/await over continuation passing is that you're working in a single scope. A single scope should execute on a single queue unless the programmer explicitly requests otherwise. Queue hopping is a surprising problem in a single scope, and one that there's currently no adequate solution for.

Also consider error handling. In this code it's really hard to reason about what queue the catch block will execute on! And what about the defer block?

startSpinner()
defer { stopSpinner() }
do {
  try await doSomeWorkOnSomeQ()
  try await doSomeMoreWorkOnSomeOtherQ()
} catch {
  // Where am I?
}

Please, please, PLEASE don't force us to litter our business logic with gobs of explicit queue-hopping code!

I've been following the conversations around Chris Lattner's intriguing async/await proposal and would like to offer my own take. I feel that the proposal as written is almost perfect. My suggestions for improving it are not highly original -- I think they have all come up already -- but I'd like to present them from my own perspective.

1. Fixing "queue confusion" *must* be part of this proposal. The key bit of "magic" offered by async/await over continuation passing is that you're working in a single scope. A single scope should execute on a single queue unless the programmer explicitly requests otherwise. Queue hopping is a surprising problem in a single scope, and one that there's currently no adequate solution for.

As mentioned downthread, the “contextualizing” thread is one way to address this.

2. The proposal should include some basic coordination mechanism. The argument against returning a Future every time `await` is called is convincing, so my suggestion is to do it from `beginAsync`. The Future returned should only be specified by protocol. The protocol can start with minimal features -- perhaps just cancellation and progress. There should be a way for programmers to specify their own, more featureful, types. (The proposal mentions the idea of returning a Bool, which is perhaps the least-featureful Future type imaginable. :slight_smile:

Please don’t read too much into the beginAsync API. It is merely a strawman, and intended to be a low-level API that higher level abstractions (like a decent futures API) can be built on top of. I think it is important to have some sort of primitive low-level API that is independent of higher level abstractions like Futures.

This is all a way of saying “yes, having something like you propose makes sense” but that it should be part of the Futures API, which is outside the scope of the async/await proposal.

-Chris

···

On Aug 31, 2017, at 3:04 PM, Nathan Gray via swift-evolution <swift-evolution@swift.org> wrote:

That's the second time today somebody has responded to one of my messages
before I posted it. Glitch in the Matrix?

I'll post a more detailed response later on but the tl;dr is that I think
you're right on target. My first impression is that both of my major
concerns could be solved and it would enable us to arrive at the "clean"
version of the minimal example. Very nice!

···

On Thu, Aug 31, 2017 at 3:36 PM, Joe Groff <jgroff@apple.com> wrote:

On Aug 31, 2017, at 3:04 PM, Nathan Gray via swift-evolution < > swift-evolution@swift.org> wrote:

I've been following the conversations around Chris Lattner's intriguing
async/await proposal and would like to offer my own take. I feel that the
proposal as written is almost perfect. My suggestions for improving it are
not highly original -- I think they have all come up already -- but I'd
like to present them from my own perspective.

1. Fixing "queue confusion" *must* be part of this proposal. The key bit
of "magic" offered by async/await over continuation passing is that you're
working in a single scope. A single scope should execute on a single queue
unless the programmer explicitly requests otherwise. Queue hopping is a
surprising problem in a single scope, and one that there's currently no
adequate solution for.

Also consider error handling. In this code it's really hard to reason
about what queue the catch block will execute on! And what about the defer
block?

startSpinner()
defer { stopSpinner() }
do {
  try await doSomeWorkOnSomeQ()
  try await doSomeMoreWorkOnSomeOtherQ()
} catch {
  // Where am I?
}

Please, please, PLEASE don't force us to litter our business logic with
gobs of explicit queue-hopping code!

2. The proposal should include some basic coordination mechanism. The
argument against returning a Future every time `await` is called is
convincing, so my suggestion is to do it from `beginAsync`. The Future
returned should only be specified by protocol. The protocol can start with
minimal features -- perhaps just cancellation and progress. There should
be a way for programmers to specify their own, more featureful, types. (The
proposal mentions the idea of returning a Bool, which is perhaps the
least-featureful Future type imaginable. :slight_smile:

To make this a little more concrete. Let's look at how these proposals
can clean up Jakob Egger's excellent example of more realistic usage of
async. Here's the original "minimal" example that Jakob identified:

class ImageProcessingTask {
  var cancelled = false
  func process() async -> Image? { … }
}

var currentTask: ImageProcessingTask?

@IBAction func buttonDidClick(sender:AnyObject) {
  currentTask?.cancelled = true
  let task = ImageProcessingTask()
  currentTask = task
  beginAsync {
    guard let image = await task.process() else { return }
    DispatchQueue.main.async {
      guard task.cancelled == false else { return }
      imageView.image = image
    }
  }
}

Here's how it could look with the changes I'm proposing. I will start
with the bare code to demonstrate the clarity, then add some comments to
explain some subtle points:

func processImage() async -> Image? { ... }
weak var currentTask: Future<Void>?

@IBAction func buttonDidClick(sender:AnyObject) {
    currentTask?.cancel()
    currentTask = beginAsync { task in
        guard let image = await processImage() else { return }
        if !task.isCancelled {
            imageView.image = image
        }
    }
}

IMHO this is a very clean expression of the intended behavior. Each line
is business logic -- there is no extraneous code in service of the
execution model. Here's the annotated version:

func processImage() async -> Image? { ... }
// If ARC can be taught to keep an executing future alive we can
// make this weak and not worry about cleanup.  See below.
weak var currentTask: Future<Void>?

@IBAction func buttonDidClick(sender:AnyObject) {
    currentTask?.cancel()
    // `beginAsync` creates a future, returns it, and passes it into
    // the closure's body. Can use beginAsync(MyCustomFutureType)
    // for your own future type
    currentTask = beginAsync { task in
        /* You can imagine there's implicitly code like this here
        task.retain();  defer { task.release() }
        */
        guard let image = await processImage() else { return }
        // Guaranteed back on the main queue here w/o boilerplate.
        if !task.isCancelled {
            imageView.image = image
        }
    }
}

Note that if `beginAsync` is spelled `async` (which I do like) this idea
dovetails nicely with the proposal to use `async` in place of `await` to
get "future-lite" behavior. My personal belief is that there's no such
thing as "future-lite" and the ideas are largely equivalent.

This got pretty long so thanks for reading! I look forward to hearing
your comments.

You're not the only one to make these observations. What do you think
about the modifications I just posed here?

https://lists.swift.org/pipermail/swift-evolution/
Week-of-Mon-20170828/039349.html

-Joe

--
Functional Programmer, iOS Developer, Surfs Poorly
http://twitter.com/n8gray

It's true that actors would require a solution to the queue confusion problem, but not every program will want to use actors. My position is that the syntax of async/await demands a solution with or without actors. If we're ripping open the walls anyway let's fix the plumbing!

···

On Aug 31, 2017, at 3:31 PM, Marc Schlichte <marc.schlichte@googlemail.com> wrote:

Am 01.09.2017 um 00:04 schrieb Nathan Gray via swift-evolution <swift-evolution@swift.org>:

1. Fixing "queue confusion" *must* be part of this proposal. The key bit of "magic" offered by async/await over continuation passing is that you're working in a single scope. A single scope should execute on a single queue unless the programmer explicitly requests otherwise. Queue hopping is a surprising problem in a single scope, and one that there's currently no adequate solution for.

Also consider error handling. In this code it's really hard to reason about what queue the catch block will execute on! And what about the defer block?

startSpinner()
defer { stopSpinner() }
do {
  try await doSomeWorkOnSomeQ()
  try await doSomeMoreWorkOnSomeOtherQ()
} catch {
  // Where am I?
}

Please, please, PLEASE don't force us to litter our business logic with gobs of explicit queue-hopping code!

In my understanding, this is the advantage of using Actors, as calling `await` on actor functions is guaranteed to continue on the queue of the calling actor.

Only if you call „legacy“ non-actor async functions, you have to take care of queue hopping by yourself - as you do today.

I've been following the conversations around Chris Lattner's intriguing async/await proposal and would like to offer my own take. I feel that the proposal as written is almost perfect. My suggestions for improving it are not highly original -- I think they have all come up already -- but I'd like to present them from my own perspective.

1. Fixing "queue confusion" *must* be part of this proposal. The key bit of "magic" offered by async/await over continuation passing is that you're working in a single scope. A single scope should execute on a single queue unless the programmer explicitly requests otherwise. Queue hopping is a surprising problem in a single scope, and one that there's currently no adequate solution for.

As mentioned downthread, the “contextualizing” thread is one way to address this.

2. The proposal should include some basic coordination mechanism. The argument against returning a Future every time `await` is called is convincing, so my suggestion is to do it from `beginAsync`. The Future returned should only be specified by protocol. The protocol can start with minimal features -- perhaps just cancellation and progress. There should be a way for programmers to specify their own, more featureful, types. (The proposal mentions the idea of returning a Bool, which is perhaps the least-featureful Future type imaginable. :slight_smile:

Please don’t read too much into the beginAsync API. It is merely a strawman, and intended to be a low-level API that higher level abstractions (like a decent futures API) can be built on top of. I think it is important to have some sort of primitive low-level API that is independent of higher level abstractions like Futures.

This is all a way of saying “yes, having something like you propose makes sense” but that it should be part of the Futures API, which is outside the scope of the async/await proposal.

But it would be nice for all high-level APIs that conform to a Awaitable protocol to be used with await without having to reach for a get property or something similar everytime.

···

On 2 Sep 2017, at 20:24, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:
On Aug 31, 2017, at 3:04 PM, Nathan Gray via swift-evolution <swift-evolution@swift.org> wrote:

-Chris

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

No,

as I see it, async / await without actors should behave the same way as if you pass continuations instead - especially if we auto convert CPS style functions into await/async style, the queueing behavior must not change IMHO.

Cheers
Marc

···

Von: swift-evolution@swift.org

Gesendet: 1. September 2017 9:10 vorm.

An: marc.schlichte@googlemail.com

Antworten: n8gray@n8gray.org

Cc: swift-evolution@swift.org

Betreff: Re: [swift-evolution] [Concurrency] A slightly different perspective

On Aug 31, 2017, at 3:31 PM, Marc Schlichte marc.schlichte@googlemail.com wrote:

Am 01.09.2017 um 00:04 schrieb Nathan Gray via swift-evolution swift-evolution@swift.org:

  1. Fixing “queue confusion” must be part of this proposal. The key bit of “magic” offered by async/await over continuation passing is that you’re working in a single scope. A single scope should execute on a single queue unless the programmer explicitly requests otherwise. Queue hopping is a surprising problem in a single scope, and one that there’s currently no adequate solution for.

Also consider error handling. In this code it’s really hard to reason about what queue the catch block will execute on! And what about the defer block?

startSpinner()

defer { stopSpinner() }

do {

try await doSomeWorkOnSomeQ()

try await doSomeMoreWorkOnSomeOtherQ()

} catch {

// Where am I?

}

Please, please, PLEASE don’t force us to litter our business logic with gobs of explicit queue-hopping code!

In my understanding, this is the advantage of using Actors, as calling await on actor functions is guaranteed to continue on the queue of the calling actor.

Only if you call „legacy“ non-actor async functions, you have to take care of queue hopping by yourself - as you do today.

It’s true that actors would require a solution to the queue confusion problem, but not every program will want to use actors. My position is that the syntax of async/await demands a solution with or without actors. If we’re ripping open the walls anyway let’s fix the plumbing!

The futures API that is outlined in the proposal is just an example, it isn’t a concrete pitch for a specific API. There are a bunch of improvements that can (and should) be made to it, it is just that a futures API should be the subject of a follow-on proposal to the basic async/await mechanics.

-Chris

···

On Sep 3, 2017, at 4:00 AM, David Hart <david@hartbit.com> wrote:

Please don’t read too much into the beginAsync API. It is merely a strawman, and intended to be a low-level API that higher level abstractions (like a decent futures API) can be built on top of. I think it is important to have some sort of primitive low-level API that is independent of higher level abstractions like Futures.

This is all a way of saying “yes, having something like you propose makes sense” but that it should be part of the Futures API, which is outside the scope of the async/await proposal.

But it would be nice for all high-level APIs that conform to a Awaitable protocol to be used with await without having to reach for a get property or something similar everytime.

Would it be possible to have the manifesto be a series of proposals then? I really think it is important for us to look at how all of these things fit together. I agree that async/await should come first, but looking at how concrete things like Futures would work may help to inform the design of async/await. We should do the back-propigation in our design before anything is locked in...

The thing I would most like to see as a quick follow-on to async/await is the ability to use the ‘async’ keyword to defer ‘await’. This feels very natural, is highly optimizable by the compiler, and it allows for a lot of very common use-cases which are not covered well by pure async/await. I think it would have a large impact on the eventual design/implementation of futures (and at least some impact on the design of async/await).

Thanks,
Jon

···

On Sep 3, 2017, at 9:04 AM, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

On Sep 3, 2017, at 4:00 AM, David Hart <david@hartbit.com <mailto:david@hartbit.com>> wrote:

Please don’t read too much into the beginAsync API. It is merely a strawman, and intended to be a low-level API that higher level abstractions (like a decent futures API) can be built on top of. I think it is important to have some sort of primitive low-level API that is independent of higher level abstractions like Futures.

This is all a way of saying “yes, having something like you propose makes sense” but that it should be part of the Futures API, which is outside the scope of the async/await proposal.

But it would be nice for all high-level APIs that conform to a Awaitable protocol to be used with await without having to reach for a get property or something similar everytime.

The futures API that is outlined in the proposal is just an example, it isn’t a concrete pitch for a specific API. There are a bunch of improvements that can (and should) be made to it, it is just that a futures API should be the subject of a follow-on proposal to the basic async/await mechanics.

Please don’t read too much into the beginAsync API. It is merely a strawman, and intended to be a low-level API that higher level abstractions (like a decent futures API) can be built on top of. I think it is important to have some sort of primitive low-level API that is independent of higher level abstractions like Futures.

This is all a way of saying “yes, having something like you propose makes sense” but that it should be part of the Futures API, which is outside the scope of the async/await proposal.

But it would be nice for all high-level APIs that conform to a Awaitable protocol to be used with await without having to reach for a get property or something similar everytime.

The futures API that is outlined in the proposal is just an example, it isn’t a concrete pitch for a specific API. There are a bunch of improvements that can (and should) be made to it, it is just that a futures API should be the subject of a follow-on proposal to the basic async/await mechanics.

Of course, but perhaps an Awaitable protocol could be part of the proposal, no? In that case, even this proposal does end up being accepted and implemented for Swift 5, but a Promise proposal does not, a protocol would be available for third party futures and promises to blend well with await.

···

On 3 Sep 2017, at 18:03, Chris Lattner <clattner@nondot.org> wrote:

On Sep 3, 2017, at 4:00 AM, David Hart <david@hartbit.com> wrote:

-Chris

No,

as I see it, async / await without actors should behave the same way as if you pass continuations instead - especially if we auto convert CPS style functions into await/async style, the queueing behavior must not change IMHO.

Ok, then I guess we just disagree. I don't see any need for the new syntax to have the same semantics, since you can still use the CPS syntax if you want the old behavior.

If all async/await does is give us a slightly prettier way to write the same grungy code I'll be very disappointed.

···

Sent from my iPad

On Sep 1, 2017, at 12:44 AM, Marc Schlichte <marc.schlichte@googlemail.com> wrote:

Jonathan,

You've mentioned the desire to have 'async' defer calling 'await', but I haven't seen a detailed design yet. For example, is the following code valid?

  let image = async fetchImage()
  let image2 = async fetchImage()
  let deferredThings = [image1, image2]

If so, what is the type of 'deferredThings'? And how does it not count as 'using' the values.

If the above code is not valid, how is this situation better than the suggested use of a Future type to allow concurrent async requests?

  let future1 = Future { await fetchImage() }
  let future2 = Future { await fetchImage() }
  let deferredThings = [future1, future2]

Note that in this example, 'deferredThings' has a concrete type, and we can inspect its values.

You keep bringing up this suggestion, so I must be missing something, but it seems to me that your suggestion is covered by Futures. Why is calling with 'async' better?

-BJ

···

On Sep 3, 2017, at 6:01 PM, Jonathan Hull via swift-evolution <swift-evolution@swift.org> wrote:

On Sep 3, 2017, at 9:04 AM, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

On Sep 3, 2017, at 4:00 AM, David Hart <david@hartbit.com> wrote:
Please don’t read too much into the beginAsync API. It is merely a strawman, and intended to be a low-level API that higher level abstractions (like a decent futures API) can be built on top of. I think it is important to have some sort of primitive low-level API that is independent of higher level abstractions like Futures.

This is all a way of saying “yes, having something like you propose makes sense” but that it should be part of the Futures API, which is outside the scope of the async/await proposal.

But it would be nice for all high-level APIs that conform to a Awaitable protocol to be used with await without having to reach for a get property or something similar everytime.

The futures API that is outlined in the proposal is just an example, it isn’t a concrete pitch for a specific API. There are a bunch of improvements that can (and should) be made to it, it is just that a futures API should be the subject of a follow-on proposal to the basic async/await mechanics.

Would it be possible to have the manifesto be a series of proposals then? I really think it is important for us to look at how all of these things fit together. I agree that async/await should come first, but looking at how concrete things like Futures would work may help to inform the design of async/await. We should do the back-propigation in our design before anything is locked in...

The thing I would most like to see as a quick follow-on to async/await is the ability to use the ‘async’ keyword to defer ‘await’. This feels very natural, is highly optimizable by the compiler, and it allows for a lot of very common use-cases which are not covered well by pure async/await. I think it would have a large impact on the eventual design/implementation of futures (and at least some impact on the design of async/await).

Thanks,
Jon
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Please don’t read too much into the beginAsync API. It is merely a strawman, and intended to be a low-level API that higher level abstractions (like a decent futures API) can be built on top of. I think it is important to have some sort of primitive low-level API that is independent of higher level abstractions like Futures.

This is all a way of saying “yes, having something like you propose makes sense” but that it should be part of the Futures API, which is outside the scope of the async/await proposal.

But it would be nice for all high-level APIs that conform to a Awaitable protocol to be used with await without having to reach for a get property or something similar everytime.

The futures API that is outlined in the proposal is just an example, it isn’t a concrete pitch for a specific API. There are a bunch of improvements that can (and should) be made to it, it is just that a futures API should be the subject of a follow-on proposal to the basic async/await mechanics.

Would it be possible to have the manifesto be a series of proposals then? I really think it is important for us to look at how all of these things fit together. I agree that async/await should come first, but looking at how concrete things like Futures would work may help to inform the design of async/await. We should do the back-propigation in our design before anything is locked in…

Sure, that would be great. I don’t have time to write up a futures proposal myself, but I’d be happy to contribute editorial advice to someone (or some group) who wants to do so.

The thing I would most like to see as a quick follow-on to async/await is the ability to use the ‘async’ keyword to defer ‘await’.

This keeps coming up, and is certainly possible (within the scope of the swift grammar) but a decision like this should be driven by a specific cost/benefit tradeoff. That tradeoff decision can only be made when the futures API has a specific proposal that people generally agree to.

Because of this, I think that the Futures proposal should be a *library only* proposal on top of the async/await language stuff, and them mention something like “async foo()” as a possible future extension - the subject of its own proposal.

-Chris

···

On Sep 3, 2017, at 5:01 PM, Jonathan Hull <jhull@gbis.com> wrote:

On Sep 3, 2017, at 9:04 AM, Chris Lattner via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Sep 3, 2017, at 4:00 AM, David Hart <david@hartbit.com <mailto:david@hartbit.com>> wrote:

Jonathan,

You've mentioned the desire to have 'async' defer calling 'await', but I haven't seen a detailed design yet.

Oh, we discussed it awhile back on a few other threads. I am happy to help write up a more formal/detailed design if there is enough interest...

For example, is the following code valid?

  let image = async fetchImage()
  let image2 = async fetchImage()
  let deferredThings = [image1, image2]

If so, what is the type of 'deferredThings'? And how does it not count as 'using' the values.

No this code is not valid. You would need to ‘await’ both image 1 & 2 before they could be put in an array or transferred to another variable. You could combine the ‘await’s though (similar to try):

  let image = async fetchImage()
  let image2 = async fetchImage()
  let deferredThings = await [image1, image2]

Note: You can return something which is deferred from an async function without awaiting though...

If the above code is not valid, how is this situation better than the suggested use of a Future type to allow concurrent async requests?

  let future1 = Future { await fetchImage() }
  let future2 = Future { await fetchImage() }
  let deferredThings = [future1, future2]

Note that in this example, 'deferredThings' has a concrete type, and we can inspect its values.

It isn’t meant to be used instead of Futures (though you may not need to reach for them as often), it is a much lower-level construct which would be used as a building block for things like futures (and other concurrency constructs).

Because there is no way for the programmer to get at the thing being ‘await’ed (or any representation of it) without awaiting on it, and it can’t escape the context, it gives the compiler some extra guarantees that it can use to optimize things behind the scenes. Even if the compiler just ends up creating a future behind the scenes, that implementation is completely hidden to the programmer, and can be updated/changed at any time by the compiler team without involving evolution.

Let’s say at some point down the road as part of the actor work, the compiler ends up getting some visibility into how actor queues will be coalesced… Then it can use that knowledge to reorganize the code above to be more efficient. For example, if it knows that the part of the calls to fetchImage() will be serialized by an actor, it can just make that part synchronous and avoid the asynchronous overhead. If we had baked in everything at the low-level to use future objects, we wouldn’t be able to make that optimization.

There are 2 big results from this:

1) It gives us the ability to easily start several things behind the scenes and then await the group of them finishing

2) Our implementation of Futures (and other constructs) can potentially take advantage of the compiler optimizations by using this beneath the hood.

The first point is huge for me. This is an extremely common thing to want in real-world code, but it usually requires a bunch of complex (sometimes error-prone) machinery to actually pull off. There is often wasted overhead as well. I like that I can do it here in a very natural and lightweight way. (Futures also simplify this problem a great deal, but I wouldn’t really call them lightweight).

You keep bringing up this suggestion, so I must be missing something, but it seems to me that your suggestion is covered by Futures. Why is calling with 'async' better?

As I said above, I think this will help our Futures be much more efficient when we build them, but there are also some advantages of having this as an option and not just using Futures for everything.

Mainly, futures require a different return type, and they require boxing/unboxing. To chain futures, each link in the chain has to be future-aware… and each link can only be used with futures (i.e. the return type is Future<MyType>). There are a lot of cases where I am boxing just to unbox and re-box. Most of that is fine as long as the framework was built with it in mind, but it doesn’t work as well if I want to add the functionality post-hoc.

With ‘async’ being used to defer ‘await’, it just works on any function which can be awaited. Further, it can be chained naturally through any number of async functions. I showed this in another thread, but if you think through what happens when you return something which still needs to be awaited, you see that that state is naturally passed along with it (the function was either called with ‘await’ or ‘async’, and thus the return value is either awaited on, or the result picks up the compiler marking from ‘async’ showing it still needs to be awaited). Because there is no (visible) boxing, you don’t have to worry about monads, etc…

It basically nests in the same way that try/throws nests, and we already have to do that for async/await anyway… so it is a lot of win for very little additional complexity (at least on the usage side).

Thanks,
Jon

···

On Sep 3, 2017, at 7:35 PM, BJ Homer <bjhomer@gmail.com> wrote:

-BJ

On Sep 3, 2017, at 6:01 PM, Jonathan Hull via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Sep 3, 2017, at 9:04 AM, Chris Lattner via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Sep 3, 2017, at 4:00 AM, David Hart <david@hartbit.com <mailto:david@hartbit.com>> wrote:

Please don’t read too much into the beginAsync API. It is merely a strawman, and intended to be a low-level API that higher level abstractions (like a decent futures API) can be built on top of. I think it is important to have some sort of primitive low-level API that is independent of higher level abstractions like Futures.

This is all a way of saying “yes, having something like you propose makes sense” but that it should be part of the Futures API, which is outside the scope of the async/await proposal.

But it would be nice for all high-level APIs that conform to a Awaitable protocol to be used with await without having to reach for a get property or something similar everytime.

The futures API that is outlined in the proposal is just an example, it isn’t a concrete pitch for a specific API. There are a bunch of improvements that can (and should) be made to it, it is just that a futures API should be the subject of a follow-on proposal to the basic async/await mechanics.

Would it be possible to have the manifesto be a series of proposals then? I really think it is important for us to look at how all of these things fit together. I agree that async/await should come first, but looking at how concrete things like Futures would work may help to inform the design of async/await. We should do the back-propigation in our design before anything is locked in...

The thing I would most like to see as a quick follow-on to async/await is the ability to use the ‘async’ keyword to defer ‘await’. This feels very natural, is highly optimizable by the compiler, and it allows for a lot of very common use-cases which are not covered well by pure async/await. I think it would have a large impact on the eventual design/implementation of futures (and at least some impact on the design of async/await).

Thanks,
Jon
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

Okay, that's an interesting proposal. I'm not qualified to judge how big of a win the compiler optimizations would be there; I'm a little skeptical, but I admit a lack of expertise there. It seems like it's not essential to the core 'async/await' proposal, though; it could be added later with no source breakage, and in the meantime we can accomplish most of the same behavior through an explicit Future type. I think it makes more sense to consider the base proposal before adding more complexity to it.

-BJ

···

On Sep 3, 2017, at 10:15 PM, Jonathan Hull <jhull@gbis.com> wrote:

On Sep 3, 2017, at 7:35 PM, BJ Homer <bjhomer@gmail.com> wrote:

Jonathan,

You've mentioned the desire to have 'async' defer calling 'await', but I haven't seen a detailed design yet.

Oh, we discussed it awhile back on a few other threads. I am happy to help write up a more formal/detailed design if there is enough interest...

For example, is the following code valid?

  let image = async fetchImage()
  let image2 = async fetchImage()
  let deferredThings = [image1, image2]

If so, what is the type of 'deferredThings'? And how does it not count as 'using' the values.

No this code is not valid. You would need to ‘await’ both image 1 & 2 before they could be put in an array or transferred to another variable. You could combine the ‘await’s though (similar to try):

  let image = async fetchImage()
  let image2 = async fetchImage()
  let deferredThings = await [image1, image2]

Note: You can return something which is deferred from an async function without awaiting though...

If the above code is not valid, how is this situation better than the suggested use of a Future type to allow concurrent async requests?

  let future1 = Future { await fetchImage() }
  let future2 = Future { await fetchImage() }
  let deferredThings = [future1, future2]

Note that in this example, 'deferredThings' has a concrete type, and we can inspect its values.

It isn’t meant to be used instead of Futures (though you may not need to reach for them as often), it is a much lower-level construct which would be used as a building block for things like futures (and other concurrency constructs).

Because there is no way for the programmer to get at the thing being ‘await’ed (or any representation of it) without awaiting on it, and it can’t escape the context, it gives the compiler some extra guarantees that it can use to optimize things behind the scenes. Even if the compiler just ends up creating a future behind the scenes, that implementation is completely hidden to the programmer, and can be updated/changed at any time by the compiler team without involving evolution.

Let’s say at some point down the road as part of the actor work, the compiler ends up getting some visibility into how actor queues will be coalesced… Then it can use that knowledge to reorganize the code above to be more efficient. For example, if it knows that the part of the calls to fetchImage() will be serialized by an actor, it can just make that part synchronous and avoid the asynchronous overhead. If we had baked in everything at the low-level to use future objects, we wouldn’t be able to make that optimization.

There are 2 big results from this:

1) It gives us the ability to easily start several things behind the scenes and then await the group of them finishing

2) Our implementation of Futures (and other constructs) can potentially take advantage of the compiler optimizations by using this beneath the hood.

The first point is huge for me. This is an extremely common thing to want in real-world code, but it usually requires a bunch of complex (sometimes error-prone) machinery to actually pull off. There is often wasted overhead as well. I like that I can do it here in a very natural and lightweight way. (Futures also simplify this problem a great deal, but I wouldn’t really call them lightweight).

You keep bringing up this suggestion, so I must be missing something, but it seems to me that your suggestion is covered by Futures. Why is calling with 'async' better?

As I said above, I think this will help our Futures be much more efficient when we build them, but there are also some advantages of having this as an option and not just using Futures for everything.

Mainly, futures require a different return type, and they require boxing/unboxing. To chain futures, each link in the chain has to be future-aware… and each link can only be used with futures (i.e. the return type is Future<MyType>). There are a lot of cases where I am boxing just to unbox and re-box. Most of that is fine as long as the framework was built with it in mind, but it doesn’t work as well if I want to add the functionality post-hoc.

With ‘async’ being used to defer ‘await’, it just works on any function which can be awaited. Further, it can be chained naturally through any number of async functions. I showed this in another thread, but if you think through what happens when you return something which still needs to be awaited, you see that that state is naturally passed along with it (the function was either called with ‘await’ or ‘async’, and thus the return value is either awaited on, or the result picks up the compiler marking from ‘async’ showing it still needs to be awaited). Because there is no (visible) boxing, you don’t have to worry about monads, etc…

It basically nests in the same way that try/throws nests, and we already have to do that for async/await anyway… so it is a lot of win for very little additional complexity (at least on the usage side).

Thanks,
Jon

-BJ

On Sep 3, 2017, at 6:01 PM, Jonathan Hull via swift-evolution <swift-evolution@swift.org> wrote:

On Sep 3, 2017, at 9:04 AM, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

On Sep 3, 2017, at 4:00 AM, David Hart <david@hartbit.com> wrote:
Please don’t read too much into the beginAsync API. It is merely a strawman, and intended to be a low-level API that higher level abstractions (like a decent futures API) can be built on top of. I think it is important to have some sort of primitive low-level API that is independent of higher level abstractions like Futures.

This is all a way of saying “yes, having something like you propose makes sense” but that it should be part of the Futures API, which is outside the scope of the async/await proposal.

But it would be nice for all high-level APIs that conform to a Awaitable protocol to be used with await without having to reach for a get property or something similar everytime.

The futures API that is outlined in the proposal is just an example, it isn’t a concrete pitch for a specific API. There are a bunch of improvements that can (and should) be made to it, it is just that a futures API should be the subject of a follow-on proposal to the basic async/await mechanics.

Would it be possible to have the manifesto be a series of proposals then? I really think it is important for us to look at how all of these things fit together. I agree that async/await should come first, but looking at how concrete things like Futures would work may help to inform the design of async/await. We should do the back-propigation in our design before anything is locked in...

The thing I would most like to see as a quick follow-on to async/await is the ability to use the ‘async’ keyword to defer ‘await’. This feels very natural, is highly optimizable by the compiler, and it allows for a lot of very common use-cases which are not covered well by pure async/await. I think it would have a large impact on the eventual design/implementation of futures (and at least some impact on the design of async/await).

Thanks,
Jon
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

I think we need to consider both the original proposal on its own, and also together with other potential proposals (including deferring ‘async’ and futures). We don’t need to have them fully spelled out, but I think the other proposals will help inform us where the sharp edges of await/async are. That was my original point on this thread.

I keep seeing “Let’s wait to consider X until after async/await” and I am saying “Let’s consider how X would affect the async/await proposal”. Better to figure out any design issues and back-propagation now than after we have baked things in.

Thanks,
Jon

···

On Sep 3, 2017, at 10:16 PM, BJ Homer <bjhomer@gmail.com> wrote:

Okay, that's an interesting proposal. I'm not qualified to judge how big of a win the compiler optimizations would be there; I'm a little skeptical, but I admit a lack of expertise there. It seems like it's not essential to the core 'async/await' proposal, though; it could be added later with no source breakage, and in the meantime we can accomplish most of the same behavior through an explicit Future type. I think it makes more sense to consider the base proposal before adding more complexity to it.

-BJ

On Sep 3, 2017, at 10:15 PM, Jonathan Hull <jhull@gbis.com <mailto:jhull@gbis.com>> wrote:

On Sep 3, 2017, at 7:35 PM, BJ Homer <bjhomer@gmail.com <mailto:bjhomer@gmail.com>> wrote:

Jonathan,

You've mentioned the desire to have 'async' defer calling 'await', but I haven't seen a detailed design yet.

Oh, we discussed it awhile back on a few other threads. I am happy to help write up a more formal/detailed design if there is enough interest...

For example, is the following code valid?

  let image = async fetchImage()
  let image2 = async fetchImage()
  let deferredThings = [image1, image2]

If so, what is the type of 'deferredThings'? And how does it not count as 'using' the values.

No this code is not valid. You would need to ‘await’ both image 1 & 2 before they could be put in an array or transferred to another variable. You could combine the ‘await’s though (similar to try):

  let image = async fetchImage()
  let image2 = async fetchImage()
  let deferredThings = await [image1, image2]

Note: You can return something which is deferred from an async function without awaiting though...

If the above code is not valid, how is this situation better than the suggested use of a Future type to allow concurrent async requests?

  let future1 = Future { await fetchImage() }
  let future2 = Future { await fetchImage() }
  let deferredThings = [future1, future2]

Note that in this example, 'deferredThings' has a concrete type, and we can inspect its values.

It isn’t meant to be used instead of Futures (though you may not need to reach for them as often), it is a much lower-level construct which would be used as a building block for things like futures (and other concurrency constructs).

Because there is no way for the programmer to get at the thing being ‘await’ed (or any representation of it) without awaiting on it, and it can’t escape the context, it gives the compiler some extra guarantees that it can use to optimize things behind the scenes. Even if the compiler just ends up creating a future behind the scenes, that implementation is completely hidden to the programmer, and can be updated/changed at any time by the compiler team without involving evolution.

Let’s say at some point down the road as part of the actor work, the compiler ends up getting some visibility into how actor queues will be coalesced… Then it can use that knowledge to reorganize the code above to be more efficient. For example, if it knows that the part of the calls to fetchImage() will be serialized by an actor, it can just make that part synchronous and avoid the asynchronous overhead. If we had baked in everything at the low-level to use future objects, we wouldn’t be able to make that optimization.

There are 2 big results from this:

1) It gives us the ability to easily start several things behind the scenes and then await the group of them finishing

2) Our implementation of Futures (and other constructs) can potentially take advantage of the compiler optimizations by using this beneath the hood.

The first point is huge for me. This is an extremely common thing to want in real-world code, but it usually requires a bunch of complex (sometimes error-prone) machinery to actually pull off. There is often wasted overhead as well. I like that I can do it here in a very natural and lightweight way. (Futures also simplify this problem a great deal, but I wouldn’t really call them lightweight).

You keep bringing up this suggestion, so I must be missing something, but it seems to me that your suggestion is covered by Futures. Why is calling with 'async' better?

As I said above, I think this will help our Futures be much more efficient when we build them, but there are also some advantages of having this as an option and not just using Futures for everything.

Mainly, futures require a different return type, and they require boxing/unboxing. To chain futures, each link in the chain has to be future-aware… and each link can only be used with futures (i.e. the return type is Future<MyType>). There are a lot of cases where I am boxing just to unbox and re-box. Most of that is fine as long as the framework was built with it in mind, but it doesn’t work as well if I want to add the functionality post-hoc.

With ‘async’ being used to defer ‘await’, it just works on any function which can be awaited. Further, it can be chained naturally through any number of async functions. I showed this in another thread, but if you think through what happens when you return something which still needs to be awaited, you see that that state is naturally passed along with it (the function was either called with ‘await’ or ‘async’, and thus the return value is either awaited on, or the result picks up the compiler marking from ‘async’ showing it still needs to be awaited). Because there is no (visible) boxing, you don’t have to worry about monads, etc…

It basically nests in the same way that try/throws nests, and we already have to do that for async/await anyway… so it is a lot of win for very little additional complexity (at least on the usage side).

Thanks,
Jon

-BJ

On Sep 3, 2017, at 6:01 PM, Jonathan Hull via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Sep 3, 2017, at 9:04 AM, Chris Lattner via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Sep 3, 2017, at 4:00 AM, David Hart <david@hartbit.com <mailto:david@hartbit.com>> wrote:

Please don’t read too much into the beginAsync API. It is merely a strawman, and intended to be a low-level API that higher level abstractions (like a decent futures API) can be built on top of. I think it is important to have some sort of primitive low-level API that is independent of higher level abstractions like Futures.

This is all a way of saying “yes, having something like you propose makes sense” but that it should be part of the Futures API, which is outside the scope of the async/await proposal.

But it would be nice for all high-level APIs that conform to a Awaitable protocol to be used with await without having to reach for a get property or something similar everytime.

The futures API that is outlined in the proposal is just an example, it isn’t a concrete pitch for a specific API. There are a bunch of improvements that can (and should) be made to it, it is just that a futures API should be the subject of a follow-on proposal to the basic async/await mechanics.

Would it be possible to have the manifesto be a series of proposals then? I really think it is important for us to look at how all of these things fit together. I agree that async/await should come first, but looking at how concrete things like Futures would work may help to inform the design of async/await. We should do the back-propigation in our design before anything is locked in...

The thing I would most like to see as a quick follow-on to async/await is the ability to use the ‘async’ keyword to defer ‘await’. This feels very natural, is highly optimizable by the compiler, and it allows for a lot of very common use-cases which are not covered well by pure async/await. I think it would have a large impact on the eventual design/implementation of futures (and at least some impact on the design of async/await).

Thanks,
Jon
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

Tanks Jonathan,

As you may already know, I am a great advocate of this feature. I will not
repeat my explanations and examples that I gave in the other threads. But I
think you got the idea right. Even without any kind of optimization, I
believe there is a clear gain in this approach.

But I think you got the idea right. Even without any kind of optimization,
I believe there is a clear gain.

It is extremely naive to believe that people do not need a basic
parallelism already in the first version. 100% of people will have to look
for third-party libraries, or create their custom versions on top of async
/ await already on the first day. Batch two (or more) things to do in the
same function is a pretty common pattern.

func processImageData1a() async -> Image {
  let dataResource = async loadWebResource("dataprofile.txt")
  let imageResource = async loadWebResource("imagedata.dat")

  // ... other stuff can go here to cover load latency...
  let imageTmp = await decodeImage(dataResource, imageResource) //
compiler error if await is not present.
  let imageResult = await dewarpAndCleanupImage(imageTmp)
  return imageResult
}

If this (or something like that) is not implemented, people will create
several versions to solve the same problem, so that later (Swift 6?) will
be solved (because people want this), and we will live with several bad
codes to maintain.

Since I do not think we should encourage building bad coding standards to
solve simple problems (and I've seen very bad few examples of this right
here), I think we should sort this out soon enough. Even without obvious
optimizations in the first version.

Future/Promises is another talk! Involve a lot of other concepts, and also
will be a shame to need the Future/Promises to solve this problem, because
this is something that we actually can do using GCD only, without involve
any other type/controlling mechanism. And also, can help build better
Future/Promises too.

···

Em seg, 4 de set de 2017 às 02:37, Jonathan Hull via swift-evolution < swift-evolution@swift.org> escreveu:

I think we need to consider both the original proposal on its own, and
also together with other potential proposals (including deferring ‘async’
and futures). We don’t need to have them fully spelled out, but I think the
other proposals will help inform us where the sharp edges of await/async
are. That was my original point on this thread.

I keep seeing “Let’s wait to consider X until after async/await” and I am
saying “Let’s consider how X would affect the async/await proposal”.
Better to figure out any design issues and back-propagation now than after
we have baked things in.

Thanks,
Jon

On Sep 3, 2017, at 10:16 PM, BJ Homer <bjhomer@gmail.com> wrote:

Okay, that's an interesting proposal. I'm not qualified to judge how big
of a win the compiler optimizations would be there; I'm a little skeptical,
but I admit a lack of expertise there. It seems like it's not essential to
the core 'async/await' proposal, though; it could be added later with no
source breakage, and in the meantime we can accomplish most of the same
behavior through an explicit Future type. I think it makes more sense to
consider the base proposal before adding more complexity to it.

-BJ

On Sep 3, 2017, at 10:15 PM, Jonathan Hull <jhull@gbis.com> wrote:

On Sep 3, 2017, at 7:35 PM, BJ Homer <bjhomer@gmail.com> wrote:

Jonathan,

You've mentioned the desire to have 'async' defer calling 'await', but I
haven't seen a detailed design yet.

Oh, we discussed it awhile back on a few other threads. I am happy to
help write up a more formal/detailed design if there is enough interest...

For example, is the following code valid?

  let image = async fetchImage()
  let image2 = async fetchImage()
  let deferredThings = [image1, image2]

If so, what is the type of 'deferredThings'? And how does it not count as
'using' the values.

No this code is not valid. You would need to ‘await’ both image 1 & 2
before they could be put in an array or transferred to another variable.
You could combine the ‘await’s though (similar to try):

let image = async fetchImage()
let image2 = async fetchImage()
let deferredThings = await [image1, image2]

Note: You can return something which is deferred from an async function
without awaiting though...

If the above code is not valid, how is this situation better than the
suggested use of a Future type to allow concurrent async requests?

  let future1 = Future { await fetchImage() }
  let future2 = Future { await fetchImage() }
  let deferredThings = [future1, future2]

Note that in this example, 'deferredThings' has a concrete type, and we
can inspect its values.

It isn’t meant to be used instead of Futures (though you may not need to
reach for them as often), it is a much lower-level construct which would be
used as a building block for things like futures (and other concurrency
constructs).

Because there is no way for the programmer to get at the thing being
‘await’ed (or any representation of it) without awaiting on it, and it
can’t escape the context, it gives the compiler some extra guarantees that
it can use to optimize things behind the scenes. Even if the compiler just
ends up creating a future behind the scenes, that implementation is
completely hidden to the programmer, and can be updated/changed at any time
by the compiler team without involving evolution.

Let’s say at some point down the road as part of the actor work, the
compiler ends up getting some visibility into how actor queues will be
coalesced… Then it can use that knowledge to reorganize the code above to
be more efficient. For example, if it knows that the part of the calls to
fetchImage() will be serialized by an actor, it can just make that part
synchronous and avoid the asynchronous overhead. If we had baked in
everything at the low-level to use future objects, we wouldn’t be able to
make that optimization.

There are 2 big results from this:

1) It gives us the ability to easily start several things behind the
scenes and then await the group of them finishing

2) Our implementation of Futures (and other constructs) can potentially
take advantage of the compiler optimizations by using this beneath the hood.

The first point is huge for me. This is an extremely common thing to want
in real-world code, but it usually requires a bunch of complex (sometimes
error-prone) machinery to actually pull off. There is often wasted overhead
as well. I like that I can do it here in a very natural and lightweight
way. (Futures also simplify this problem a great deal, but I wouldn’t
really call them lightweight).

You keep bringing up this suggestion, so I must be missing something, but
it seems to me that your suggestion is covered by Futures. Why is calling
with 'async' better?

As I said above, I think this will help our Futures be much more efficient
when we build them, but there are also some advantages of having this as an
option and not just using Futures for everything.

Mainly, futures require a different return type, and they require
boxing/unboxing. To chain futures, each link in the chain has to be
future-aware… and each link can only be used with futures (i.e. the return
type is Future<MyType>). There are a lot of cases where I am boxing just
to unbox and re-box. Most of that is fine as long as the framework was
built with it in mind, but it doesn’t work as well if I want to add the
functionality post-hoc.

With ‘async’ being used to defer ‘await’, it just works on any function
which can be awaited. Further, it can be chained naturally through any
number of async functions. I showed this in another thread, but if you
think through what happens when you return something which still needs to
be awaited, you see that that state is naturally passed along with it (the
function was either called with ‘await’ or ‘async’, and thus the return
value is either awaited on, or the result picks up the compiler marking
from ‘async’ showing it still needs to be awaited). Because there is no
(visible) boxing, you don’t have to worry about monads, etc…

It basically nests in the same way that try/throws nests, and we already
have to do that for async/await anyway… so it is a lot of win for very
little additional complexity (at least on the usage side).

Thanks,
Jon

-BJ

On Sep 3, 2017, at 6:01 PM, Jonathan Hull via swift-evolution < > swift-evolution@swift.org> wrote:

On Sep 3, 2017, at 9:04 AM, Chris Lattner via swift-evolution < > swift-evolution@swift.org> wrote:

On Sep 3, 2017, at 4:00 AM, David Hart <david@hartbit.com> wrote:

Please don’t read too much into the beginAsync API. It is merely a
strawman, and intended to be a low-level API that higher level abstractions
(like a decent futures API) can be built on top of. I think it is
important to have some sort of primitive low-level API that is independent
of higher level abstractions like Futures.

This is all a way of saying “yes, having something like you propose makes
sense” but that it should be part of the Futures API, which is outside the
scope of the async/await proposal.

But it would be nice for all high-level APIs that conform to a *Awaitable*
protocol to be used with *await* without having to reach for a *get* property
or something similar everytime.

The futures API that is outlined in the proposal is just an example, it
isn’t a concrete pitch for a specific API. There are a bunch of
improvements that can (and should) be made to it, it is just that a futures
API should be the subject of a follow-on proposal to the basic async/await
mechanics.

Would it be possible to have the manifesto be a series of proposals then?
I really think it is important for us to look at how all of these things
fit together. I agree that async/await should come first, but looking at
how concrete things like Futures would work may help to inform the design
of async/await. We should do the back-propigation in our design before
anything is locked in...

The thing I would most like to see as a quick follow-on to async/await is
the ability to use the ‘async’ keyword to defer ‘await’. This feels very
natural, is highly optimizable by the compiler, and it allows for a lot of
very common use-cases which are not covered well by pure async/await. I
think it would have a large impact on the eventual design/implementation of
futures (and at least some impact on the design of async/await).

Thanks,
Jon

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Just to be sure of what you are proposing, am I right to assume this would be compiled down to something like this?

func processImageData1a(completion: (Image) -> ()) {
  var dataResource: Resource? = nil
  var imageResource: Resource? = nil
  var finishedBody = false

  func continuation() {
    // only continue once everything is ready
    guard finishedBody else { return }
    guard dataResource = dataResource else { return }
    guard imageResource = imageResource else { return }

    // everything is ready now
    decodeImage(dataResource, imageResource) { imageTmp in
      dewarpAndCleanupImage(imageTmp) { imageResult in
        completion(imageResult)
      }
    }
  }

  loadWebResource("dataprofile.txt") { result in
    dataResource = result
    continuation()
  }
  loadWebResource("imagedata.dat") { result in
    imageResource = result
    continuation()
  }

  // ... other stuff can go here to cover load latency...

  finishedBody = true
  continuation()
}

This seems more lightweight than a future to me. I know I've used this pattern a few times. What I'm not sure about is how thrown errors would work. Surely you want error handling to work when loading resources from the web.

···

Le 4 sept. 2017 à 10:01, Wallacy via swift-evolution <swift-evolution@swift.org> a écrit :

func processImageData1a() async ->
Image {
  let dataResource = async loadWebResource("dataprofile.txt")
  let imageResource = async loadWebResource("imagedata.dat")
  
  // ... other stuff can go here to cover load latency...
  
  let imageTmp = await decodeImage(dataResource, imageResource) // compiler error if await is not present.
  let imageResult = await dewarpAndCleanupImage(imageTmp)
  return imageResult
}

If this (or something like that) is not implemented, people will create several versions to solve the same problem, so that later (Swift 6?) will be solved (because people want this), and we will live with several bad codes to maintain.

--
Michel Fortin
https://michelf.ca

Yes, maybe in this way... Or using dispatch_group..

dispatch_group_t group = dispatch_group_create();

dispatch_group_async(group,dispatch_get_global_queue(0, 0), ^ {
    // loadWebResource});

dispatch_group_async(group,dispatch_get_global_queue(0, 0), ^ {
    // loadWebResource});

dispatch_group_notify(group,dispatch_get_global_queue(0, 0), ^ {
    // decodeImage ... etc...});

Can be made using different strategies, the compiler will select the best
fit for every case. Different runtimes, has different "best" strategies
also. No need to use a intermediary type.

···

Em seg, 4 de set de 2017 às 14:53, Michel Fortin <michel.fortin@michelf.ca> escreveu:

> Le 4 sept. 2017 à 10:01, Wallacy via swift-evolution < > swift-evolution@swift.org> a écrit :
>
> func processImageData1a() async ->
> Image {
> let dataResource = async loadWebResource("dataprofile.txt")
> let imageResource = async loadWebResource("imagedata.dat")
>
> // ... other stuff can go here to cover load latency...
>
> let imageTmp = await decodeImage(dataResource, imageResource) //
compiler error if await is not present.
> let imageResult = await dewarpAndCleanupImage(imageTmp)
> return imageResult
> }
>
>
> If this (or something like that) is not implemented, people will create
several versions to solve the same problem, so that later (Swift 6?) will
be solved (because people want this), and we will live with several bad
codes to maintain.

Just to be sure of what you are proposing, am I right to assume this would
be compiled down to something like this?

func processImageData1a(completion: (Image) -> ()) {
  var dataResource: Resource? = nil
  var imageResource: Resource? = nil
  var finishedBody = false

  func continuation() {
    // only continue once everything is ready
    guard finishedBody else { return }
    guard dataResource = dataResource else { return }
    guard imageResource = imageResource else { return }

    // everything is ready now
    decodeImage(dataResource, imageResource) { imageTmp in
      dewarpAndCleanupImage(imageTmp) { imageResult in
        completion(imageResult)
      }
    }
  }

  loadWebResource("dataprofile.txt") { result in
    dataResource = result
    continuation()
  }
  loadWebResource("imagedata.dat") { result in
    imageResource = result
    continuation()
  }

  // ... other stuff can go here to cover load latency...

  finishedBody = true
  continuation()
}

This seems more lightweight than a future to me. I know I've used this
pattern a few times. What I'm not sure about is how thrown errors would
work. Surely you want error handling to work when loading resources from
the web.

--
Michel Fortin
https://michelf.ca