> On Aug 21, 2017, at 10:21 PM, David Hart <[email protected]> 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 
>> <[email protected] <mailto:[email protected]>> 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
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to