Async await and Result type

I had to write this little extension for a personal project. Then I couldn't help wondering why this is not already part of Swift? Because to me it seems like a missing piece of the puzzle without which the move towards "async await" wouldn't be complete. It looks like such a glaring omission that I keep wondering if there is good reason why this should not be done? Also asked a question here


extension Result where Failure == Error {
    
    // Write an async version of the init that takes a throwing closure. In this case the throwing
    // closure is an async function.
    init(catchingAsync closure: @escaping () async throws -> Success) async {
        // Call the async function and await the result.
        do {
            let value = try await closure()
            self = .success(value)
        } catch {
            self = .failure(error)
        }
    }

    // An async version of map. This is a function that takes a throwing closure and returns a Result.
    // in this case the throwing closure is an async function.
    func asyncMap<T>(_ transform: @escaping (Success) async throws -> T) async -> Result<T, Error> {
        switch self {
        case let .success(value):
            do {
                let transformedValue = try await transform(value)
                return .success(transformedValue)
            } catch {
                return .failure(error)
            }
        case let .failure(error):
            return .failure(error)
        }
    }

    //An async version of flatMap. This is a function that takes a throwing closure and returns a Result.
    // in this case the throwing closure is an async function.
    func asyncFlatMap<T>(_ transform: @escaping (Success) async throws -> Result<T, Error>) async -> Result<T, Error> {
        switch self {
        case let .success(value):
            do {
                let transformedValue = try await transform(value)
                return transformedValue
            } catch {
                return .failure(error)
            }
        case let .failure(error):
            return .failure(error)
        }
    }

    // An async version of mapError. This is a function that takes a throwing closure and returns a Result.
    // in this case the throwing closure is an async function.
    func asyncMapError(_ transform: @escaping (Error) async throws -> Error) async -> Result<Success, Error> {
        switch self {
        case let .success(value):
            return .success(value)
        case let .failure(error):
            do {
                let transformedError = try await transform(error)
                return .failure(transformedError)
            } catch {
                return .failure(error)
            }
        }
    }
    
    // An async version of flatMapError. This is a function that takes a throwing closure and returns a Result.
    // in this case the throwing closure is an async function.
    func asyncFlatMapError(_ transform: @escaping (Error) async throws -> Result<Success, Error>) async -> Result<Success, Error> {
        switch self {
        case let .success(value):
            return .success(value)
        case let .failure(error):
            do {
                let transformedError = try await transform(error)
                return transformedError
            } catch {
                return .failure(error)
            }
        }
    }
}
1 Like

related discussion: Looking for something in between a `TaskGroup` and a `ThrowingTaskGroup` - #6 by taylorswift

1 Like