On 29.08.2017 19:02, Wallacy via swift-evolution wrote:
In this example i think we lose clarity, just looking for the code we cant know if this two line will run on parallel or not! Also, image.get blocks the thread, in this case we need the await anyway! And `async` can throws too... So the error handler can be pretty similar.

let  image=  asyncpreprocessImage(downloadImage()) // These first two lines run in 
parallel and I can "see" the async keyword.
let  text=  asynctranslate(downloadText())
await render(image: image ?? defaultImage,text: text ?? defaultText) // No 
blocking!


FWIW: I'm following the whole discussion from the start, and do support the opinion that async/await is much clear solution that proposed Futures, especially for beginners. We need a low-level building blocks which can be used to implement Futures/Promises in libraries. Also I really like the idea of 'async' on the caller side to have code running in parallel.

The 'async' version of func declaration is clearly saying what type it *want* to return, and 'async' modifier just saying *how* it will/can return that type('Image' in examples). So on both sides, on declaration and on caller side, we are clear what types we are working with. Future<Type> - is mixing of what is returning and how this will be returned. Code is saying that we preprocessesImage, but actually we have Future<Image> type, no 'markers' of asynchronous code.

Also, I wonder(if I missed that in proposal/discussion, please let me know), if I have async function like

func foo() async -> Type {}

, may I want to call it synchronously? If so, what would be a solution here? I can think about something like 'sync' modifier on caller side:
let x = sync foo() // calling asynchronous function synchronously

I believe that is what Future.get is doing, no?
let future =  ...
future.get() // blocks the execution, waits for the result.

Probably it is reasonable to allow just call foo() to get blocking result, just like any other 'simple' blocking funcs that we call, but this can lead to unexpected behavior as user can expect async execution.

With Futures, it seems like we can't "just" call such function and need to call .get() later:
let future = someFuncReturnsFuture() // already returns Future<Type> type

Vladimir.


Like i said before! Today's, the proposal only lack two things over the 
`Future`....
Parallel computing: Can be implemented by a third party library or a personal one, but i don't think this is a good approach to the first version. Coordination: This we can wait! And why? Because coordination, can be made in different ways, maybe is more suitable to a standard library class/function, not a language level resource.

Also, coordination cant be applied to all variants of the runtimes in the same way! async/await as language level works just as well with GCD as with pthreads or another API. And coordination is a compromise that we can make after that one.

Em ter, 29 de ago de 2017 às 05:23, Howard Lovatt via swift-evolution <[email protected] <mailto:[email protected]>> escreveu:

    @David,

    Using the `Future` library based on GCD that I have previously posted your
    example would be:

    let  image=  preprocessImage(downloadImage()) // These first two lines run 
in parallel
    let  text=  translate(downloadText())
    render(image: image.get ?? defaultImage,text: text.get ?? defaultText)


    The main difference, and I would argue an improvement, is that the `Future`
    version handles errors.

    So what advantage does async/await have over a `Future` library we can 
write today?


       -- Howard.

    On 29 August 2017 at 15:28, David Hart via swift-evolution
    <[email protected] <mailto:[email protected]>> wrote:


        On 29 Aug 2017, at 02:22, Xiaodi Wu via swift-evolution
        <[email protected] <mailto:[email protected]>> wrote:

        On Mon, Aug 28, 2017 at 16:10 Adam Kemp via swift-evolution
        <[email protected] <mailto:[email protected]>> wrote:

            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.


        Why couldn't you mix and match async/await and futures and get the
        queue-return behavior of futures if futures are built on top of 
async/await
        instead off the other way around?

        We could, but the syntax is much worse. Contrast:

        *async/await built on top of Futures*
        *
        *

        let  image=  preprocessImage(downloadImage())
        let  text=  translate(downloadText())
        awaitrender(image: image,text: text)


        *Futures built on top of async/await*
        *
        *

        let  image=  Future(downloadImage).then({preprocessImage($0) })
        let  text=  Future(downloadText).then({translate($0) })
        awaitrender(image: image.get(),text: text.get())


            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]
            <mailto:[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/).
                    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=  
awaitloadWebResource("dataprofile.txt")
                       let  imageResource=  
awaitloadWebResource("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

                        --
                        -- Howard.
                        _______________________________________________
                        swift-evolution mailing list
                        [email protected]
                        <mailto:[email protected]>
                        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



            _______________________________________________
            swift-evolution mailing list
            [email protected] <mailto:[email protected]>
            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


        _______________________________________________
        swift-evolution mailing list
        [email protected] <mailto:[email protected]>
        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



_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to