Running async code inside flatMap on Result type

Hi all,
I have encountered an issue with the following code that fails to compile:

let message = Result { try decoder.decode(ConsumerMessage.self, from: payload) }
    .flatMap { (msg: ConsumerMessage) async in
        let analysed = await wa.doesContainViolentOrHarmfulMeaning(keywords: msg.interests)
        if (analysed) {
            return Result.failure(ConsumerError.harmfulOrViolentInterests("Interests contain harmful or violent meaning"))
        } else {
            return await redis.sadd(msg.interests, to: RedisKey(id)).get() != 0 ? Result.failure(ConsumerError.storeInterestsFailed("Failed to publish the interests")) : Result.success("Interests have been successfully published")
        }
    }

The compiler raises the following complaints:

Cannot pass function of type '(ConsumerMessage) async -> Result<_, any Error>' to parameter expecting synchronous function type
Generic parameter 'NewSuccess' could not be inferred

How can I execute asynchronous code within the flatMap closure? I couldn't find an async variant in the documentation at https://developer.apple.com/documentation/swift/result/flatmap(_:) .

Best regards

You cannot. As you found, such a variant doesn't exist, so you'll need to add it yourself.

2 Likes

Something like this will work:

extension Result {
    func flatMap<NewSuccess>(_ transform: (Success) async -> Result<NewSuccess, Failure>) async -> Result<NewSuccess, Failure> {
        switch self {
        case .success(let success):
            return await transform(success)
        case .failure(let failure):
            return .failure(failure)
        }
    }
}

But note that you'll need to change your first line to

let message = await Result { try decoder.decode(ConsumerMessage.self, from: payload) }
//            ^^^^^
    .flatMap { ... }

Is there any reason why it is not part of Swift? With the introduction of "async throws", this seems to be a natural addition to the result type. It seems trivial to add this and the related "map", "get" and and async counter part to the Result init that takes an async throwing closure etc. I wonder if there is a good reason for not having these things as part of Swift?