[Concurrency] Async/Await

Hello,

Thanks for the great work on the async/await proposal! After reading it, I have a few questions and comments about it, so I’m creating this thread to concentrate on that topic (instead of Actors).

Generators

The proposal mentions in Problem 6 of the Motivation how generators can help write sequences:

In contrast, languages that have generators allow you to write something more close to this:

func getSequence() -> AnySequence<Int> {
    let seq = sequence {
        for i in 1...10 {
            yield(i*i)
        }
    }
    return AnySequence(seq)
}

This feels very similar to me from C# where the yield keyword is used to support the generator feature. But I fail to see how the coroutines as described in this proposal resolve this problem. Can someone explain?

beginAsync

The documentation of the beginAsync and suspendAsync functions state:

// NB: Names subject to bikeshedding. These are low-level primitives that most
// users should not need to interact with directly, so namespacing them
// and/or giving them verbose names unlikely to collide or pollute code
// completion (and possibly not even exposing them outside the stdlib to begin
// with) would be a good idea.

But I don’t understand how they can be kept private to the standard library when they are used for the important pattern of spawning off an async operation from a non-async function:

Despite these problems, it is essential that the model encompasses this pattern, because it is a practical necessity in Cocoa development. With this proposal, it would look like this:

@IBAction func buttonDidClick(sender:AnyObject) {
  // 1
  beginAsync {
    // 2
    let image = await processImage()
    imageView.image = image
  }
  // 3
Futures

When discussing futures, the proposal states:

The exact design for a future type deserves its own proposal, but a proof of concept could look like this:

Does that sentence imply that the Core Team would welcome a Future implementation into the Standard Library?

async as a subtype of throws instead of orthogonal to it

I’ve been thinking a lot about this since the proposal came out and I see a few serious disadvantages at making async a subtype of throws which might benefit from being discussed or/and mentioned in the proposal.

1. We loose the automatic documentation try provides for signaling failable functions:

let image = await downloadImage()
let processedImage = await processImage(image)
await present(MyViewController(image: image))

In my example, downloadImage can fail because of network conditions, processImage can not fail, and present is the UIKit function which presents view controllers and it can’t fail either. But that’s not obvious from reading the code. We’ve lost information.

2. Supporting try? and try! adds a lot of confusion:

As was mentioned by Karim Nassar is another post, if await infers try, then there seems to be no good solution for supporting try? and try!:

Using await? and await! seems slightly conter-intuitive because we are further mixing the concepts of coroutines/asynchronous operations with error handling.
Adding try? and try! (like suggested Chris Lattner) feels like it makes point (1) by having both explicit try?/! and implicit try through await.

3. Philosophical discussion

If async calls don’t return futures because coroutines are a generally useful language features beyond the domain of async/await, doesn’t making async imply throws also muddy the concept of coroutines where failable coroutine operations don’t make much sense?

David.

I (feel like I) understand the generator example with yield. It is like return, but if you call it again, it picks up from where it left off. Or at least that is my reading of what it does.

I am finding that I DO NOT understand beginAsync. At first I thought it was the async/await version of 'do{}’, but as others are giving examples which use it in different ways, I find I don’t actually know how it works… because the examples aren’t using it that way.

Can anyone give a layman’s explanation of beginAsync. That might actually help with the bike shedding. If you can’t explain something simply, then you don’t really understand it… and if you don’t understand it, you can’t really give it an intuitive name.

Thanks,
Jon

···

On Aug 20, 2017, at 10:58 PM, David Hart via swift-evolution <swift-evolution@swift.org> wrote:

Hello,

Thanks for the great work on the async/await proposal! After reading it, I have a few questions and comments about it, so I’m creating this thread to concentrate on that topic (instead of Actors).

Generators

The proposal mentions in Problem 6 of the Motivation how generators can help write sequences:

In contrast, languages that have generators allow you to write something more close to this:

func getSequence() -> AnySequence<Int> {
    let seq = sequence {
        for i in 1...10 {
            yield(i*i)
        }
    }
    return AnySequence(seq)
}

This feels very similar to me from C# where the yield keyword is used to support the generator feature. But I fail to see how the coroutines as described in this proposal resolve this problem. Can someone explain?

beginAsync

The documentation of the beginAsync and suspendAsync functions state:

// NB: Names subject to bikeshedding. These are low-level primitives that most
// users should not need to interact with directly, so namespacing them
// and/or giving them verbose names unlikely to collide or pollute code
// completion (and possibly not even exposing them outside the stdlib to begin
// with) would be a good idea.

But I don’t understand how they can be kept private to the standard library when they are used for the important pattern of spawning off an async operation from a non-async function:

Despite these problems, it is essential that the model encompasses this pattern, because it is a practical necessity in Cocoa development. With this proposal, it would look like this:

@IBAction func buttonDidClick(sender:AnyObject) {
  // 1
  beginAsync {
    // 2
    let image = await processImage()
    imageView.image = image
  }
  // 3
Futures

When discussing futures, the proposal states:

The exact design for a future type deserves its own proposal, but a proof of concept could look like this:

Does that sentence imply that the Core Team would welcome a Future implementation into the Standard Library?

async as a subtype of throws instead of orthogonal to it

I’ve been thinking a lot about this since the proposal came out and I see a few serious disadvantages at making async a subtype of throws which might benefit from being discussed or/and mentioned in the proposal.

1. We loose the automatic documentation try provides for signaling failable functions:

let image = await downloadImage()
let processedImage = await processImage(image)
await present(MyViewController(image: image))

In my example, downloadImage can fail because of network conditions, processImage can not fail, and present is the UIKit function which presents view controllers and it can’t fail either. But that’s not obvious from reading the code. We’ve lost information.

2. Supporting try? and try! adds a lot of confusion:

As was mentioned by Karim Nassar is another post, if await infers try, then there seems to be no good solution for supporting try? and try!:

Using await? and await! seems slightly conter-intuitive because we are further mixing the concepts of coroutines/asynchronous operations with error handling.
Adding try? and try! (like suggested Chris Lattner) feels like it makes point (1) by having both explicit try?/! and implicit try through await.

3. Philosophical discussion

If async calls don’t return futures because coroutines are a generally useful language features beyond the domain of async/await, doesn’t making async imply throws also muddy the concept of coroutines where failable coroutine operations don’t make much sense?

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

Sorry for the shameless bump :-/ but I’d love to get some answers so I can better understand the proposal and participate in the discussions.

···

On 21 Aug 2017, at 07:58, David Hart via swift-evolution <swift-evolution@swift.org> wrote:

Hello,

Thanks for the great work on the async/await proposal! After reading it, I have a few questions and comments about it, so I’m creating this thread to concentrate on that topic (instead of Actors).

Generators

The proposal mentions in Problem 6 of the Motivation how generators can help write sequences:

In contrast, languages that have generators allow you to write something more close to this:

func getSequence() -> AnySequence<Int> {
    let seq = sequence {
        for i in 1...10 {
            yield(i*i)
        }
    }
    return AnySequence(seq)
}

This feels very similar to me from C# where the yield keyword is used to support the generator feature. But I fail to see how the coroutines as described in this proposal resolve this problem. Can someone explain?

beginAsync

The documentation of the beginAsync and suspendAsync functions state:

// NB: Names subject to bikeshedding. These are low-level primitives that most
// users should not need to interact with directly, so namespacing them
// and/or giving them verbose names unlikely to collide or pollute code
// completion (and possibly not even exposing them outside the stdlib to begin
// with) would be a good idea.

But I don’t understand how they can be kept private to the standard library when they are used for the important pattern of spawning off an async operation from a non-async function:

Despite these problems, it is essential that the model encompasses this pattern, because it is a practical necessity in Cocoa development. With this proposal, it would look like this:

@IBAction func buttonDidClick(sender:AnyObject) {
  // 1
  beginAsync {
    // 2
    let image = await processImage()
    imageView.image = image
  }
  // 3
Futures

When discussing futures, the proposal states:

The exact design for a future type deserves its own proposal, but a proof of concept could look like this:

Does that sentence imply that the Core Team would welcome a Future implementation into the Standard Library?

async as a subtype of throws instead of orthogonal to it

I’ve been thinking a lot about this since the proposal came out and I see a few serious disadvantages at making async a subtype of throws which might benefit from being discussed or/and mentioned in the proposal.

1. We loose the automatic documentation try provides for signaling failable functions:

let image = await downloadImage()
let processedImage = await processImage(image)
await present(MyViewController(image: image))

In my example, downloadImage can fail because of network conditions, processImage can not fail, and present is the UIKit function which presents view controllers and it can’t fail either. But that’s not obvious from reading the code. We’ve lost information.

2. Supporting try? and try! adds a lot of confusion:

As was mentioned by Karim Nassar is another post, if await infers try, then there seems to be no good solution for supporting try? and try!:

Using await? and await! seems slightly conter-intuitive because we are further mixing the concepts of coroutines/asynchronous operations with error handling.
Adding try? and try! (like suggested Chris Lattner) feels like it makes point (1) by having both explicit try?/! and implicit try through await.

3. Philosophical discussion

If async calls don’t return futures because coroutines are a generally useful language features beyond the domain of async/await, doesn’t making async imply throws also muddy the concept of coroutines where failable coroutine operations don’t make much sense?

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

This would be my explanation:

  `beginAsync` runs the provided async function up until the point where it needs to `await` a result; it then returns so that you can do other work. The function will resume later, after whatever result it's `await`ing has been returned.

A slightly more technical explanation would note that `beginAsync` runs the function until it, or one of its callees, calls `suspendAsync` with a function that returns without calling the continuation function it was passed. It is `suspendAsync` being called and returning without calling the continuation that causes all the functions above it, all the way up to the one passed to `beginAsync`, to return.

···

On Aug 21, 2017, at 10:47 PM, Jonathan Hull via swift-evolution <swift-evolution@swift.org> wrote:

I am finding that I DO NOT understand beginAsync. At first I thought it was the async/await version of 'do{}’, but as others are giving examples which use it in different ways, I find I don’t actually know how it works… because the examples aren’t using it that way.

Can anyone give a layman’s explanation of beginAsync. That might actually help with the bike shedding. If you can’t explain something simply, then you don’t really understand it… and if you don’t understand it, you can’t really give it an intuitive name.

--
Brent Royal-Gordon
Architechies

Sorry for the shameless bump :-/ but I’d love to get some answers so I can better understand the proposal and participate in the discussions.

Hello,

Thanks for the great work on the async/await proposal! After reading it, I have a few questions and comments about it, so I’m creating this thread to concentrate on that topic (instead of Actors).

Generators

The proposal mentions in Problem 6 of the Motivation how generators can help write sequences:

In contrast, languages that have generators allow you to write something more close to this:

func getSequence() -> AnySequence<Int> {
    let seq = sequence {
        for i in 1...10 {
            yield(i*i)
        }
    }
    return AnySequence(seq)
}

This feels very similar to me from C# where the yield keyword is used to support the generator feature. But I fail to see how the coroutines as described in this proposal resolve this problem. Can someone explain?

The feature provides general delimited continuations. You could write an IteratorProtocol-conforming interface over a coroutine like this:

class Generator<T>: IteratorProtocol {
var next: T? = nil
var resume: (() -> ())? = nil

init(_ body: (_ yield: @escaping (T) async -> Void) -> Void) {
   self.resume = {
     beginAsync {
       body(self.yield)
     }
}

func next() -> T? {
   if let resume = self.resume {
     resume()
     return self.next
   }
   return nil
}

private func yield(_ value: T) async -> Void {
   self.next = value
   await suspendAsync { cont in
     resume = cont
   }
}
}

let fibs = Generator { yield in
var (a, b) = (0, 1)
while a < 1000 {
   await yield(a)
   (a, b) = (b, a + b)
}
}

This isn't ideal in a number of ways (awkward, not particularly efficient, and has the gotcha that the generator's `body` could suspend itself with something other than the `yield` operation, doesn't integrate with ownership in the way John proposes in the ownership manifesto), so it may not be a good idea, of course.

beginAsync

The documentation of the beginAsync and suspendAsync functions state:

// NB: Names subject to bikeshedding. These are low-level primitives that most
// users should not need to interact with directly, so namespacing them
// and/or giving them verbose names unlikely to collide or pollute code
// completion (and possibly not even exposing them outside the stdlib to begin
// with) would be a good idea.

But I don’t understand how they can be kept private to the standard library when they are used for the important pattern of spawning off an async operation from a non-async function:

beginAsync provides raw material for starting an async process, but I think you'd often want to wrap it up in something more interesting, like a DispatchQueue method that enqueues the coroutine on a specific queue, a Future constructor that captures the eventual result, etc. Nonetheless, I removed this statement from the proposal.

Despite these problems, it is essential that the model encompasses this pattern, because it is a practical necessity in Cocoa development. With this proposal, it would look like this:

@IBAction func buttonDidClick(sender:AnyObject) {
  // 1
  beginAsync {
    // 2
    let image = await processImage()
    imageView.image = image
  }
  // 3
Futures

When discussing futures, the proposal states:

The exact design for a future type deserves its own proposal, but a proof of concept could look like this:

Does that sentence imply that the Core Team would welcome a Future implementation into the Standard Library?

It's worth discussing. My personal feeling is that a lot of the things people do with futures can potentially be done better with other coordination primitives, but we'll see.

async as a subtype of throws instead of orthogonal to it

I’ve been thinking a lot about this since the proposal came out and I see a few serious disadvantages at making async a subtype of throws which might benefit from being discussed or/and mentioned in the proposal.

1. We loose the automatic documentation try provides for signaling failable functions:

let image = await downloadImage()
let processedImage = await processImage(image)
await present(MyViewController(image: image))

In my example, downloadImage can fail because of network conditions, processImage can not fail, and present is the UIKit function which presents view controllers and it can’t fail either. But that’s not obvious from reading the code. We’ve lost information.

This seems like a similar pitfall to too narrow exception types that we try to avoid with `throws`. Saying that even a long-lived computation like processImage can't throw is a brittle architectural choice, since you may need to build in support for cancellation at some point, and if you ever decide to offload the computation to a GPU, coprocessor, or out-of-process worker, then it will be able to fail at that point. It's not clear to me why `present` would be async here; it seems to me like a fire-and-forget kind of operation you don't want to wait for.

-Joe

···

On Aug 21, 2017, at 10:21 PM, David Hart <david@hartbit.com> wrote:

On 21 Aug 2017, at 07:58, David Hart via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

async as a subtype of throws instead of orthogonal to it

I’ve been thinking a lot about this since the proposal came out and I see a few serious disadvantages at making async a subtype of throws which might benefit from being discussed or/and mentioned in the proposal.

1. We loose the automatic documentation try provides for signaling failable functions:

let image = await downloadImage()
let processedImage = await processImage(image)
await present(MyViewController(image: image))

In my example, downloadImage can fail because of network conditions, processImage can not fail, and present is the UIKit function which presents view controllers and it can’t fail either. But that’s not obvious from reading the code. We’ve lost information.

This seems like a similar pitfall to too narrow exception types that we try to avoid with `throws`. Saying that even a long-lived computation like processImage can't throw is a brittle architectural choice, since you may need to build in support for cancellation at some point, and if you ever decide to offload the computation to a GPU, coprocessor, or out-of-process worker, then it will be able to fail at that point.

You may be able to handle cancellation by abandoning the completion, rather than returning an error. And I can't imagine the error behavior of a low-level operation like `processImage(_:)` would ever bubble back up into the UI, so you can incorporate that error logic into the async function. (For instance, if you want to do the operation on the GPU but fall back to the CPU if it fails, then do that fallback inside `processImage(_:)`.)

And if I'm wrong? Well, then you'll add `throws` and the compiler will tell you where to insert error handling. The switch from "zero errors" to "some errors" is much simpler than the switch from "N errors" to "N+1 errors". And remember, if the code previously couldn't throw in practice, any error handling code the user might have written to placate the compiler has probably never been exercised. There's a pretty good chance they used `try!` or empty `catch` blocks to quickly dispose of the non-existent errors. The compiler can tell you where you need to add error-handling code, but it can't tell you where your error-handling code is useless or wrong.

It's not clear to me why `present` would be async here; it seems to me like a fire-and-forget kind of operation you don't want to wait for.

The `UIViewController.present` method has a completion block; I assume David Hart is thinking that `await` here would cause the subsequent code to run once the view controller finished presenting.

(Which brings up an interesting point: there are some APIs with completion blocks that you only occasionally use. For these APIs, it might make sense to have an `ignore` counterpart to `await` or something, and that counterpart seems like it might make `beginAsync` redundant.)

···

On Aug 22, 2017, at 9:32 AM, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:

--
Brent Royal-Gordon
Architechies

Just to get it out of the way and to put the issue down... assuming we could get something like Promises and Futures, how much experience do people have with how async and await + Promises are changing the asynchronous by nature JavaScript? What in there really does not work and should one of the layer we provide be similar to it?

···

Sent from my iPhone

On 22 Aug 2017, at 17:32, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:

On Aug 21, 2017, at 10:21 PM, David Hart <david@hartbit.com> wrote:

Sorry for the shameless bump :-/ but I’d love to get some answers so I can better understand the proposal and participate in the discussions.

On 21 Aug 2017, at 07:58, David Hart via swift-evolution <swift-evolution@swift.org> wrote:

Hello,

Thanks for the great work on the async/await proposal! After reading it, I have a few questions and comments about it, so I’m creating this thread to concentrate on that topic (instead of Actors).

Generators

The proposal mentions in Problem 6 of the Motivation how generators can help write sequences:

In contrast, languages that have generators allow you to write something more close to this:

func getSequence() -> AnySequence<Int> {
    let seq = sequence {
        for i in 1...10 {
            yield(i*i)
        }
    }
    return AnySequence(seq)
}

This feels very similar to me from C# where the yield keyword is used to support the generator feature. But I fail to see how the coroutines as described in this proposal resolve this problem. Can someone explain?

The feature provides general delimited continuations. You could write an IteratorProtocol-conforming interface over a coroutine like this:

class Generator<T>: IteratorProtocol {
var next: T? = nil
var resume: (() -> ())? = nil

init(_ body: (_ yield: @escaping (T) async -> Void) -> Void) {
   self.resume = {
     beginAsync {
       body(self.yield)
     }
}

func next() -> T? {
   if let resume = self.resume {
     resume()
     return self.next
   }
   return nil
}

private func yield(_ value: T) async -> Void {
   self.next = value
   await suspendAsync { cont in
     resume = cont
   }
}
}

let fibs = Generator { yield in
var (a, b) = (0, 1)
while a < 1000 {
   await yield(a)
   (a, b) = (b, a + b)
}
}

This isn't ideal in a number of ways (awkward, not particularly efficient, and has the gotcha that the generator's `body` could suspend itself with something other than the `yield` operation, doesn't integrate with ownership in the way John proposes in the ownership manifesto), so it may not be a good idea, of course.

beginAsync

The documentation of the beginAsync and suspendAsync functions state:

// NB: Names subject to bikeshedding. These are low-level primitives that most
// users should not need to interact with directly, so namespacing them
// and/or giving them verbose names unlikely to collide or pollute code
// completion (and possibly not even exposing them outside the stdlib to begin
// with) would be a good idea.

But I don’t understand how they can be kept private to the standard library when they are used for the important pattern of spawning off an async operation from a non-async function:

beginAsync provides raw material for starting an async process, but I think you'd often want to wrap it up in something more interesting, like a DispatchQueue method that enqueues the coroutine on a specific queue, a Future constructor that captures the eventual result, etc. Nonetheless, I removed this statement from the proposal.

Despite these problems, it is essential that the model encompasses this pattern, because it is a practical necessity in Cocoa development. With this proposal, it would look like this:

@IBAction func buttonDidClick(sender:AnyObject) {
  // 1
  beginAsync {
    // 2
    let image = await processImage()
    imageView.image = image
  }
  // 3
Futures

When discussing futures, the proposal states:

The exact design for a future type deserves its own proposal, but a proof of concept could look like this:

Does that sentence imply that the Core Team would welcome a Future implementation into the Standard Library?

It's worth discussing. My personal feeling is that a lot of the things people do with futures can potentially be done better with other coordination primitives, but we'll see.

async as a subtype of throws instead of orthogonal to it

I’ve been thinking a lot about this since the proposal came out and I see a few serious disadvantages at making async a subtype of throws which might benefit from being discussed or/and mentioned in the proposal.

1. We loose the automatic documentation try provides for signaling failable functions:

let image = await downloadImage()
let processedImage = await processImage(image)
await present(MyViewController(image: image))

In my example, downloadImage can fail because of network conditions, processImage can not fail, and present is the UIKit function which presents view controllers and it can’t fail either. But that’s not obvious from reading the code. We’ve lost information.

This seems like a similar pitfall to too narrow exception types that we try to avoid with `throws`. Saying that even a long-lived computation like processImage can't throw is a brittle architectural choice, since you may need to build in support for cancellation at some point, and if you ever decide to offload the computation to a GPU, coprocessor, or out-of-process worker, then it will be able to fail at that point. It's not clear to me why `present` would be async here; it seems to me like a fire-and-forget kind of operation you don't want to wait for.

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

Hello,

The feature provides general delimited continuations. You could write an IteratorProtocol-conforming interface over a coroutine like this:

private func yield(_ value: T) async -> Void {
   self.next = value
   await suspendAsync { cont in
     resume = cont
   }
}

This isn't ideal in a number of ways (awkward, not particularly efficient, and has the gotcha that the generator's `body` could suspend itself with something other than the `yield` operation, doesn't integrate with ownership in the way John proposes in the ownership manifesto), so it may not be a good idea, of course.

I still have trouble understanding how exactly suspendAsync is going to work.
Is the body called right away or is it put into some dispatch queue on another thread?
Does the continuation function return immediately (besides queueing suspendAsync to also return)?

How does this correlate with coroutines?
There is no extra stack for the coroutine (which runs the generator function), right?
In my mental model, the generator function is split into individual functions which are queued one after another, but reusing the same thread/stack, is this correct?

How would a generator feature without the mentioned shortcomings look like?

Thanks for any help in understanding how it works!
And thanks for all the great work, this looks like a nice basis for future Swift :-)

— Martin

···

Am 22.08.2017 um 18:32 schrieb Joe Groff via swift-evolution <swift-evolution@swift.org>:

Hello,

The feature provides general delimited continuations. You could write an IteratorProtocol-conforming interface over a coroutine like this:

private func yield(_ value: T) async -> Void {
   self.next = value
   await suspendAsync { cont in
     resume = cont
   }
}

This isn't ideal in a number of ways (awkward, not particularly efficient, and has the gotcha that the generator's `body` could suspend itself with something other than the `yield` operation, doesn't integrate with ownership in the way John proposes in the ownership manifesto), so it may not be a good idea, of course.

I still have trouble understanding how exactly suspendAsync is going to work.
Is the body called right away or is it put into some dispatch queue on another thread?
Does the continuation function return immediately (besides queueing suspendAsync to also return)?

suspendAsync suspends the coroutine's context and passes it as a continuation into the body, which is executed immediately on the current thread.

How does this correlate with coroutines?
There is no extra stack for the coroutine (which runs the generator function), right?
In my mental model, the generator function is split into individual functions which are queued one after another, but reusing the same thread/stack, is this correct?

That's the way C# implements it, yeah, and it could be how we implement it as well. It would likely be the quickest way to get the feature up and running on existing platforms. I intentionally left the mechanism vague because there may be other implementation models we can explore within this model, such as running coroutines on their own lightweight growable stacks the way some other languages like Julia and recent versions of Go implement coroutines.

How would a generator feature without the mentioned shortcomings look like?

The main thing missing from this model that generators need is a way to require a value to be yielded to the controlling context at each point the coroutine suspends itself. The proposal as written puts the responsibility for scheduling the continuation and passing data from async to sync into the caller of suspendAsync; this is good for async tasks since you don't generally want or need to expose dependencies on specific coordination mechanisms through an async task. One could complicate the model by making 'async' a typed effect, analogous to the frequently-requested "typed throws" feature, where the type of "async" is the type of value that is yielded back to the sync context at every suspension point.

-Joe

···

On Aug 23, 2017, at 12:06 AM, Martin Waitz <tali@admingilde.org> wrote:

Am 22.08.2017 um 18:32 schrieb Joe Groff via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>>: