What is the type of `async let` declaration?

I have a family of questions related to the new async/await and structured concurrency features in Swift.

If we make an async let declaration, the Xcode reports the type of the declaration as the type that the object will have after it's been awaited for. I.E.: If we

async let image = downloadImage()

then if we inspect image, its type will be UIImage.

Question #1: Is there a way to pass an async let declaration to a function as a parameter?

I would like to have a function that accepts an async let declaration and transforms it somehow. The obvious way that comes to mind is to wrap the declaration into an async closure that includes an await of this declaration inside, but I wonder if there is a way to reference a type like Async<UIImage>, Task<UIImage> or something else so that an instance of this type could be declared as an async let

Question #2: Why does the Xcode report the type of an async let as the type of value after an await instead of some metaphysical type representing the presence of "awaitable" context? Why isn't the type of async let displayed as Task<UIImage>? Why didn't the language go the ECMAScript path where an async function is supposed to return a Promise that can be both subscribed to and awaited?

Answered by ole in 679561022

Question #2: Why does the Xcode report the type of an async let as the type of value after an await instead of some metaphysical type representing the presence of "awaitable" context? Why isn't the type of async let displayed as Task<UIImage>? Why didn't the language go the ECMAScript path where an async function is supposed to return a Promise that can be both subscribed to and awaited?

Not exposing async let child tasks in the type system was a deliberate decision to maintain the structured concurrency invariants, i.e. that child tasks are not allowed to escape their scope. The Swift Evolution proposal for async let has some background:

Alternatives considered

Explicit futures

As discussed in the structured concurrency proposal, we choose not to expose futures or Tasks for child tasks in task groups, because doing so either can undermine the hierarchy of tasks, by escaping from their parent task group and being awaited on indefinitely later, or would result in there being two kinds of future, one of which dynamically asserts that it's only used within the task's scope. async let allows for future-like data flow from child tasks to parent, without the need for general-purpose futures to be exposed.

If you need an explicit task handle, you must create an unstructured task with Task { … } or a detached task with Task.detached { … } (in Xcode 13 beta 1 these are named async { … } and detach { … }, respectively). These return a Task.Handle value, which is like a future.

Accepted Answer

Question #2: Why does the Xcode report the type of an async let as the type of value after an await instead of some metaphysical type representing the presence of "awaitable" context? Why isn't the type of async let displayed as Task<UIImage>? Why didn't the language go the ECMAScript path where an async function is supposed to return a Promise that can be both subscribed to and awaited?

Not exposing async let child tasks in the type system was a deliberate decision to maintain the structured concurrency invariants, i.e. that child tasks are not allowed to escape their scope. The Swift Evolution proposal for async let has some background:

Alternatives considered

Explicit futures

As discussed in the structured concurrency proposal, we choose not to expose futures or Tasks for child tasks in task groups, because doing so either can undermine the hierarchy of tasks, by escaping from their parent task group and being awaited on indefinitely later, or would result in there being two kinds of future, one of which dynamically asserts that it's only used within the task's scope. async let allows for future-like data flow from child tasks to parent, without the need for general-purpose futures to be exposed.

If you need an explicit task handle, you must create an unstructured task with Task { … } or a detached task with Task.detached { … } (in Xcode 13 beta 1 these are named async { … } and detach { … }, respectively). These return a Task.Handle value, which is like a future.

What is the type of `async let` declaration?
 
 
Q