>From what I understand, the C# and Swift designs are different:

- C# async functions immediately return a task which can be enqueued to 
complete the work (including selecting which queue for UI cases, etc). Tasks 
have callback behavior attached to them.

- Swift async functions immediately enqueue to do work along with a callback 
function to be executed once work finishes. There is no first-class 
representation of a Future or the like, at least by default (a callback 
function could be promoted to a future). There is no support for selecting 
which queue work or the callback runs on (although your callback could 
immediately enqueue work on the appropriate thread, but this does increase 
latency in busy systems)

Assuming I got the above all correct there is no async Task vs async Void in 
swift, because a function which dispatches work to a queue without taking a 
callback just returns void. Those functions aren’t “async” at all, you can call 
them normally to dispatch work just as easily as you could call them within an 
async block, and they would have the same behavior. They also can’t be 
“awaited”, because there is no callback mechanism to tell when the work 
completes.

Thats not to say I wouldn’t prefer the Task-type design, but it isn’t how most 
of the legacy asynchronous code is written to work (and would have some issues 
with GCD, since you can’t discover your current queue in order to enqueue more 
work there by default)

// Function which asynchronously dispatches work, without triggering any 
callback. If it throws, the error represents a synchronous issue (e.g. bad 
parameters or state)

func doWorkBlindly();

// Function which asynchronously dispatches work, returning a callback without 
data but possibly indicating an error
func doWorkExceptionally() async -> Void;

// Function which asynchronously dispatches work ,returning data and possibly 
an error
func doValuableWork() async -> Data;

// works fine
doWorkBlindly()
beginAsync {
    // has the same behavior as above
    doWorkBlindly()
    do {
        // can only be called within an async block
        await doWorkExceptionally()
        let result = await doValuableWork()
    }
    catch {
       fatalError()
    }
}

-DW


> On Nov 12, 2017, at 10:56 AM, Xiaodi Wu via swift-evolution 
> <swift-evolution@swift.org> wrote:
> 
> Sorry, I'm just getting into this conversation late and am by no means 
> experienced in the area, but why can't the one where you *don't* want the 
> caller to wait for the result be spelled `async -> Never`? Theoretically, 
> `async -> Void` means you're awaiting a result with only one possible value, 
> but if you're not waiting at all, then there is truly no result, yes?
> 
> 
> On Sun, Nov 12, 2017 at 9:27 AM, Yuta Koshizawa via swift-evolution 
> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
> Sorry, I had got some confusion. Please let me retry to explain.
> 
> As you said, C# provides three kinds of async functions: `async Void`,
> `async Task` and `async Task<Foo>`. All of them are necessary and
> Swift should provide same functionalities.
> 
> When we think about `async/await` in Swift, because we have already
> had `throws/try`, it is desired that `async/await` in Swift is
> consistent with `throws/try`. So it is better to have `async/await`
> without introducing a type like `Task` (or `Promise`).
> 
> Even if we employ `async/await` without `Task`, Swift has to provides
> functionalities to implement "three kinds of async functions" in C#.
> However if `async -> Void` in Swift works similarly to `async Void` in
> C#, how can we express ones like `async Task` in C#? I think there are
> two possibilities:
> 
> 1. Calling `async -> Void` functions without `await` in Swift works
> like `async Void` in C# and calling them *with* `await` works like
> `async Task` in C#.
> 2. Calling `async -> Void` functions without `await` in Swift works
> like `async Void` in C# and never support something like `async Task`
> in C#.
> 
> I think 2 is impermissible. For example, handling completion events of
> asynchronous operations without result values needs something like
> `async Task` in C#. However, with 1, we lose the benefit of static
> checks by the compiler. Because both of `fooAsync()` without `await`
> and `await fooAsync()` are allowed, even if we want it to work like
> `async Task` in C# and forget to mark `await`, the compiler tell us
> nothing and it works like `async Void` in C#. It causes unexpected
> behaviors. It is hard to fix such kinds of bugs. So I think
> introducing `beginAsync` is better.
> 
> --
> Yuta
> 
> 
> 2017-11-12 10:23 GMT+09:00 Yuta Koshizawa via swift-evolution
> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>>:
> > 2017-11-12 2:57 GMT+09:00 Adam Kemp <adam.k...@apple.com 
> > <mailto:adam.k...@apple.com>>:
> >>
> >>
> >>> On Nov 11, 2017, at 6:24 AM, Yuta Koshizawa <ko...@koherent.org 
> >>> <mailto:ko...@koherent.org>> wrote:
> >>>
> >>> If you replace `async` with `throws`, you can get answers.
> >>>
> >>>
> >>>> Can you declare an async closure variable?
> >>>
> >>> Yes. Like `let throwingClosure:() throws -> Void = { ... }`.
> >>>
> >>>
> >>>> Can a non-async closure be passed to a function expecting a async 
> >>>> closure?
> >>>
> >>> Yes. Like we can pass `() -> Void` to a function expecting a throwing
> >>> closure `() throws -> Void`.
> >>>
> >>> It is possible because `(Foo) throws -> Bar` is a supertype of `(Foo)
> >>> -> Bar`. `(Foo) async -> Bar` is a supertype of `(Foo) -> Bar` in the
> >>> same way.
> >>>
> >>> To treat an async function as a sync function is legal. It is similar
> >>> to make a `Promise` by `Promise(value)` which is completed
> >>> immediately.
> >>>
> >>>
> >>>> Can an async closure be passed to a function expecting a non-async 
> >>>> closure?
> >>>
> >>> No. `() -> Void` is a subtype of `() async -> Void`. It is same as
> >>> passing `() throws -> Void` to a function expecting `() -> Void` is
> >>> not allowed.
> >>
> >> But why not? Just asserting that it must work the same as throws
> >> is not a convincing argument. You have to justify why it must work
> >> that way. I think there is good reason to allow it, which I have described.
> >> What reason is there to disallow it?
> >
> > `() async -> Void` needs to be called with `await` because it prevents
> > us from forgetting handling asynchronous operations.
> >
> > If we use callbacks to handle asynchronous operations, it is shown to
> > us by a compiler as a compilation error.
> >
> > ```
> > func fooAsync(_ handler: () -> Void) -> Void { ... }
> >
> > fooAsync() // compilation error
> >
> > fooAsync {
> >   // handles a completion event here
> > }
> > ```
> >
> > With proposed `async/await`, it is realized similarly like below.
> >
> > ```
> > func fooAsync() async -> Void { ... }
> >
> > fooAsync() // compilation error
> >
> > await fooAsync()
> > // handles a completion event here
> > ```
> >
> > However, if async void functions work like `beginAsync`, we can easily
> > forget it and it can cause unexpected behaviors.
> >
> > ```
> > func fooAsync() async -> Void { ... }
> >
> > fooAsync() // OK
> > // hard to know this line is executed asynchronously
> > ```
> >
> > Readability also suffers seriously. If we don't know `bar` in the
> > following code is a async function, it is impossible to expect lines
> > after `baz()` are executed asynchronously.
> >
> > ```
> > foo()
> > bar()
> > baz()
> > qux()
> > ```
> >
> >
> >>>> It’s weird to me that we would allow you to have async void closures but 
> >>>> not async void functions
> >>>
> >>> I am not sure what you mean. "async void closures" and "async void
> >>> functions" have a same type. Following two are almost same.
> >>>
> >>> ```
> >>> func foo() async -> Void { ... }
> >>> let foo: () async -> Void = { ... }
> >>> ```
> >>
> >> What started this thread is my suggestion that you should be able to write
> >> an async void function. The current proposal doesn’t allow that. That’s why
> >> you have to use beginAsync.
> >>
> >> I don’t think that makes sense. It sounds like you also think that would 
> >> be strange,
> >> hence your assumption that you could.
> >
> > By the reasons I wrote above, we need `await` even for async void
> > functions for checks by compilers. Then it is required to provide a
> > way to write entry points of async functions. That is `beginAsync`.
> >
> > --
> > Yuta
> > _______________________________________________
> > swift-evolution mailing list
> > swift-evolution@swift.org <mailto:swift-evolution@swift.org>
> > https://lists.swift.org/mailman/listinfo/swift-evolution 
> > <https://lists.swift.org/mailman/listinfo/swift-evolution>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
> https://lists.swift.org/mailman/listinfo/swift-evolution 
> <https://lists.swift.org/mailman/listinfo/swift-evolution>
> 
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution

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

Reply via email to