(Sorry if this has already been brought up, I did not read every message of this thread)
Very excited to hear that you started implementation of Typed Errors
for me this is a Swift improvement almost as exciting as macros!
What are your thoughts on (or already working code for) handling "aggregation"/"mapping" of typed errors?
I think this will be a super common pattern:
public enum NetworkError: Swift.Error, Sendable, Equatable {
case badHTTPResponseCode(Int)
}
public enum JSONError: Swift.Error, Sendable, Equatable {
// naive implementation `DecodingError` made equatable: https://gist.github.com/Sajjon/be1e0204930cef6549e9069094ad577a
case decodingError(DecodingError)
}
public enum MyError: Swift.Error, Sendable, Equatable {
case network(NetworkError)
case json(JSONError)
}
public func makeNetworkRequest(path: String) async throws(NetworkError) -> Data { ... }
public func decode<T: Decodable>(data: Data, as: T.Type = T.self) throws(JSONError) -> T { ... }
public struct User: Decodable, Sendable, Equatable { ... }
And then combine makeNetworkRequest and decode in a function which throws MyError, and then we would have to "manually" aggregate the two thrown error types into our MyError type, like so:
/// Uh... cumbersome boilerplaty mapping error types into MyError :/
public func fetchUser(...) async throws(MyError) -> User {
do {
let data = try await makeNetworkRequest(path: "user/..")
return try decode(data: data, as: User.self)
} catch let networkError as NetworkError {
return .networkError(networkError)
} catch let jsonError as JSONError {
return .json(jsonError)
}
}
(Here I assume we do not need a "catch-all" catch {} since the only two possible error types NetworkError and JSONError is accounted for.)
But what would be amazing, would be some kind of Swift compiler magic, which would allow me to just do:
public func fetchUser(...) async throws(MyError) -> User {
let data = try await makeNetworkRequest(path: "user/..")
return try decode(data: data, as: User.self)
}
I'm no compiler engineer, but it feels like this ought to be possible - if and only the MyError enum contained one single case with each of the thrown Error types. Meaning if I would add case foo(NetworkError) then such compiler magic would not be possible, since how to know which case the NetworkError would go into. And analogously if I were to change case networkError(NetworkError) into something silly like case networkError(NetworkError, url: String) then that would also make such compiler magic impossible.
Now, how badly do I want the two line version? Very very badly :D since just the increased indentation makes the code hard to read, and it is just doing a boilerplaty mapping. And for functions with many many different error types, such mappings really bloats the code base.
Surely you have considered this already? is it doable? :)