[Question] Why does `beginAsync` rethrow errors?

Although I posted about this topic before, let me post this again
because I think it is important and I have received just few replies.
Sorry if I missed some discussion about it.

In the proposal (
https://gist.github.com/lattner/429b9070918248274f25b714dcfc7619 ),
`beginAsync` has the following signature.

func beginAsync(_ body: () async throws -> Void) rethrows -> Void

However, I think it is better to forbid `body` to throw errors, that
is to say, to change its signature to the following one.

func beginAsync(_ body: () async -> Void) -> Void

Even if `beginAsync` allows that `body` throws errors, it can rethrow
ones which are thrown before only first `await` call. In following
cases, `beginAsync` just has to make the program crash when `foo`
throws an error. It breaks safety for error handing by typed
propagation realized by `throws/try`.

// throws errors asynchronously
func foo() async throws -> Int { ... }

do {
    beginAsync {
        let a = try await foo()
        // uses `a` here
    }
} catch _ {
    // never reaches here
}

If `beginAsync` forbid `body` to throw errors, it can be detected as a
compilation error and is possible to fix it as follows.

beginAsync {
    do {
        let a = try await foo()
        // uses `a` here
    } catch _ {
        //  error handling
    }
}

And even when we want to write `try` calls in `beginAsync` before
first `await` call, those lines can be moved before the `beginAsync`
call.

// before ( `beginAsync` marked with `rethrows` )
do {
    beginAsync {
        let a = try bar()
        let b = try baz()
        let c = await qux(a, b)
        // uses `c` here
    }
catch _ {
    // error handling
}

// after ( `beginAsync` without `rethrows` )
do {
    let a = try bar()
    let b = try baz()
    beginAsync {
        let c = await qux(a, b)
        // uses `c` here
    }
catch _ {
    // error handling
}

So the functionalities of `beginAsync` seems be kept even if it forbid
`body` to throw errors.

What do you think about it?

···

--
Yuta

I think I agree with this. beginAsync is similar to C#’s async void functions, and one of the gotchas in C# is that it is never safe to allow an exception to be thrown from an async void function. The reason is that if the exception happens after the continuation then there won’t be any application code above it to catch that exception. As a result, the built in behavior is to immediately crash the app.

This is unavoidable in C# where it’s impossible to write a function that is guaranteed not to throw. The semantics of exception throwing don’t allow for that in C#.

Swift has the advantage in this case of being able to statically verify that a function doesn’t throw so we can do better.

So I would argue in favor of not allowing beginAsync to throw at all.

FWIW, I also still think it would be better if we allowed for async void functions instead of requiring beginAsync in the first place. If I had my way then we would have async void, but an async void would not be allowed to throw.

···

On Nov 7, 2017, at 7:04 PM, Yuta Koshizawa via swift-evolution <swift-evolution@swift.org> wrote:

Although I posted about this topic before, let me post this again
because I think it is important and I have received just few replies.
Sorry if I missed some discussion about it.

In the proposal (
https://gist.github.com/lattner/429b9070918248274f25b714dcfc7619 ),
`beginAsync` has the following signature.

func beginAsync(_ body: () async throws -> Void) rethrows -> Void

However, I think it is better to forbid `body` to throw errors, that
is to say, to change its signature to the following one.

func beginAsync(_ body: () async -> Void) -> Void

Even if `beginAsync` allows that `body` throws errors, it can rethrow
ones which are thrown before only first `await` call. In following
cases, `beginAsync` just has to make the program crash when `foo`
throws an error. It breaks safety for error handing by typed
propagation realized by `throws/try`.

// throws errors asynchronously
func foo() async throws -> Int { ... }

do {
   beginAsync {
       let a = try await foo()
       // uses `a` here
   }
} catch _ {
   // never reaches here
}

If `beginAsync` forbid `body` to throw errors, it can be detected as a
compilation error and is possible to fix it as follows.

beginAsync {
   do {
       let a = try await foo()
       // uses `a` here
   } catch _ {
       //  error handling
   }
}

And even when we want to write `try` calls in `beginAsync` before
first `await` call, those lines can be moved before the `beginAsync`
call.

// before ( `beginAsync` marked with `rethrows` )
do {
   beginAsync {
       let a = try bar()
       let b = try baz()
       let c = await qux(a, b)
       // uses `c` here
   }
catch _ {
   // error handling
}

// after ( `beginAsync` without `rethrows` )
do {
   let a = try bar()
   let b = try baz()
   beginAsync {
       let c = await qux(a, b)
       // uses `c` here
   }
catch _ {
   // error handling
}

So the functionalities of `beginAsync` seems be kept even if it forbid
`body` to throw errors.

What do you think about it?

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

I totally agree Yuta's suggestion.
beginAsync does not have to accept function which throws.

Adam

I don't think that C# style async void function invodation matchs swift.

If we can do, following code can be compile.

async func sendMessage() -> Void { ... }

func onButtonClick() {
sendMessage()
showAlert("message sent")
}

But in this case, the logic actually programmer desired is
showing alert after sendMessage completed.
Above style code is not easy readable about execution fall through
without waiting completion of sendMessage to showAlert.
With this rule, compiler can not help us to find such mistaken code.

This seems like unchecked exception problem in other languages.
Keep starting asynchronous invodation explicit suck like
throwing function invocation explicitly marked with `try` or `do`.

···

2017年11月8日(水) 13:28 Adam Kemp via swift-evolution <swift-evolution@swift.org >:

I think I agree with this. beginAsync is similar to C#’s async void
functions, and one of the gotchas in C# is that it is never safe to allow
an exception to be thrown from an async void function. The reason is that
if the exception happens after the continuation then there won’t be any
application code above it to catch that exception. As a result, the built
in behavior is to immediately crash the app.

This is unavoidable in C# where it’s impossible to write a function that
is guaranteed not to throw. The semantics of exception throwing don’t allow
for that in C#.

Swift has the advantage in this case of being able to statically verify
that a function doesn’t throw so we can do better.

So I would argue in favor of not allowing beginAsync to throw at all.

FWIW, I also still think it would be better if we allowed for async void
functions instead of requiring beginAsync in the first place. If I had my
way then we would have async void, but an async void would not be allowed
to throw.

> On Nov 7, 2017, at 7:04 PM, Yuta Koshizawa via swift-evolution < > swift-evolution@swift.org> wrote:
>
> Although I posted about this topic before, let me post this again
> because I think it is important and I have received just few replies.
> Sorry if I missed some discussion about it.
>
> In the proposal (
> https://gist.github.com/lattner/429b9070918248274f25b714dcfc7619 ),
> `beginAsync` has the following signature.
>
> ```
> func beginAsync(_ body: () async throws -> Void) rethrows -> Void
> ```
>
> However, I think it is better to forbid `body` to throw errors, that
> is to say, to change its signature to the following one.
>
> ```
> func beginAsync(_ body: () async -> Void) -> Void
> ```
>
> Even if `beginAsync` allows that `body` throws errors, it can rethrow
> ones which are thrown before only first `await` call. In following
> cases, `beginAsync` just has to make the program crash when `foo`
> throws an error. It breaks safety for error handing by typed
> propagation realized by `throws/try`.
>
> ```
> // throws errors asynchronously
> func foo() async throws -> Int { ... }
>
> do {
> beginAsync {
> let a = try await foo()
> // uses `a` here
> }
> } catch _ {
> // never reaches here
> }
> ```
>
> If `beginAsync` forbid `body` to throw errors, it can be detected as a
> compilation error and is possible to fix it as follows.
>
> ```
> beginAsync {
> do {
> let a = try await foo()
> // uses `a` here
> } catch _ {
> // error handling
> }
> }
> ```
>
> And even when we want to write `try` calls in `beginAsync` before
> first `await` call, those lines can be moved before the `beginAsync`
> call.
>
> ```
> // before ( `beginAsync` marked with `rethrows` )
> do {
> beginAsync {
> let a = try bar()
> let b = try baz()
> let c = await qux(a, b)
> // uses `c` here
> }
> catch _ {
> // error handling
> }
>
> // after ( `beginAsync` without `rethrows` )
> do {
> let a = try bar()
> let b = try baz()
> beginAsync {
> let c = await qux(a, b)
> // uses `c` here
> }
> catch _ {
> // error handling
> }
> ```
>
> So the functionalities of `beginAsync` seems be kept even if it forbid
> `body` to throw errors.
>
> What do you think about it?
>
> --
> Yuta
> _______________________________________________
> 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

--
omochimetaru

(I changed the subject since it’s not really the same topic)

The short answer is that this code has the same problem even without async void:

func sendMessage() {
    beginAsync {
        // …
    }
}

func onButtonClick() {
  sendMessage()
  showAlert("message sent")
}

If you want someone to be able to wait for something to finish then you make your function awaitable. So what does that mean for an async function that doesn’t really return anything? To understand what I’m thinking it helps to understand how C# does it, since that’s the model I’m proposing, and I think it works really well. In C# it works like this:

void NoReturn() { }
int IntReturn() { return 0; }

async Task NoReturnAsync() { await Task.Yield(); }
async Task<int> IntReturnAsync() { await Task.Yield(); return 0; }

async void NoReturnAsyncVoid() { await Task.Yield(); }

async Task Caller()
{
    NoReturn();
    int i = IntReturn();
    await NoReturnAsync();
    int j = await IntReturnAsync();

    NoReturnAsync(); // this cannot be awaited
}

An important difference is that in C# the async keyword does not make a function awaitable. Notice how NoReturnAsyncVoid is marked as “async”, but the caller cannot use “await” with it. So what do you do if you want to wait for it to finish, like in your example? Well notice that another function NoReturnAsync doesn’t actually appear to return anything, even though its return type is Task. The compiler transforms the function into pieces where the first piece (the one actually called by the caller) returns a Task object. A bare Task is only used for waiting for completion, whereas a Task<T> also holds a return value. When you make a function that returns T async then you change the return type to Task<T>. When you make a void function async and want to allow the caller to wait for it to finish then you change the void to Task. When you make a void function async but want the caller to not wait for it to finish then you leave it as void.

This is subtle, but consider this alternative form:

void NoReturn() { }
int IntReturn() { return 0; }

Task NoReturnAsync() { return Task.Yield(); }
Task<int> IntReturnAsync() { return Task.Yield(); return 0; }

void NoReturnAsyncVoid() { Task.Yield(); }

async Task Caller()
{
    NoReturn();
    int i = IntReturn();
    await NoReturnAsync();
    int j = await IntReturnAsync();

    NoReturnAsync(); // this cannot be awaited
}

I changed all of the “async" functions above except for Caller by removing the “async” keyword and (where applicable) added return statements. Now none of those functions is async, but they are functionally equivalent. Notice that the async Caller (which I didn’t change at all) can still await the ones that return Task or Task<T>. That’s because, again, in C# the async keyword does not mean “this can be awaited”. Being able to await a function call is orthogonal to whether that function is marked as async.

Async means only one thing: that function can use the await keyword, and it will be decomposed by the compiler accordingly. So what can you await? Anything that’s “awaitable”. :slight_smile: Basically if the return type of the function has a GetAwaiter method that returns a type that has a few properties and methods then you can use await on that function. This is described here: <https://blogs.msdn.microsoft.com/pfxteam/2011/01/13/await-anything/>. Task happens to have a GetAwaiter (<https://msdn.microsoft.com/en-us/library/system.threading.tasks.task.getawaiter(v=vs.110).aspx <https://msdn.microsoft.com/en-us/library/system.threading.tasks.task.getawaiter(v=vs.110).aspx>>) method that returns a TaskAwaiter (<https://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.taskawaiter(v=vs.110).aspx>) <https://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.taskawaiter(v=vs.110).aspx>)>, which happens to have an IsCompleted property and GetResult and OnCompleted methods.

You can make any type awaitable by supplying a suitable GetAwaiter implementation, even using an extension method. I think this could probably be done with protocols and extensions in Swift.

Back to the problem you described: wouldn’t an async void method be confusing to callers who expect it to finish before returning? Obviously if it’s a function that requires the caller to wait before continuing then it should be made awaitable. So in C# you wouldn’t make an async void function that you expected people to want to wait for, and in C# you have that choice: you can make an awaitable function that returns nothing to the awaiter.

The question, though, is “are there any use cases for an async function that doesn’t require its caller to wait for it?” Or, put another way, is there a use case for an async function that a caller can call as if it’s just any other void function? There definitely are multiple use cases for that, but the most important one probably is event callbacks, most often UI event callbacks. Think of a button click handler. When a button is clicked maybe you want to start some async task. This is a common pattern in .Net:

private async void OnButtonClicked(object sender, EventArgs e)
{
    Button button = (Button)sender;
    button.IsEnabled = false;
    await DoSomethingAsync();
    button.IsEnabled = true;
}

In Swift with the current proposal that would be something like this:

func OnButtonClicked(_ sender:AnyObject) {
    let button = sender as! UIButton
    button.isEnabled = false
    beginAsync {
        await DoSomethingAsync()
        button.isEnabled = true
    }
}

If you compare those two implementations I think it’s obvious which one is clearer. The advantage of async/await is that it makes async code look like sync code by preserving the logical code flow. It makes the compiler do the hard work of splitting the function up. If you have to use beginAsync then you’re forced to do what the compiler could do for you. You have to think about where it goes, and how much should be inside that block or outside. Someone might be tempted to write that method above like this:

func OnButtonClicked(_ sender:AnyObject) {
    let button = sender as! UIButton
    button.isEnabled = false
    beginAsync {
        await DoSomethingAsync()
    }
    button.isEnabled = true
}

That code would be wrong. Is that obvious at a glance? I don’t think so. What about this?

func OnButtonClicked(_ sender:AnyObject) {
    beginAsync {
        let button = sender as! UIButton
        button.isEnabled = false
        await DoSomethingAsync()
        button.isEnabled = true
    }
}

That code does the right thing. So why wouldn’t you always write it that way? Serious question: what is the use case for using beginAsync for only part of a function? It looks like every example in Chris’s proposal wraps the entire contents of the function (except for one in an initializer).

If beginAsync is used almost exclusively to wrap the entire contents of void functions that you want to be async then why wouldn’t we just make it possible to make that function itself async and use await directly? It seems much clearer to me. async/await is already kind of confusing for newcomers, and I think beginAsync makes it worse by introducing a new concept that is easily misused and shouldn’t be necessary.

···

On Nov 7, 2017, at 10:22 PM, omochi.metaru <omochi.metaru@gmail.com> wrote:

I totally agree Yuta's suggestion.
beginAsync does not have to accept function which throws.

> Adam

I don't think that C# style async void function invodation matchs swift.

If we can do, following code can be compile.

async func sendMessage() -> Void { ... }

func onButtonClick() {
	sendMessage()
	showAlert("message sent")
}

But in this case, the logic actually programmer desired is
showing alert after sendMessage completed.
Above style code is not easy readable about execution fall through
without waiting completion of sendMessage to showAlert.
With this rule, compiler can not help us to find such mistaken code.

This seems like unchecked exception problem in other languages.
Keep starting asynchronous invodation explicit suck like
throwing function invocation explicitly marked with `try` or `do`.

2017年11月8日(水) 13:28 Adam Kemp via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>>:
I think I agree with this. beginAsync is similar to C#’s async void functions, and one of the gotchas in C# is that it is never safe to allow an exception to be thrown from an async void function. The reason is that if the exception happens after the continuation then there won’t be any application code above it to catch that exception. As a result, the built in behavior is to immediately crash the app.

This is unavoidable in C# where it’s impossible to write a function that is guaranteed not to throw. The semantics of exception throwing don’t allow for that in C#.

Swift has the advantage in this case of being able to statically verify that a function doesn’t throw so we can do better.

So I would argue in favor of not allowing beginAsync to throw at all.

FWIW, I also still think it would be better if we allowed for async void functions instead of requiring beginAsync in the first place. If I had my way then we would have async void, but an async void would not be allowed to throw.

> On Nov 7, 2017, at 7:04 PM, Yuta Koshizawa via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>
> Although I posted about this topic before, let me post this again
> because I think it is important and I have received just few replies.
> Sorry if I missed some discussion about it.
>
> In the proposal (
> https://gist.github.com/lattner/429b9070918248274f25b714dcfc7619 ),
> `beginAsync` has the following signature.
>
> ```
> func beginAsync(_ body: () async throws -> Void) rethrows -> Void
> ```
>
> However, I think it is better to forbid `body` to throw errors, that
> is to say, to change its signature to the following one.
>
> ```
> func beginAsync(_ body: () async -> Void) -> Void
> ```
>
> Even if `beginAsync` allows that `body` throws errors, it can rethrow
> ones which are thrown before only first `await` call. In following
> cases, `beginAsync` just has to make the program crash when `foo`
> throws an error. It breaks safety for error handing by typed
> propagation realized by `throws/try`.
>
> ```
> // throws errors asynchronously
> func foo() async throws -> Int { ... }
>
> do {
> beginAsync {
> let a = try await foo()
> // uses `a` here
> }
> } catch _ {
> // never reaches here
> }
> ```
>
> If `beginAsync` forbid `body` to throw errors, it can be detected as a
> compilation error and is possible to fix it as follows.
>
> ```
> beginAsync {
> do {
> let a = try await foo()
> // uses `a` here
> } catch _ {
> // error handling
> }
> }
> ```
>
> And even when we want to write `try` calls in `beginAsync` before
> first `await` call, those lines can be moved before the `beginAsync`
> call.
>
> ```
> // before ( `beginAsync` marked with `rethrows` )
> do {
> beginAsync {
> let a = try bar()
> let b = try baz()
> let c = await qux(a, b)
> // uses `c` here
> }
> catch _ {
> // error handling
> }
>
> // after ( `beginAsync` without `rethrows` )
> do {
> let a = try bar()
> let b = try baz()
> beginAsync {
> let c = await qux(a, b)
> // uses `c` here
> }
> catch _ {
> // error handling
> }
> ```
>
> So the functionalities of `beginAsync` seems be kept even if it forbid
> `body` to throw errors.
>
> What do you think about it?
>
> --
> Yuta
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org <mailto:swift-evolution@swift.org>
> 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
--
omochimetaru

Before i make a comment on this topic i need to confirm something:

On original proposal, this:

func processImageData1(completionBlock: (result: Image) -> Void) {
    loadWebResource("dataprofile.txt") { dataResource in
        loadWebResource("imagedata.dat") { imageResource in
            decodeImage(dataResource, imageResource) { imageTmp in
                dewarpAndCleanupImage(imageTmp) { imageResult in
                    completionBlock(imageResult)
                }
            }
        }
    }
}

Is converted to this:

func processImageData1() async -> Image {
    let dataResource = await loadWebResource("dataprofile.txt")
    let imageResource = await loadWebResource("imagedata.dat")
    let imageTmp = await decodeImage(dataResource, imageResource)
    let imageResult = await dewarpAndCleanupImage(imageTmp)
    return imageResult
}

And has other sentences explain that await will not block the thread etc...

So for me, this:

func OnButtonClicked(_ sender:AnyObject) {

    let button = sender as! UIButton
    button.isEnabled = false
    beginAsync {
        await DoSomethingAsync()
        button.isEnabled = true
    }
}

Does not make any sense... Because await will not block the thread we can
assume (like the exemple on proposal) that compiler will actually do the
same what we will do using GDC and pick everyone to until the context ends
and encapsulate as completion handle to the away call.

So we will only have this:

func OnButtonClicked(_ sender:AnyObject) {

        let button = sender as! UIButton

        button.isEnabled = false

        await DoSomethingAsync()

        button.isEnabled = true

}

I don't know how to handle reentrante or something like that, but the
proposal give me the impression this behavior.

In the other way, await will block the thread!

I know the proposal give us this exemple:

@IBAction func buttonDidClick(sender:AnyObject) {
  // 1 beginAsync {
    // 2 let image = await processImage()
    imageView.image = image
  }
  // 3}

But this is different, because "3" will be fired before "2".

And this one:

@IBAction func buttonDidClick(sender:AnyObject) {
  beginAsync {
    let image = await processImageData()
    // Do the update on the main thread/queue since it owns imageView.
   mainQ.async {
      imageView.image = image
    }
  }
}

Does not make any sense too.

The only way to await to do not block the thread is make a early return
like beginAsync and encapsulate as completion handle the rest of the code.

If not, i don't see how the first exemple of the proposal will be made.

···

Em qua, 8 de nov de 2017 às 15:55, Adam Kemp via swift-evolution < swift-evolution@swift.org> escreveu:

(I changed the subject since it’s not really the same topic)

The short answer is that this code has the same problem even without async
void:

func sendMessage() {
    beginAsync {
        // …
    }
}

func onButtonClick() {
sendMessage()
showAlert("message sent")
}

If you want someone to be able to wait for something to finish then you
make your function awaitable. So what does that mean for an async function
that doesn’t really return anything? To understand what I’m thinking it
helps to understand how C# does it, since that’s the model I’m proposing,
and I think it works really well. In C# it works like this:

void NoReturn() { }
int IntReturn() { return 0; }

async Task NoReturnAsync() { await Task.Yield(); }
async Task<int> IntReturnAsync() { await Task.Yield(); return 0; }

async void NoReturnAsyncVoid() { await Task.Yield(); }

async Task Caller()
{
    NoReturn();
    int i = IntReturn();
    await NoReturnAsync();
    int j = await IntReturnAsync();

    NoReturnAsync(); // this cannot be awaited
}

An important difference is that in C# the async keyword does not make a
function awaitable. Notice how NoReturnAsyncVoid is marked as “async”, but
the caller cannot use “await” with it. So what do you do if you want to
wait for it to finish, like in your example? Well notice that another
function NoReturnAsync doesn’t actually appear to return anything, even
though its return type is Task. The compiler transforms the function into
pieces where the first piece (the one actually called by the caller)
returns a Task object. A bare Task is only used for waiting for completion,
whereas a Task<T> also holds a return value. When you make a function that
returns T async then you change the return type to Task<T>. When you make a
void function async and want to allow the caller to wait for it to finish
then you change the void to Task. When you make a void function async but
want the caller to not wait for it to finish then you leave it as void.

This is subtle, but consider this alternative form:

void NoReturn() { }
int IntReturn() { return 0; }

Task NoReturnAsync() { return Task.Yield(); }
Task<int> IntReturnAsync() { return Task.Yield(); return 0; }

void NoReturnAsyncVoid() { Task.Yield(); }

async Task Caller()
{
    NoReturn();
    int i = IntReturn();
    await NoReturnAsync();
    int j = await IntReturnAsync();

    NoReturnAsync(); // this cannot be awaited
}

I changed all of the “async" functions above except for Caller by removing
the “async” keyword and (where applicable) added return statements. Now
none of those functions is async, but they are functionally equivalent.
Notice that the async Caller (which I didn’t change at all) can still await
the ones that return Task or Task<T>. That’s because, again, in C# the
async keyword does not mean “this can be awaited”. Being able to await a
function call is orthogonal to whether that function is marked as async.

Async means only one thing: that function can use the await keyword, and
it will be decomposed by the compiler accordingly. So what can you await?
Anything that’s “awaitable”. :slight_smile: Basically if the return type of the
function has a GetAwaiter method that returns a type that has a few
properties and methods then you can use await on that function. This is
described here: <
https://blogs.msdn.microsoft.com/pfxteam/2011/01/13/await-anything/>.
Task happens to have a GetAwaiter (<
https://msdn.microsoft.com/en-us/library/system.threading.tasks.task.getawaiter(v=vs.110).aspx>)
method that returns a TaskAwaiter (<
https://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.taskawaiter(v=vs.110).aspx>),
which happens to have an IsCompleted property and GetResult and OnCompleted
methods.

You can make any type awaitable by supplying a suitable GetAwaiter
implementation, even using an extension method. I think this could probably
be done with protocols and extensions in Swift.

Back to the problem you described: wouldn’t an async void method be
confusing to callers who expect it to finish before returning? Obviously if
it’s a function that requires the caller to wait before continuing then it
should be made awaitable. So in C# you wouldn’t make an async void function
that you expected people to want to wait for, and in C# you have that
choice: you can make an awaitable function that returns nothing to the
awaiter.

The question, though, is “are there any use cases for an async function
that doesn’t require its caller to wait for it?” Or, put another way, is
there a use case for an async function that a caller can call as if it’s
just any other void function? There definitely are multiple use cases for
that, but the most important one probably is event callbacks, most often UI
event callbacks. Think of a button click handler. When a button is clicked
maybe you want to start some async task. This is a common pattern in .Net:

private async void OnButtonClicked(object sender, EventArgs e)
{
    Button button = (Button)sender;
    button.IsEnabled = false;
    await DoSomethingAsync();
    button.IsEnabled = true;
}

In Swift with the current proposal that would be something like this:

func OnButtonClicked(_ sender:AnyObject) {

    let button = sender as! UIButton
    button.isEnabled = false
    beginAsync {
        await DoSomethingAsync()
        button.isEnabled = true
    }
}

If you compare those two implementations I think it’s obvious which one is
clearer. The advantage of async/await is that it makes async code look like
sync code by preserving the logical code flow. It makes the compiler do the
hard work of splitting the function up. If you have to use beginAsync then
you’re forced to do what the compiler could do for you. You have to think
about where it goes, and how much should be inside that block or outside.
Someone might be tempted to write that method above like this:

func OnButtonClicked(_ sender:AnyObject) {

    let button = sender as! UIButton
    button.isEnabled = false

    beginAsync {

        await DoSomethingAsync()
    }

    button.isEnabled = true
}

That code would be wrong. Is that obvious at a glance? I don’t think so.
What about this?

func OnButtonClicked(_ sender:AnyObject) {

    beginAsync {

        let button = sender as! UIButton
        button.isEnabled = false

        await DoSomethingAsync()

        button.isEnabled = true
    }

}

That code does the right thing. So why wouldn’t you always write it that
way? Serious question: what is the use case for using beginAsync for only
part of a function? It looks like every example in Chris’s proposal wraps
the entire contents of the function (except for one in an initializer).

If beginAsync is used almost exclusively to wrap the entire contents of
void functions that you want to be async then why wouldn’t we just make it
possible to make that function itself async and use await directly? It
seems much clearer to me. async/await is already kind of confusing for
newcomers, and I think beginAsync makes it worse by introducing a new
concept that is easily misused and shouldn’t be necessary.

On Nov 7, 2017, at 10:22 PM, omochi.metaru <omochi.metaru@gmail.com> > wrote:

I totally agree Yuta's suggestion.
beginAsync does not have to accept function which throws.

> Adam

I don't think that C# style async void function invodation matchs swift.

If we can do, following code can be compile.

async func sendMessage() -> Void { ... }

func onButtonClick() {
sendMessage()
showAlert("message sent")
}

But in this case, the logic actually programmer desired is
showing alert after sendMessage completed.
Above style code is not easy readable about execution fall through
without waiting completion of sendMessage to showAlert.
With this rule, compiler can not help us to find such mistaken code.

This seems like unchecked exception problem in other languages.
Keep starting asynchronous invodation explicit suck like
throwing function invocation explicitly marked with `try` or `do`.

2017年11月8日(水) 13:28 Adam Kemp via swift-evolution <
swift-evolution@swift.org>:

I think I agree with this. beginAsync is similar to C#’s async void
functions, and one of the gotchas in C# is that it is never safe to allow
an exception to be thrown from an async void function. The reason is that
if the exception happens after the continuation then there won’t be any
application code above it to catch that exception. As a result, the built
in behavior is to immediately crash the app.

This is unavoidable in C# where it’s impossible to write a function that
is guaranteed not to throw. The semantics of exception throwing don’t allow
for that in C#.

Swift has the advantage in this case of being able to statically verify
that a function doesn’t throw so we can do better.

So I would argue in favor of not allowing beginAsync to throw at all.

FWIW, I also still think it would be better if we allowed for async void
functions instead of requiring beginAsync in the first place. If I had my
way then we would have async void, but an async void would not be allowed
to throw.

> On Nov 7, 2017, at 7:04 PM, Yuta Koshizawa via swift-evolution < >> swift-evolution@swift.org> wrote:
>
> Although I posted about this topic before, let me post this again
> because I think it is important and I have received just few replies.
> Sorry if I missed some discussion about it.
>
> In the proposal (
> https://gist.github.com/lattner/429b9070918248274f25b714dcfc7619 ),
> `beginAsync` has the following signature.
>
> ```
> func beginAsync(_ body: () async throws -> Void) rethrows -> Void
> ```
>
> However, I think it is better to forbid `body` to throw errors, that
> is to say, to change its signature to the following one.
>
> ```
> func beginAsync(_ body: () async -> Void) -> Void
> ```
>
> Even if `beginAsync` allows that `body` throws errors, it can rethrow
> ones which are thrown before only first `await` call. In following
> cases, `beginAsync` just has to make the program crash when `foo`
> throws an error. It breaks safety for error handing by typed
> propagation realized by `throws/try`.
>
> ```
> // throws errors asynchronously
> func foo() async throws -> Int { ... }
>
> do {
> beginAsync {
> let a = try await foo()
> // uses `a` here
> }
> } catch _ {
> // never reaches here
> }
> ```
>
> If `beginAsync` forbid `body` to throw errors, it can be detected as a
> compilation error and is possible to fix it as follows.
>
> ```
> beginAsync {
> do {
> let a = try await foo()
> // uses `a` here
> } catch _ {
> // error handling
> }
> }
> ```
>
> And even when we want to write `try` calls in `beginAsync` before
> first `await` call, those lines can be moved before the `beginAsync`
> call.
>
> ```
> // before ( `beginAsync` marked with `rethrows` )
> do {
> beginAsync {
> let a = try bar()
> let b = try baz()
> let c = await qux(a, b)
> // uses `c` here
> }
> catch _ {
> // error handling
> }
>
> // after ( `beginAsync` without `rethrows` )
> do {
> let a = try bar()
> let b = try baz()
> beginAsync {
> let c = await qux(a, b)
> // uses `c` here
> }
> catch _ {
> // error handling
> }
> ```
>
> So the functionalities of `beginAsync` seems be kept even if it forbid
> `body` to throw errors.
>
> What do you think about it?
>
> --
> Yuta
> _______________________________________________
> 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

--
omochimetaru

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

Hi,
I have two opinion about async void function,

1. I think async(or sync) function should has a returning value even if
type is void.

let result = await somethingAsyncVoid()

In this case the `result` will available when `someThingAsyncVoid` was
completed.
Someone can omit it, but it should be usable for other one who want to use
it.
Even if someone omit returning value in async void function, the function
should return result(void) when process was finished.

2. About async throws function.
We should consider about combine `async` and `throws` function. Some
failure may happen asynchronously, we cannot solve the `async error` if we
ignore await async void function.

···

2017-11-09 2:55 GMT+09:00 Adam Kemp via swift-evolution < swift-evolution@swift.org>:

(I changed the subject since it’s not really the same topic)

The short answer is that this code has the same problem even without async
void:

func sendMessage() {
    beginAsync {
        // …
    }
}

func onButtonClick() {
sendMessage()
showAlert("message sent")
}

If you want someone to be able to wait for something to finish then you
make your function awaitable. So what does that mean for an async function
that doesn’t really return anything? To understand what I’m thinking it
helps to understand how C# does it, since that’s the model I’m proposing,
and I think it works really well. In C# it works like this:

void NoReturn() { }
int IntReturn() { return 0; }

async Task NoReturnAsync() { await Task.Yield(); }
async Task<int> IntReturnAsync() { await Task.Yield(); return 0; }

async void NoReturnAsyncVoid() { await Task.Yield(); }

async Task Caller()
{
    NoReturn();
    int i = IntReturn();
    await NoReturnAsync();
    int j = await IntReturnAsync();

    NoReturnAsync(); // this cannot be awaited
}

An important difference is that in C# the async keyword does not make a
function awaitable. Notice how NoReturnAsyncVoid is marked as “async”, but
the caller cannot use “await” with it. So what do you do if you want to
wait for it to finish, like in your example? Well notice that another
function NoReturnAsync doesn’t actually appear to return anything, even
though its return type is Task. The compiler transforms the function into
pieces where the first piece (the one actually called by the caller)
returns a Task object. A bare Task is only used for waiting for completion,
whereas a Task<T> also holds a return value. When you make a function that
returns T async then you change the return type to Task<T>. When you make a
void function async and want to allow the caller to wait for it to finish
then you change the void to Task. When you make a void function async but
want the caller to not wait for it to finish then you leave it as void.

This is subtle, but consider this alternative form:

void NoReturn() { }
int IntReturn() { return 0; }

Task NoReturnAsync() { return Task.Yield(); }
Task<int> IntReturnAsync() { return Task.Yield(); return 0; }

void NoReturnAsyncVoid() { Task.Yield(); }

async Task Caller()
{
    NoReturn();
    int i = IntReturn();
    await NoReturnAsync();
    int j = await IntReturnAsync();

    NoReturnAsync(); // this cannot be awaited
}

I changed all of the “async" functions above except for Caller by removing
the “async” keyword and (where applicable) added return statements. Now
none of those functions is async, but they are functionally equivalent.
Notice that the async Caller (which I didn’t change at all) can still await
the ones that return Task or Task<T>. That’s because, again, in C# the
async keyword does not mean “this can be awaited”. Being able to await a
function call is orthogonal to whether that function is marked as async.

Async means only one thing: that function can use the await keyword, and
it will be decomposed by the compiler accordingly. So what can you await?
Anything that’s “awaitable”. :slight_smile: Basically if the return type of the
function has a GetAwaiter method that returns a type that has a few
properties and methods then you can use await on that function. This is
described here: <https://blogs.msdn.microsoft.
com/pfxteam/2011/01/13/await-anything/>. Task happens to have a
GetAwaiter (<https://msdn.microsoft.com/en-us/library/system.
threading.tasks.task.getawaiter(v=vs.110).aspx>) method that returns a
TaskAwaiter (<https://msdn.microsoft.com/en-us/library/system.runtime.
compilerservices.taskawaiter(v=vs.110).aspx>), which happens to have an
IsCompleted property and GetResult and OnCompleted methods.

You can make any type awaitable by supplying a suitable GetAwaiter
implementation, even using an extension method. I think this could probably
be done with protocols and extensions in Swift.

Back to the problem you described: wouldn’t an async void method be
confusing to callers who expect it to finish before returning? Obviously if
it’s a function that requires the caller to wait before continuing then it
should be made awaitable. So in C# you wouldn’t make an async void function
that you expected people to want to wait for, and in C# you have that
choice: you can make an awaitable function that returns nothing to the
awaiter.

The question, though, is “are there any use cases for an async function
that doesn’t require its caller to wait for it?” Or, put another way, is
there a use case for an async function that a caller can call as if it’s
just any other void function? There definitely are multiple use cases for
that, but the most important one probably is event callbacks, most often UI
event callbacks. Think of a button click handler. When a button is clicked
maybe you want to start some async task. This is a common pattern in .Net:

private async void OnButtonClicked(object sender, EventArgs e)
{
    Button button = (Button)sender;
    button.IsEnabled = false;
    await DoSomethingAsync();
    button.IsEnabled = true;
}

In Swift with the current proposal that would be something like this:

func OnButtonClicked(_ sender:AnyObject) {

    let button = sender as! UIButton
    button.isEnabled = false
    beginAsync {
        await DoSomethingAsync()
        button.isEnabled = true
    }
}

If you compare those two implementations I think it’s obvious which one is
clearer. The advantage of async/await is that it makes async code look like
sync code by preserving the logical code flow. It makes the compiler do the
hard work of splitting the function up. If you have to use beginAsync then
you’re forced to do what the compiler could do for you. You have to think
about where it goes, and how much should be inside that block or outside.
Someone might be tempted to write that method above like this:

func OnButtonClicked(_ sender:AnyObject) {

    let button = sender as! UIButton
    button.isEnabled = false

    beginAsync {

        await DoSomethingAsync()
    }

    button.isEnabled = true
}

That code would be wrong. Is that obvious at a glance? I don’t think so.
What about this?

func OnButtonClicked(_ sender:AnyObject) {

    beginAsync {

        let button = sender as! UIButton
        button.isEnabled = false

        await DoSomethingAsync()

        button.isEnabled = true
    }

}

That code does the right thing. So why wouldn’t you always write it that
way? Serious question: what is the use case for using beginAsync for only
part of a function? It looks like every example in Chris’s proposal wraps
the entire contents of the function (except for one in an initializer).

If beginAsync is used almost exclusively to wrap the entire contents of
void functions that you want to be async then why wouldn’t we just make it
possible to make that function itself async and use await directly? It
seems much clearer to me. async/await is already kind of confusing for
newcomers, and I think beginAsync makes it worse by introducing a new
concept that is easily misused and shouldn’t be necessary.

On Nov 7, 2017, at 10:22 PM, omochi.metaru <omochi.metaru@gmail.com> > wrote:

I totally agree Yuta's suggestion.
beginAsync does not have to accept function which throws.

> Adam

I don't think that C# style async void function invodation matchs swift.

If we can do, following code can be compile.

async func sendMessage() -> Void { ... }

func onButtonClick() {
sendMessage()
showAlert("message sent")
}

But in this case, the logic actually programmer desired is
showing alert after sendMessage completed.
Above style code is not easy readable about execution fall through
without waiting completion of sendMessage to showAlert.
With this rule, compiler can not help us to find such mistaken code.

This seems like unchecked exception problem in other languages.
Keep starting asynchronous invodation explicit suck like
throwing function invocation explicitly marked with `try` or `do`.

2017年11月8日(水) 13:28 Adam Kemp via swift-evolution <
swift-evolution@swift.org>:

I think I agree with this. beginAsync is similar to C#’s async void
functions, and one of the gotchas in C# is that it is never safe to allow
an exception to be thrown from an async void function. The reason is that
if the exception happens after the continuation then there won’t be any
application code above it to catch that exception. As a result, the built
in behavior is to immediately crash the app.

This is unavoidable in C# where it’s impossible to write a function that
is guaranteed not to throw. The semantics of exception throwing don’t allow
for that in C#.

Swift has the advantage in this case of being able to statically verify
that a function doesn’t throw so we can do better.

So I would argue in favor of not allowing beginAsync to throw at all.

FWIW, I also still think it would be better if we allowed for async void
functions instead of requiring beginAsync in the first place. If I had my
way then we would have async void, but an async void would not be allowed
to throw.

> On Nov 7, 2017, at 7:04 PM, Yuta Koshizawa via swift-evolution < >> swift-evolution@swift.org> wrote:
>
> Although I posted about this topic before, let me post this again
> because I think it is important and I have received just few replies.
> Sorry if I missed some discussion about it.
>
> In the proposal (
> https://gist.github.com/lattner/429b9070918248274f25b714dcfc7619 ),
> `beginAsync` has the following signature.
>
> ```
> func beginAsync(_ body: () async throws -> Void) rethrows -> Void
> ```
>
> However, I think it is better to forbid `body` to throw errors, that
> is to say, to change its signature to the following one.
>
> ```
> func beginAsync(_ body: () async -> Void) -> Void
> ```
>
> Even if `beginAsync` allows that `body` throws errors, it can rethrow
> ones which are thrown before only first `await` call. In following
> cases, `beginAsync` just has to make the program crash when `foo`
> throws an error. It breaks safety for error handing by typed
> propagation realized by `throws/try`.
>
> ```
> // throws errors asynchronously
> func foo() async throws -> Int { ... }
>
> do {
> beginAsync {
> let a = try await foo()
> // uses `a` here
> }
> } catch _ {
> // never reaches here
> }
> ```
>
> If `beginAsync` forbid `body` to throw errors, it can be detected as a
> compilation error and is possible to fix it as follows.
>
> ```
> beginAsync {
> do {
> let a = try await foo()
> // uses `a` here
> } catch _ {
> // error handling
> }
> }
> ```
>
> And even when we want to write `try` calls in `beginAsync` before
> first `await` call, those lines can be moved before the `beginAsync`
> call.
>
> ```
> // before ( `beginAsync` marked with `rethrows` )
> do {
> beginAsync {
> let a = try bar()
> let b = try baz()
> let c = await qux(a, b)
> // uses `c` here
> }
> catch _ {
> // error handling
> }
>
> // after ( `beginAsync` without `rethrows` )
> do {
> let a = try bar()
> let b = try baz()
> beginAsync {
> let c = await qux(a, b)
> // uses `c` here
> }
> catch _ {
> // error handling
> }
> ```
>
> So the functionalities of `beginAsync` seems be kept even if it forbid
> `body` to throw errors.
>
> What do you think about it?
>
> --
> Yuta
> _______________________________________________
> 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

--
omochimetaru

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

So for me, this:

func OnButtonClicked(_ sender:AnyObject) {
    let button = sender as! UIButton
    button.isEnabled = false
    beginAsync {
        await DoSomethingAsync()
        button.isEnabled = true
    }
}

Does not make any sense... Because await will not block the thread we can assume (like the exemple on proposal) that compiler will actually do the same what we will do using GDC and pick everyone to until the context ends and encapsulate as completion handle to the away call.

I don’t understand what you’re saying here. In terms of GCD the above code is basically equivalent to this:

func buttonClicked(_ sender:AnyObject) {
    let button = sender as! UIButton
    button.isEnabled = false
    doSomethingAsync { // completion callback, assume it’s on the main queue
        button.isEnabled = true
    }
}

And this one:

@IBAction func buttonDidClick(sender:AnyObject) {
  beginAsync {
    let image = await processImageData()
    // Do the update on the main thread/queue since it owns imageView.
    mainQ.async {
      imageView.image = image
    }
  }
}

Does not make any sense too.

The only way to await to do not block the thread is make a early return like beginAsync and encapsulate as completion handle the rest of the code.

This is why “await” can only be used in a function that is marked as “async”. The “async” keyword tells the compiler that this function will be broken up into parts, and its return value will be produced asynchronously.

One thing that might be confusing is that in the examples using “beginAsync” the trailing closure is the async function. That’s not obvious because there’s no “async” keyword, but it’s there in the signature of the closure argument:

func beginAsync(_ body: () async throws -> Void) rethrows -> Void

I’m not sure how the proposed design handles type checking for async closures. Is async part of the type? Can I declare a local closure variable as async? What are the rules for conversion between async and non-async closures? Is it a warning or error to use a closure literal without an async keyword that is assigned to an async closure variable or passed as an async closure argument?

C# dodges all of these issues because async isn't part of the type system. Closures that don’t return anything can be declared async just like void functions, and code that uses void-returning closures doesn’t need to care whether the implementation is async or not.

···

On Nov 9, 2017, at 11:02 AM, Wallacy <wallacyf@gmail.com> wrote:

I’m not sure how the proposed design handles type checking for async
closures. Is async part of the type? Can I declare a local closure variable
as async? What are the rules for conversion between async and non-async
closures? Is it a warning or error to use a closure literal without an async
keyword that is assigned to an async closure variable or passed as an async
closure argument?

It has been already realized for `throws`.

// This works in Swift 4
func foo(_ f: (Bar) throws -> Baz)

// This can be checked in the same way
func foo(_ f: (Bar) async -> Baz)

Proposed `async/await` in Swift are analogous to `throws/try`. So a
lot of things about `async/await` can be imagined when we think about
`throws/try`. It is also true for `(Foo) async -> Void`. We can use it
in the same way as we use `(Foo) throws -> Void`.

`async/await` as an analogy to `throws/try` works well because both of
them can be mapped to monads. `throws` is similar to `Result` and
`async` is similar to `Promise` ( `Future` ).

// `a` and `b` are similar
func a() throws -> Int
func b() -> Result<Int>

// `c` and `d` are similar
func c() async -> Int
func d() -> Promise<Int>

// `a` : `b` == `c` : `d`
// `a` : `c` == `b` : `d`

`try` and `await` are also similar to `flatMap`. ( I think most
popular `Promise` is one in JavaScript. Although it does not have
`flatMap`, its `then` method can be considered as `flatMap`. )

let x = try a()
// uses `x` here

b().flatMap { x in
  // uses `x` here
}

let y = await c()
// uses `y` here

d().flatMap { y in
  // uses `y` here
}

So `throws` : `try` : `Result` == `async` : `await` : `Promise` and
`throws` : `async` == `try` : `await` == `Result` : `Promise`. I think
those relations are beautiful and proposed `async/await` fits well to
Swift because we have already had `throws/try`.

···

--
Yuta

2017-11-10 6:45 GMT+09:00 Adam Kemp via swift-evolution <swift-evolution@swift.org>:

On Nov 9, 2017, at 11:02 AM, Wallacy <wallacyf@gmail.com> wrote:

So for me, this:

func OnButtonClicked(_ sender:AnyObject) {

    let button = sender as! UIButton
    button.isEnabled = false
    beginAsync {
        await DoSomethingAsync()
        button.isEnabled = true
    }
}

Does not make any sense... Because await will not block the thread we can
assume (like the exemple on proposal) that compiler will actually do the
same what we will do using GDC and pick everyone to until the context ends
and encapsulate as completion handle to the away call.

I don’t understand what you’re saying here. In terms of GCD the above code
is basically equivalent to this:

func buttonClicked(_ sender:AnyObject) {
    let button = sender as! UIButton
    button.isEnabled = false
    doSomethingAsync { // completion callback, assume it’s on the main queue
        button.isEnabled = true
    }
}

And this one:

@IBAction func buttonDidClick(sender:AnyObject) {
  beginAsync {
    let image = await processImageData()
    // Do the update on the main thread/queue since it owns imageView.
    mainQ.async {
      imageView.image = image
    }
  }
}

Does not make any sense too.

The only way to await to do not block the thread is make a early return like
beginAsync and encapsulate as completion handle the rest of the code.

This is why “await” can only be used in a function that is marked as
“async”. The “async” keyword tells the compiler that this function will be
broken up into parts, and its return value will be produced asynchronously.

One thing that might be confusing is that in the examples using “beginAsync”
the trailing closure is the async function. That’s not obvious because
there’s no “async” keyword, but it’s there in the signature of the closure
argument:

func beginAsync(_ body: () async throws -> Void) rethrows -> Void

I’m not sure how the proposed design handles type checking for async
closures. Is async part of the type? Can I declare a local closure variable
as async? What are the rules for conversion between async and non-async
closures? Is it a warning or error to use a closure literal without an async
keyword that is assigned to an async closure variable or passed as an async
closure argument?

C# dodges all of these issues because async isn't part of the type system.
Closures that don’t return anything can be declared async just like void
functions, and code that uses void-returning closures doesn’t need to care
whether the implementation is async or not.

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

I’m not sure that answered my questions.

Can I do this?

let asyncClosure:() async -> Void = { await doSomethingAsync() } // Can you declare an async closure variable?

beginAsync(asyncClosure)

What about this?

let syncClosure:() -> Void = { doSomethingSync() }

beginAsync(syncClosure) // Can a non-async closure be passed to a function expecting a async closure?

And this?

func callSyncClosure(_ closure:() -> ()) { closure() }

let asyncClosure:() async -> Void = { await doSomethingAsync() }

callSyncClosure(asyncClosure) // Can an async closure be passed to a function expecting a non-async closure?

Here are my thoughts on each of these:

Can you declare an async closure variable? If async is part of the type then it would be very strange not to allow this. I think the proposal implies that you can, but only by way of an example about inferring whether a closure is async or not.

Can a non-async closure be passed to a function expecting a sync closure? This seems like an obvious conversion. I feel like disallowing this would probably make things harder, but I haven’t thought it through enough to say exactly how. Are there reasons that this shouldn’t work, though?

Can an async closure be passed to a function expecting a sync closure? This may be controversial, but I think this should be allowed too. I think a caller that expects a void-returning synchronous function should be able to call an async function and treat it as a call-and-forget.

It’s weird to me that we would allow you to have async void closures but not async void functions, but in order to support beginAsync we have to have async void closures. So if we can make that work then why couldn’t we just make async void functions work and dispense with beginAsync entirely?

···

On Nov 10, 2017, at 1:38 AM, Yuta Koshizawa via swift-evolution <swift-evolution@swift.org> wrote:

I’m not sure how the proposed design handles type checking for async
closures. Is async part of the type? Can I declare a local closure variable
as async? What are the rules for conversion between async and non-async
closures? Is it a warning or error to use a closure literal without an async
keyword that is assigned to an async closure variable or passed as an async
closure argument?

It has been already realized for `throws`.

// This works in Swift 4
func foo(_ f: (Bar) throws -> Baz)

// This can be checked in the same way
func foo(_ f: (Bar) async -> Baz)

Proposed `async/await` in Swift are analogous to `throws/try`. So a
lot of things about `async/await` can be imagined when we think about
`throws/try`. It is also true for `(Foo) async -> Void`. We can use it
in the same way as we use `(Foo) throws -> Void`.

`async/await` as an analogy to `throws/try` works well because both of
them can be mapped to monads. `throws` is similar to `Result` and
`async` is similar to `Promise` ( `Future` ).

// `a` and `b` are similar
func a() throws -> Int
func b() -> Result<Int>

// `c` and `d` are similar
func c() async -> Int
func d() -> Promise<Int>

// `a` : `b` == `c` : `d`
// `a` : `c` == `b` : `d`

`try` and `await` are also similar to `flatMap`. ( I think most
popular `Promise` is one in JavaScript. Although it does not have
`flatMap`, its `then` method can be considered as `flatMap`. )

let x = try a()
// uses `x` here

b().flatMap { x in
 // uses `x` here
}

let y = await c()
// uses `y` here

d().flatMap { y in
 // uses `y` here
}

So `throws` : `try` : `Result` == `async` : `await` : `Promise` and
`throws` : `async` == `try` : `await` == `Result` : `Promise`. I think
those relations are beautiful and proposed `async/await` fits well to
Swift because we have already had `throws/try`.

--
Yuta

2017-11-10 6:45 GMT+09:00 Adam Kemp via swift-evolution
<swift-evolution@swift.org>:

On Nov 9, 2017, at 11:02 AM, Wallacy <wallacyf@gmail.com> wrote:

So for me, this:

func OnButtonClicked(_ sender:AnyObject) {

   let button = sender as! UIButton
   button.isEnabled = false
   beginAsync {
       await DoSomethingAsync()
       button.isEnabled = true
   }
}

Does not make any sense... Because await will not block the thread we can
assume (like the exemple on proposal) that compiler will actually do the
same what we will do using GDC and pick everyone to until the context ends
and encapsulate as completion handle to the away call.

I don’t understand what you’re saying here. In terms of GCD the above code
is basically equivalent to this:

func buttonClicked(_ sender:AnyObject) {
   let button = sender as! UIButton
   button.isEnabled = false
   doSomethingAsync { // completion callback, assume it’s on the main queue
       button.isEnabled = true
   }
}

And this one:

@IBAction func buttonDidClick(sender:AnyObject) {
beginAsync {
   let image = await processImageData()
   // Do the update on the main thread/queue since it owns imageView.
   mainQ.async {
     imageView.image = image
   }
}
}

Does not make any sense too.

The only way to await to do not block the thread is make a early return like
beginAsync and encapsulate as completion handle the rest of the code.

This is why “await” can only be used in a function that is marked as
“async”. The “async” keyword tells the compiler that this function will be
broken up into parts, and its return value will be produced asynchronously.

One thing that might be confusing is that in the examples using “beginAsync”
the trailing closure is the async function. That’s not obvious because
there’s no “async” keyword, but it’s there in the signature of the closure
argument:

func beginAsync(_ body: () async throws -> Void) rethrows -> Void

I’m not sure how the proposed design handles type checking for async
closures. Is async part of the type? Can I declare a local closure variable
as async? What are the rules for conversion between async and non-async
closures? Is it a warning or error to use a closure literal without an async
keyword that is assigned to an async closure variable or passed as an async
closure argument?

C# dodges all of these issues because async isn't part of the type system.
Closures that don’t return anything can be declared async just like void
functions, and code that uses void-returning closures doesn’t need to care
whether the implementation is async or not.

_______________________________________________
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

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.

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 = { ... }
···

--
Yuta

2017-11-11 11:02 GMT+09:00 Adam Kemp <adam_kemp@apple.com>:

I’m not sure that answered my questions.

Can I do this?

let asyncClosure:() async -> Void = { await doSomethingAsync() } // Can you
declare an async closure variable?

beginAsync(asyncClosure)

What about this?

let syncClosure:() -> Void = { doSomethingSync() }

beginAsync(syncClosure) // Can a non-async closure be passed to a function
expecting a async closure?

And this?

func callSyncClosure(_ closure:() -> ()) { closure() }

let asyncClosure:() async -> Void = { await doSomethingAsync() }

callSyncClosure(asyncClosure) // Can an async closure be passed to a
function expecting a non-async closure?

Here are my thoughts on each of these:

Can you declare an async closure variable? If async is part of the type then
it would be very strange not to allow this. I think the proposal implies
that you can, but only by way of an example about inferring whether a
closure is async or not.

Can a non-async closure be passed to a function expecting a sync closure?
This seems like an obvious conversion. I feel like disallowing this would
probably make things harder, but I haven’t thought it through enough to say
exactly how. Are there reasons that this shouldn’t work, though?

Can an async closure be passed to a function expecting a sync closure? This
may be controversial, but I think this should be allowed too. I think a
caller that expects a void-returning synchronous function should be able to
call an async function and treat it as a call-and-forget.

It’s weird to me that we would allow you to have async void closures but not
async void functions, but in order to support beginAsync we have to have
async void closures. So if we can make that work then why couldn’t we just
make async void functions work and dispense with beginAsync entirely?

On Nov 10, 2017, at 1:38 AM, Yuta Koshizawa via swift-evolution > <swift-evolution@swift.org> wrote:

I’m not sure how the proposed design handles type checking for async
closures. Is async part of the type? Can I declare a local closure variable
as async? What are the rules for conversion between async and non-async
closures? Is it a warning or error to use a closure literal without an async
keyword that is assigned to an async closure variable or passed as an async
closure argument?

It has been already realized for `throws`.

// This works in Swift 4
func foo(_ f: (Bar) throws -> Baz)

// This can be checked in the same way
func foo(_ f: (Bar) async -> Baz)

Proposed `async/await` in Swift are analogous to `throws/try`. So a
lot of things about `async/await` can be imagined when we think about
`throws/try`. It is also true for `(Foo) async -> Void`. We can use it
in the same way as we use `(Foo) throws -> Void`.

`async/await` as an analogy to `throws/try` works well because both of
them can be mapped to monads. `throws` is similar to `Result` and
`async` is similar to `Promise` ( `Future` ).

// `a` and `b` are similar
func a() throws -> Int
func b() -> Result<Int>

// `c` and `d` are similar
func c() async -> Int
func d() -> Promise<Int>

// `a` : `b` == `c` : `d`
// `a` : `c` == `b` : `d`

`try` and `await` are also similar to `flatMap`. ( I think most
popular `Promise` is one in JavaScript. Although it does not have
`flatMap`, its `then` method can be considered as `flatMap`. )

let x = try a()
// uses `x` here

b().flatMap { x in
 // uses `x` here
}

let y = await c()
// uses `y` here

d().flatMap { y in
 // uses `y` here
}

So `throws` : `try` : `Result` == `async` : `await` : `Promise` and
`throws` : `async` == `try` : `await` == `Result` : `Promise`. I think
those relations are beautiful and proposed `async/await` fits well to
Swift because we have already had `throws/try`.

--
Yuta

2017-11-10 6:45 GMT+09:00 Adam Kemp via swift-evolution
<swift-evolution@swift.org>:

On Nov 9, 2017, at 11:02 AM, Wallacy <wallacyf@gmail.com> wrote:

So for me, this:

func OnButtonClicked(_ sender:AnyObject) {

   let button = sender as! UIButton
   button.isEnabled = false
   beginAsync {
       await DoSomethingAsync()
       button.isEnabled = true
   }
}

Does not make any sense... Because await will not block the thread we can
assume (like the exemple on proposal) that compiler will actually do the
same what we will do using GDC and pick everyone to until the context ends
and encapsulate as completion handle to the away call.

I don’t understand what you’re saying here. In terms of GCD the above code
is basically equivalent to this:

func buttonClicked(_ sender:AnyObject) {
   let button = sender as! UIButton
   button.isEnabled = false
   doSomethingAsync { // completion callback, assume it’s on the main queue
       button.isEnabled = true
   }
}

And this one:

@IBAction func buttonDidClick(sender:AnyObject) {
beginAsync {
   let image = await processImageData()
   // Do the update on the main thread/queue since it owns imageView.
   mainQ.async {
     imageView.image = image
   }
}
}

Does not make any sense too.

The only way to await to do not block the thread is make a early return like
beginAsync and encapsulate as completion handle the rest of the code.

This is why “await” can only be used in a function that is marked as
“async”. The “async” keyword tells the compiler that this function will be
broken up into parts, and its return value will be produced asynchronously.

One thing that might be confusing is that in the examples using “beginAsync”
the trailing closure is the async function. That’s not obvious because
there’s no “async” keyword, but it’s there in the signature of the closure
argument:

func beginAsync(_ body: () async throws -> Void) rethrows -> Void

I’m not sure how the proposed design handles type checking for async
closures. Is async part of the type? Can I declare a local closure variable
as async? What are the rules for conversion between async and non-async
closures? Is it a warning or error to use a closure literal without an async
keyword that is assigned to an async closure variable or passed as an async
closure argument?

C# dodges all of these issues because async isn't part of the type system.
Closures that don’t return anything can be declared async just like void
functions, and code that uses void-returning closures doesn’t need to care
whether the implementation is async or not.

_______________________________________________
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

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?

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.

···

On Nov 11, 2017, at 6:24 AM, Yuta Koshizawa <koher@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`.

···

2017-11-12 2:57 GMT+09:00 Adam Kemp <adam.kemp@apple.com>:

On Nov 11, 2017, at 6:24 AM, Yuta Koshizawa <koher@koherent.org> wrote:

--
Yuta

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>:

2017-11-12 2:57 GMT+09:00 Adam Kemp <adam.kemp@apple.com>:

On Nov 11, 2017, at 6:24 AM, Yuta Koshizawa <koher@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
https://lists.swift.org/mailman/listinfo/swift-evolution

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> 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>:
> 2017-11-12 2:57 GMT+09:00 Adam Kemp <adam.kemp@apple.com>:
>>
>>
>>> On Nov 11, 2017, at 6:24 AM, Yuta Koshizawa <koher@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
> https://lists.swift.org/mailman/listinfo/swift-evolution
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

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.kemp@apple.com <mailto:adam.kemp@apple.com>>:
>>
>>
>>> On Nov 11, 2017, at 6:24 AM, Yuta Koshizawa <koher@koherent.org <mailto:koher@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
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto: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

I kind of like that idea. I’d rather have a Task/Future/Promise/whatever, but if that’s not going to happen then Void vs. Never seems like a reasonable way of distinguishing these two cases.

···

On Nov 12, 2017, at 9:55 AM, Xiaodi Wu <xiaodi.wu@gmail.com> 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.kemp@apple.com <mailto:adam.kemp@apple.com>>:
>>
>>
>>> On Nov 11, 2017, at 6:24 AM, Yuta Koshizawa <koher@koherent.org <mailto:koher@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
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

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.

Tasks in C# don’t represent something that can be enqueued. They represent work that is already taking place asynchronously, and the Task object can be used to install a callback or to fetch the result synchronously (ideally only after the work is already completed). You can’t use a Task to control which queue the work runs on (there may be no queue at all). You can use it to configure how an await will resume (essentially, which queue should be used), but that’s different from configuring on which queue the work will run.

- 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.

The async keyword isn’t used to denote a function that does work asynchronously. It’s a bit of a misnomer in that way. What it actually denotes is “this function can use the await keyword”. That’s why there is a concept of “async void” in C#: because it’s possible to use the await keyword in a function that can’t itself be awaited. It exists for the same reason that beginAsync exists in the proposal. My argument is just that it is easier to use the feature if you just allow non-awaitable functions to use await and have some kind of syntax for that.

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)

This has been discussed before. Getting the current queue is discouraged for client code because it’s usually an invalid thing to ask for (there isn’t just a single current queue), but there’s nothing wrong with the runtime or standard library using that to do meaningful things. Getting the current queue to resume an async task is a reasonable thing to do, and it could be built in. Look for the earlier emails from Pierre Habouzit about this topic for more information.

···

On Nov 12, 2017, at 1:47 PM, David Waite <david@alkaline-solutions.com> wrote:

However, equally important to the case I was making earlier is that you should be able to call an “async Never” function as if it were a non-async Void function. This is especially important for cases where you want to pass a callback to something (like an event handler), and you want the callback to be able to use await. So as long as that still works I could live with "async Never”.

···

On Nov 13, 2017, at 9:49 AM, Adam Kemp <adam_kemp@apple.com> wrote:

I kind of like that idea. I’d rather have a Task/Future/Promise/whatever, but if that’s not going to happen then Void vs. Never seems like a reasonable way of distinguishing these two cases.

On Nov 12, 2017, at 9:55 AM, Xiaodi Wu <xiaodi.wu@gmail.com <mailto:xiaodi.wu@gmail.com>> 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.kemp@apple.com <mailto:adam.kemp@apple.com>>:
>>
>>
>>> On Nov 11, 2017, at 6:24 AM, Yuta Koshizawa <koher@koherent.org <mailto:koher@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
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution