The following does not work in swift:nightly-main-focal
:
struct Baz { }
func foo() async -> Baz { Baz() }
func bar() async -> Baz { Baz() }
func | (
lhs: @autoclosure () async -> Baz,
rhs: @autoclosure () async -> Baz
) async -> Baz {
Baz()
}
/// Error: call is 'async' in an autoclosure argument that is not marked with 'await'
let _ = await foo() | bar()
Adjusting the last line to let _ = await foo() | await bar()
creates a different issue: // Error: 'await' cannot appear to the right of a non-assignment operator
, and the original error is still raised for foo()
.
There is some discussion of async
, await
, and @autoclosure
here, but it doesn't mention operators and it isn't 100% clear on the how the compiler would interpret await computeArgumentLater(await getIntSlowly())
(my reading of this is that there would be a suspension point both prior to and in the body of computeArgumentLater
).
Overall, I think the right move is to only apply autoclosure semantics when an argument is not labeled await
. This makes it explicit that await computeArgumentLater(await getIntSlowly())
would introduce a suspension point prior to the argument and the computed value of getIntSlowly
would be promoted to a new async autoclosure before being passed to computeArgumentLater
. Operators would also just work as expected (await foo() | bar
).
As an added benefit, this would allow us to lift the restriction on functions with async autoclosure arguments needing to be async. It seems perfectly valid if computeArgumentLater
detached a task where it called getIntSlowly
and returned. In this case, labelling the statement with await
isn't even semantically helpful since there would be no suspension points in the resulting closure (referring to let closure = { … }
from the proposal).
Finally, if you are wondering "why is George even worried about this", I'm prototyping a framework for doing shell-script-like tasks in Swift. In this framework, I want a statement like cat("Foo.txt")
to effectively run the command $ cat Foo.txt
to completion, but I also want cat("Foo.txt") | sed("s/Bar/Baz/")
to do the right thing (execute cat
and sed
simultaneously, piping input from one to the other). In my implementation I hoped to achieve this by making |
take async autoclosure arguments, but then I ran into the aforementioned problem. If you are interested in looking at the code, I have a work in progress here:
https://github.com/GeorgeLyon/Swish/blob/2e69e330fd58b3455ed3394de88b73dc3bda73e7/Sources/Sample/main.swift#L6-L7