I know what the proposal said. I’m making a case that there is value in doing it differently.
The composability of futures is valuable. Mixing and matching async/await with futures is also valuable. The queue-returning behavior that you can get from futures is also valuable, and building async/await on top of futures means async/await can get that for free. Maybe you don’t value those things, which is fine. But I do, and maybe other people do too. That’s why we’re having a discussion about it. It can also be valuable having a minimal implementation, but we have to acknowledge that it comes with a downside as well. The problem with doing a minimal implementation is that you can be stuck with the consequences for a long time. I want to make sure that we’re not stuck with the consequences of a minimal implementation that doesn’t adequately address the problems that async/await should be addressing. I’d hate for Swift to get an async/await that is so weak that it has to be augmented by tedious boilerplate code before it’s useful. > On Aug 28, 2017, at 1:54 PM, Wallacy <[email protected]> wrote: > > We don't need to this now! > > Again: (Using proposal words) > > "It is important to understand that this is proposing compiler support that > is completely concurrency runtime-agnostic. This proposal does not include a > new runtime model (like "actors") - it works just as well with GCD as with > pthreads or another API. Furthermore, unlike designs in other languages, it > is independent of specific coordination mechanisms, such as futures or > channels, allowing these to be built as library feature" > > and > > "This proposal does not formally propose a Future type, or any other > coordination abstractions. There are many rational designs for futures, and a > lot of experience working with them. On the other hand, there are also > completely different coordination primitives that can be used with this > coroutine design, and incorporating them into this proposal only makes it > larger." > > and > > We focus on task-based concurrency abstractions commonly encountered in > client and server applications, particularly those that are highly event > driven (e.g. responding to UI events or requests from clients). This does not > attempt to be a comprehensive survey of all possible options, nor does it > attempt to solve all possible problems in the space of concurrency. Instead, > it outlines a single coherent design thread that can be built over the span > of years to incrementally drive Swift to further greatness. > > and > > This proposal has been kept intentionally minimal, but there are many > possible ways to expand this in the future. > > .... > > The point is: No Future type is indeed proposed yet! > > The proposal try to include de "minimum" required to implement a basic > async/await to solve the problem created by the GCD! (Pyramid of doom) > > The question is: How do you do the same using dispatch_async ? dispatch_async > also does not return nothing to do what you are intentend do do! > > Algo, by Swift 5 manifesto, there's no compromise to make a "complete" > concurrency model by this time! > > My intention is only make parity to dispatch_async, but also make the ground > free to make more complex implementation like Futures in another round on top > of this one. > > This 'async T' can be a real type in the future? Maybe will... But doesn't > matter now! Now we only need to is some kind of type which need to be > unwrapped using await before use. Maybe this intermediary/virtual type can be > a real thing and gain some abilities at some point! Maybe a full Future type, > why not? > > Em seg, 28 de ago de 2017 às 17:33, Adam Kemp <[email protected] > <mailto:[email protected]>> escreveu: > How would these anonymous types get composed? If I wanted to implement a > function that takes a collection of futures and wait on it, how would I do > that? That is, how would I implement the equivalent of C#’s Task.WhenAll and > Task.WhenAny methods? > > More generally, how do you pass one of these typeless futures to some other > function so that we can do the waiting there? > > >> On Aug 28, 2017, at 1:23 PM, Wallacy <[email protected] >> <mailto:[email protected]>> wrote: >> >> And that's why I (and others) are suggesting: >> >> func processImageData1a() async -> Image { >> let dataResource = async loadWebResource("dataprofile.txt") // No future >> type here... Just another way to call dispatch_async under the hood. >> let imageResource = async loadWebResource("imagedata.dat") >> >> // ... other stuff can go here to cover load latency... >> >> let imageTmp = await decodeImage(dataResource, imageResource) // >> Compiles force await call here... >> let imageResult = await dewarpAndCleanupImage(imageTmp) >> return imageResult >> } >> >> And now we gain all advantages of async/await again without to handle with >> one more type. >> >> Em seg, 28 de ago de 2017 às 17:07, Adam Kemp via swift-evolution >> <[email protected] <mailto:[email protected]>> escreveu: >> I think the biggest tradeoff is clearer when you look at the examples from >> the proposal where futures are built on top of async/await: >> >> func processImageData1a() async -> Image { >> let dataResource = Future { await loadWebResource("dataprofile.txt") } >> let imageResource = Future { await loadWebResource("imagedata.dat") } >> >> // ... other stuff can go here to cover load latency... >> >> let imageTmp = await decodeImage(dataResource.get(), >> imageResource.get()) >> let imageResult = await dewarpAndCleanupImage(imageTmp) >> return imageResult >> } >> >> With this approach you have to wrap each call site to create a future. >> Compare to this: >> >> func processImageData1a() -> Future<Image> { >> let dataResourceFuture = loadWebResource("dataprofile.txt”); >> let imageResourceFuture = loadWebResource("imagedata.dat”); >> >> // ... other stuff can go here to cover load latency... >> >> let imageTmp = await decodeImage(await dataResourceFuture, await >> imageResourceFuture) >> let imageResult = await dewarpAndCleanupImage(imageTmp) >> return imageResult >> } >> >> Here, not only are the explicit wrappers gone, but this function itself can >> be used with either await or as a future. You get both options with one >> implementation. >> >> As I’ve mentioned before, C#’s implementation is not tied to any one >> particular futures implementation. The Task type is commonly used, but >> async/await does not directly depend on Task. Instead it works with any >> return type that meets certain requirements (detailed here: >> https://blogs.msdn.microsoft.com/pfxteam/2011/01/13/await-anything/ >> <https://blogs.msdn.microsoft.com/pfxteam/2011/01/13/await-anything/>). >> Swift could do this using a protocol, which can be retroactively applied >> using an extension. >> >> Obviously for this to be useful we would need some kind of existing future >> implementation, but at least we wouldn’t be tied to any particular one. That >> would mean library maintainers who have already been using their own futures >> implementations could quickly adopt async/await in their code without having >> to rewrite their futures library or throw wrappers around every usage of >> async/await. They could just adopt a protocol (using an extension, even) and >> get async/await support for free. >> >> The downside is that this feature would be specific to the async/await use >> case rather than a generic coroutine implementation (i.e., there would have >> to be a separate compiler transform for yield return). It’s not clear to me >> why it should be a goal to have just one generic coroutine feature. The >> real-world usages of async/await and yield return are different enough that >> I’m not convinced we could have a single compiler feature that meets the >> needs of both cleanly. >> >>> On Aug 27, 2017, at 7:35 PM, Florent Vilmart <[email protected] >>> <mailto:[email protected]>> wrote: >>> >>> Adam, you’re completely right, languages as c# and JS have been through the >>> path before, (callback, Promises , async/await) I believe Chris’s goal it >>> to avoid building a promise implementation and go straight to a coroutines >>> model, which is more deeply integrated with the compiler. I don’t see a >>> particular trade off, pursuing that route, and the main benefit is that >>> coroutines can power any asynchronous metaphor (Signals, Streams, Futures, >>> Promises etc...) which is not true of Futures so i would tend to think that >>> for the long run, and to maximize usability, async/await/yield would >>> probably be the way to go. >>> >>> On Aug 27, 2017, 22:22 -0400, Adam Kemp <[email protected] >>> <mailto:[email protected]>>, wrote: >>>> As has been explained, futures can be built on top of async/await (or the >>>> other way around). You can have the best of both worlds. We are not losing >>>> anything by having this feature. It would be a huge improvement to have >>>> this as an option. >>>> >>>> However, using futures correctly requires more nested closures than you >>>> have shown in your examples to avoid blocking any threads. That's why >>>> you're not seeing the advantage to async/await. You're comparing examples >>>> that have very different behaviors. >>>> >>>> That said, I have also expressed my opinion that it is better to build >>>> async/await on top of futures rather than the other way around. I believe >>>> it is more powerful and cleaner to make async/await work with any >>>> arbitrary future type (via a protocol). The alternative (building futures >>>> on top of async/await) requires more code when the two are mixed. I very >>>> much prefer how it's done in C#, where you can freely mix the two models >>>> without having to resort to ad-hoc wrappers, and you can use async/await >>>> with any futures implementation you might already be using. >>>> >>>> I really think we should be having more discussion about the tradeoffs >>>> between those two approaches, and I'm concerned that some of the opinions >>>> about how C# does it are not based on a clear and accurate understanding >>>> of how it actually works in that language. >>>> >>>> -- >>>> Adam Kemp >>>> >>>> On Aug 27, 2017, at 6:02 PM, Howard Lovatt <[email protected] >>>> <mailto:[email protected]>> wrote: >>>> >>>>> The async/await is very similar to the proposed Future (as I posed >>>>> earlier) with regard to completion-handler code, they both re-write the >>>>> imported completion-handler function using a closure, the relevant >>>>> sentence from the Async Proposal is: >>>>> >>>>> "Under the hood, the compiler rewrites this code using nested closures >>>>> ..." >>>>> >>>>> Unlike the proposed future code the async code is not naturally parallel, >>>>> in the running example the following lines from the async code are run in >>>>> series, i.e. await blocks: >>>>> >>>>> let dataResource = await loadWebResource("dataprofile.txt") >>>>> let imageResource = await loadWebResource("imagedata.dat") >>>>> The equivalent lines using the proposed Future: >>>>> let dataResource = loadWebResource("dataprofile.txt") >>>>> let imageResource = loadWebResource("imagedata.dat") >>>>> Run in parallel and therefore are potentially faster assuming that >>>>> resources, like cores and IO, are available. >>>>> >>>>> Therefore you would be better using a Future than an async, so why >>>>> provide an async unless you can make a convincing argument that it allows >>>>> you to write a better future? >>>>> >>>>> -- Howard. >>>>> >>>>> On 28 August 2017 at 09:59, Adam Kemp <[email protected] >>>>> <mailto:[email protected]>> wrote: >>>>> This example still has nested closures (to create a Future), and still >>>>> relies on a synchronous get method that will block a thread. Async/await >>>>> does not require blocking any threads. >>>>> >>>>> I’m definitely a fan of futures, but this example isn’t even a good >>>>> example of using futures. If you’re using a synchronous get method then >>>>> you’re not using futures properly. They’re supposed to make it easy to >>>>> avoid writing blocking code. This example just does the blocking call on >>>>> some other thread. >>>>> >>>>> Doing it properly would show the benefits of async/await because it would >>>>> require more nesting and more complex error handling. By simplifying the >>>>> code you’ve made a comparison between proper asynchronous code (with >>>>> async/await) and improper asynchronous code (your example). >>>>> >>>>> That tendency to want to just block a thread to make it easier is exactly >>>>> why async/await is so valuable. You get simple code while still doing it >>>>> correctly. >>>>> >>>>> -- >>>>> Adam Kemp >>>>> >>>>> On Aug 27, 2017, at 4:00 PM, Howard Lovatt via swift-evolution >>>>> <[email protected] <mailto:[email protected]>> wrote: >>>>> >>>>>> The running example used in the white paper coded using a Future is: >>>>>> >>>>>> func processImageData1() -> Future<Image> { >>>>>> return AsynchronousFuture { _ -> Image in >>>>>> let dataResource = loadWebResource("dataprofile.txt") // >>>>>> dataResource and imageResource run in parallel. >>>>>> let imageResource = loadWebResource("imagedata.dat") >>>>>> let imageTmp = decodeImage(dataResource.get ?? >>>>>> Resource(path: "Default data resource or prompt user"), >>>>>> imageResource.get ?? Resource(path: "Default image resource or prompt >>>>>> user")) >>>>>> let imageResult = dewarpAndCleanupImage(imageTmp.get ?? >>>>>> Image(dataPath: "Default image or prompt user", imagePath: "Default >>>>>> image or prompt user")) >>>>>> return imageResult.get ?? Image(dataPath: "Default image or >>>>>> prompt user", imagePath: "Default image or prompt user") >>>>>> } >>>>>> } >>>>>> >>>>>> This also avoids the pyramid of doom; the pyramid is avoided by >>>>>> converting continuation-handlers into either a sync or future, i.e. it >>>>>> is the importer that eliminates the nesting by translating the code >>>>>> automatically. >>>>>> >>>>>> This example using Future also demonstrates three advantages of Future: >>>>>> they are naturally parallel (dataResource and imageResource lines run in >>>>>> parallel), they timeout automatically (get returns nil if the Future has >>>>>> taken too long), and if there is a failure (for any reason including >>>>>> timeout) it provides a method of either detecting the failure or >>>>>> providing a default (get returns nil on failure). >>>>>> >>>>>> There are a three of other advantages a Future has that this example >>>>>> doesn’t show: control over which thread the Future runs on, Futures can >>>>>> be cancelled, and debugging information is available. >>>>>> >>>>>> You could imagine `async` as a syntax sugar for Future, e.g. the above >>>>>> Future example could be: >>>>>> >>>>>> func processImageData1() async -> Image { >>>>>> let dataResource = loadWebResource("dataprofile.txt") // >>>>>> dataResource and imageResource run in parallel. >>>>>> let imageResource = loadWebResource("imagedata.dat") >>>>>> let imageTmp = decodeImage(dataResource.get ?? Resource(path: >>>>>> "Default data resource or prompt user"), imageResource.get ?? >>>>>> Resource(path: "Default image resource or prompt user")) >>>>>> let imageResult = dewarpAndCleanupImage(imageTmp.get ?? >>>>>> Image(dataPath: "Default image or prompt user", imagePath: "Default >>>>>> image or prompt user")) >>>>>> return imageResult.get ?? Image(dataPath: "Default image or prompt >>>>>> user", imagePath: "Default image or prompt user") >>>>>> } >>>>>> >>>>>> Since an async is sugar for Future the async runs as soon as it is >>>>>> created (as soon as the underlying Future is created) and get returns an >>>>>> optional (also cancel and status would be still be present). Then if you >>>>>> want control over threads and timeout they could be arguments to async: >>>>>> >>>>>> func processImageData1() async(queue: DispatchQueue.main, timeout: >>>>>> .seconds(5)) -> Image { ... } >>>>>> >>>>>> On Sat, 26 Aug 2017 at 11:00 pm, Florent Vilmart <[email protected] >>>>>> <mailto:[email protected]>> wrote: >>>>>> Howard, with async / await, the code is flat and you don’t have to >>>>>> unowned/weak self to prevent hideous cycles in the callbacks. >>>>>> Futures can’t do that >>>>>> >>>>>> On Aug 26, 2017, 04:37 -0400, Goffredo Marocchi via swift-evolution >>>>>> <[email protected] <mailto:[email protected]>>, wrote: >>>>>>> With both he now built in promises in Node8 as well as libraries like >>>>>>> Bluebird there was ample time to evaluate them and convert/auto convert >>>>>>> at times libraries that loved callback pyramids of doom when the flow >>>>>>> grows complex into promise based chains. Converting to Promises seems >>>>>>> magical for the simple case, but can quickly descend in hard to follow >>>>>>> flows and hard to debug errors when you move to non trivial multi path >>>>>>> scenarios. JS is now solving it with their implementation of >>>>>>> async/await, but the point is that without the full picture any single >>>>>>> solution would break horribly in real life scenarios. >>>>>>> >>>>>>> Sent from my iPhone >>>>>>> >>>>>>> On 26 Aug 2017, at 06:27, Howard Lovatt via swift-evolution >>>>>>> <[email protected] <mailto:[email protected]>> wrote: >>>>>>> >>>>>>>> My argument goes like this: >>>>>>>> >>>>>>>> 1. You don't need async/await to write a powerful future type; you >>>>>>>> can use the underlying threads just as well, i.e. future with >>>>>>>> async/await is no better than future without. >>>>>>>> >>>>>>>> 2. Since future is more powerful, thread control, cancel, and >>>>>>>> timeout, people should be encouraged to use this; instead because >>>>>>>> async/await are language features they will be presumed, incorrectly, >>>>>>>> to be the best way, consequently people will get into trouble with >>>>>>>> deadlocks because they don't have control. >>>>>>>> >>>>>>>> 3. async/await will require some engineering work and will at best >>>>>>>> make a mild syntax improvement and at worst lead to deadlocks, >>>>>>>> therefore they just don't carry their weight in terms of useful >>>>>>>> additions to Swift. >>>>>>>> >>>>>>>> Therefore, save some engineering effort and just provide a future >>>>>>>> library. >>>>>>>> >>>>>>>> To turn the question round another way, in two forms: >>>>>>>> >>>>>>>> 1. What can async/wait do that a future can't? >>>>>>>> >>>>>>>> 2. How will future be improved if async/await is added? >>>>>>>> >>>>>>>> >>>>>>>> -- Howard. >>>>>>>> >>>>>>>> On 26 August 2017 at 02:23, Joe Groff <[email protected] >>>>>>>> <mailto:[email protected]>> wrote: >>>>>>>> >>>>>>>>> On Aug 25, 2017, at 12:34 AM, Howard Lovatt <[email protected] >>>>>>>>> <mailto:[email protected]>> wrote: >>>>>>>>> >>>>>>>>> In particular a future that is cancellable is more powerful that the >>>>>>>>> proposed async/await. >>>>>>>> >>>>>>>> It's not more powerful; the features are to some degree disjoint. You >>>>>>>> can build a Future abstraction and then use async/await to sugar code >>>>>>>> that threads computation through futures. Getting back to Jakob's >>>>>>>> example, someone (maybe the Clang importer, maybe Apple's framework >>>>>>>> developers in an overlay) will still need to build infrastructure on >>>>>>>> top of IBActions and other currently ad-hoc signalling mechanisms to >>>>>>>> integrate them into a more expressive coordination framework. >>>>>>>> >>>>>>>> -Joe >>>>>>>> >>>>>>>> _______________________________________________ >>>>>>>> swift-evolution mailing list >>>>>>>> [email protected] <mailto:[email protected]> >>>>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution >>>>>>>> <https://lists.swift.org/mailman/listinfo/swift-evolution> >>>>>> >>>>>> -- >>>>>> -- Howard. >>>>>> _______________________________________________ >>>>>> swift-evolution mailing list >>>>>> [email protected] <mailto:[email protected]> >>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution >>>>>> <https://lists.swift.org/mailman/listinfo/swift-evolution> >>>>> >> >> _______________________________________________ >> swift-evolution mailing list >> [email protected] <mailto:[email protected]> >> https://lists.swift.org/mailman/listinfo/swift-evolution >> <https://lists.swift.org/mailman/listinfo/swift-evolution> >
_______________________________________________ swift-evolution mailing list [email protected] https://lists.swift.org/mailman/listinfo/swift-evolution
