> 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