From fa78d15cb134b53d28bc488615b611b4bf278014 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Mon, 17 Mar 2025 11:14:01 +0900 Subject: [PATCH 1/4] Create NNNN-result-codable-and-async-init.md --- .../NNNN-result-codable-and-async-init.md | 83 +++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 proposals/NNNN-result-codable-and-async-init.md diff --git a/proposals/NNNN-result-codable-and-async-init.md b/proposals/NNNN-result-codable-and-async-init.md new file mode 100644 index 0000000000..b0740621f6 --- /dev/null +++ b/proposals/NNNN-result-codable-and-async-init.md @@ -0,0 +1,83 @@ +# Result: Codable conformance & async init + +* Proposal: SE-NNNN +* Author: [Konrad 'ktoso' Malawski](https://github.com/ktoso) +* Review Manager: TBD +* Status: **Pending implementation** +* Implementation: TBD + +## Introduction + +The `Result` type in Swift is often used to bridge between async and not async contexts, and could use some minor convenience improcements for both `async` contexts as well as encoding/decoding. + +## Motivation + +The Result type is often used to bridge between async and not async contexts, and it may be useful to initialize a `Result` from the result of an async computation, to then pass it along to other non-async code without unwrapping it first. This is currently inconvenitnt because the `init(catching:)` initializer is missing an `async` overload. + +The Result type also is often reached for to encode a success or failure situation. As Swift gained typed throws, it became possible to write a catching initializer using typed throws, that would capture a `Codable` error and this makes it nice to express catching errors which are intended to be encoded as a result. + +Those two changes allow us to write code like this: + +```swift +func accept(_: A) { ... } + +enum SomeCodableError: Error, Codable { ... } +func compute() throws(SomeCodableError) -> Int { ... } + +let result: Result = Result { + try await compute() +} + +accept(result) +``` + +## Detailed design + +We propose two additions to the Result type in the standard library: + +### Async catching initializer + +We propose to add an async "catching" initializer that is equivalent to the existing synchronous version of this initializer: + +```swift +extension Result where Success: ~Copyable { + /// Creates a new result by evaluating a throwing closure, capturing the + /// returned value as a success, or any thrown error as a failure. + /// + /// - Parameter body: A potentially throwing asynchronous closure to evaluate. + @_alwaysEmitIntoClient + public init(catching body: () async throws(Failure) -> Success) async { + do { + self = .success(try await body()) + } catch { + self = .failure(error) + } + } +} +``` + +### Conditional `Codable` conformance + +We propose to add a conditional Codable conformance, as follows: + +```swift +extension Result: Codable where Success: Codable, Failure: Codable {} +``` + +The `Codable` implementation is the default, synthesized, one which was defined in [SE-0295: Codable synthesis for enums with associated values](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0295-codable-synthesis-for-enums-with-associated-values.md). + +## Source compatibility + +All protocol conformances of an existing type to an existing protocol are potentially source breaking because users could have added the exact same conformances themselves. However, given that `EnumeratedSequence` do not expose their underlying sequences, there is no reasonable way anyone could have conformed to `Collection` themselves. + +## Effect on ABI stability + +The proposal is purely additive. + +The initializer can be backdeployed. + +## Alternatives considered + +### Don't provide these additions + +In practice this means developers have to write their own `Result` types frequently, which is managable but ineffective, especially as the shape and utility of those types is generally a 1:1 copy of the existing `Result` type. From dc8b33af0915e232dbbce1baa850a8efb9f36d27 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Mon, 17 Mar 2025 11:29:57 +0900 Subject: [PATCH 2/4] Update NNNN-result-codable-and-async-init.md --- proposals/NNNN-result-codable-and-async-init.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/NNNN-result-codable-and-async-init.md b/proposals/NNNN-result-codable-and-async-init.md index b0740621f6..53fed5b8e6 100644 --- a/proposals/NNNN-result-codable-and-async-init.md +++ b/proposals/NNNN-result-codable-and-async-init.md @@ -68,7 +68,7 @@ The `Codable` implementation is the default, synthesized, one which was defined ## Source compatibility -All protocol conformances of an existing type to an existing protocol are potentially source breaking because users could have added the exact same conformances themselves. However, given that `EnumeratedSequence` do not expose their underlying sequences, there is no reasonable way anyone could have conformed to `Collection` themselves. +This proposal is source compatible. Retroactive conformances are already warned about which would be the case for manual Codable conformances declared in adopter codebases. ## Effect on ABI stability From 81e72ec0b6fd69dc95547f1189cf93d2ebb65490 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Mon, 17 Mar 2025 21:00:12 +0900 Subject: [PATCH 3/4] Update NNNN-result-codable-and-async-init.md Co-authored-by: YOCKOW --- proposals/NNNN-result-codable-and-async-init.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/NNNN-result-codable-and-async-init.md b/proposals/NNNN-result-codable-and-async-init.md index 53fed5b8e6..ffb9a8b731 100644 --- a/proposals/NNNN-result-codable-and-async-init.md +++ b/proposals/NNNN-result-codable-and-async-init.md @@ -8,7 +8,7 @@ ## Introduction -The `Result` type in Swift is often used to bridge between async and not async contexts, and could use some minor convenience improcements for both `async` contexts as well as encoding/decoding. +The `Result` type in Swift is often used to bridge between async and not async contexts, and could use some minor convenience improvements for both `async` contexts as well as encoding/decoding. ## Motivation From 946d048a0e7982638333e4cb35881cd23179c390 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Mon, 17 Mar 2025 21:29:26 +0900 Subject: [PATCH 4/4] Apply suggestions from code review Co-authored-by: YOCKOW --- proposals/NNNN-result-codable-and-async-init.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/NNNN-result-codable-and-async-init.md b/proposals/NNNN-result-codable-and-async-init.md index ffb9a8b731..4f91f24003 100644 --- a/proposals/NNNN-result-codable-and-async-init.md +++ b/proposals/NNNN-result-codable-and-async-init.md @@ -22,9 +22,9 @@ Those two changes allow us to write code like this: func accept(_: A) { ... } enum SomeCodableError: Error, Codable { ... } -func compute() throws(SomeCodableError) -> Int { ... } +func compute() async throws(SomeCodableError) -> Int { ... } -let result: Result = Result { +let result: Result = await Result { try await compute() }