将Swift Array <Result <X,Error >>转换为Result <Array <X>,Error>

时间:2019-07-29 09:24:40

标签: swift generics

我有一个Swift Result数组,像这样:

let tuple: [Result<Term, TermError>] = /* code here */

我想将其内翻,将结果拉出以给出单个结果,并将数组推入其中。

let tuple2: Result<[Term], TermError> = /* How? */
如果tuple2任何.failure,则

tuple应该为.failure。否则为.success([tuple-elements-in-here])

我想我可以提出一些可以使这项工作奏效的方法,但是我觉得应该有一种相当干净的方法来实现这一目标?

3 个答案:

答案 0 :(得分:5)

您实际上只是在尝试为sequence monad在Swift中为Haskell Monads重新创建Result函数。我们可以像Haskell implemented it一样完全实现它。

sequence       :: Monad m => [m a] -> m [a] 
sequence       =  foldr mcons (return [])
                    where mcons p q = p >>= \x -> q >>= \y -> return (x:y)

在Swift中,这看起来像:

func sequence<T, E: Error>(_ arrayOfResults: [Result<T, E>]) -> Result<[T], E> {
    return arrayOfResults.reduce(.success([])) { (p, q) in
        return p.flatMap { x in return q.map { y in return x + [y] } }
    }
}

用法:

let tuple2 = sequence(tuple)

答案 1 :(得分:2)

您可以在Array上定义一个Array<Result<Value,Error>> to来转换extension Array { func flatMapResult<Value, Error>() -> Result<Array<Value>,Error> where Element == Result<Value,Error> { let valuesAndErrors = self.map { element -> (value: Value?, error: Error?) in switch element { case .failure(let error): return (nil, error) case .success(let value): return (value, nil) } } if let firstElementWithError = valuesAndErrors.first(where: {$0.error != nil}), let firstError = firstElementWithError.error { return .failure(firstError) } else { let values = valuesAndErrors.compactMap { $0.value } return .success(values) } } } Result,Error>`,如下所示。

enum MyError: Error {
    case err
}

let arrayOfResults: Array<Result<Int,MyError>> = [.success(8), .success(2), .success(3)] // success([8, 2, 3])
let arrayOfResultsWithFailure = arrayOfResults + [.failure(.err)] // failure(__lldb_expr_2.MyError.err)

arrayOfResults.flatMapResult()
arrayOfResultsWithFailure.flatMapResult()

使用代码示例:

export class FilterType {
  constructor(
    public priority: number,
    public name: string) {
  }
}

答案 2 :(得分:1)

您可以使用map函数并在失败的情况下引发错误。

类似这样的东西:

let tuple2: Result<Array<Term>, TermError>

do {
    let result = try tuple.map { result -> Term in
        switch result {
        case .failure(let error):
            throw error
        case .success(let value):
            return value
        }
    }

    tuple2 = .success(result)
} catch let error as TermError {
    tuple2 = .failure(error)
} catch { fatalError("Unknown error") }

print(tuple2)

您可以将其移入扩展程序以重用。