如何复合功能并坚持进步

时间:2015-12-11 15:36:41

标签: ios swift functional-programming swift2 persistence

我有一系列异步方法定义如下:

func step1(input: Step1InputData, completion: (Step1OutputData -> Void)) { /* do something */ }
func step2(input: Step1OutputData, completion: (Step2OutputData -> Void)) { /* do something */ }
// etc...

如您所见,step1的输出是step2的输入。这些类型都实现了StepData协议:

protocol StepData {}
class Step1InputData : StepData { }
class Step1OutputData : StepData { }
class Step2OutputData : StepData { }

最后,我有这个自定义运算符:

infix operator => { associativity left }

func => <P:StepData, Q:StepData, R:StepData> (left:((P, Q -> Void) -> Void), right:((Q, R -> Void) -> Void)) -> ((P, R -> Void) -> Void) {
    return { (x, completion) in
        left(x, { y in
            right(y, completion)
        })
    }
}

...这意味着我可以写下以下内容:

let combinedStep = step1 => step2

这很棒,因为它超级可读,至关重要它强制执行步骤之间的类型安全。

问题在于我希望能够保持combinedStep的进度。例如,step1可以是图像上传,step2可以是本地存储写入。如果step2失败,下次我尝试时,我想从我离开的地方继续,而不是重新上传图片。当然,可以将任意数量的步骤链接在一起。

可以提出一个系统,它有一系列步骤并管理步骤之间的数据传递,并且能够保持进度,但是,我想不出办法在保持编译时类型安全的同时执行此操作。

具有更多功能编程经验的人能指出我正确的方向吗?

3 个答案:

答案 0 :(得分:0)

我建议您研究PromiseKitBrightFuturesReactiveCocoa等框架。

这些框架与您的问题和解决方案空间相同,需要编写异步操作,并且它们都提供了多种方法来处理错误和重试。

如果您喜欢其中任何一种,您可以采用它们代替您的自定义实施,并利用大型开发人员社区的支持。或者你可以从他们的代码中找到灵感,然后再带回你的。

享受

答案 1 :(得分:0)

我认为您需要使运算符正确关联,并在完成处理程序中传递枚举以允许失败。

enum StepResult<Output> {
    case Success(Output)
    case Failure((StepResult<Output>->Void)->Void)

    static func toFailHandlerWithInput<Input>(input: Input, processor: (Input,(StepResult<Output>)->Void)->Void) -> StepResult<Output> {
        let handler : ((StepResult<Output>->Void)->Void) = { completion in
            processor(input) { pout in
                completion(pout)
            }
        }
        return .Failure(handler)
    }
}


infix operator => { associativity right }

func =><Input,Intermediate,Output>(left: (Input,(StepResult<Intermediate>)->Void)->Void, right: (Intermediate,(StepResult<Output>)->Void)->Void) -> (Input,(StepResult<Output>)->Void)->Void {
    var mergedOp : ((Input,(StepResult<Output>)->Void)->Void)!

    mergedOp = { input,completion in
        left(input) { intermediate in
            switch intermediate {
            case .Success(let output):
                right(output, completion)
            case .Failure:
                let failure = StepResult.toFailHandlerWithInput(input, processor: mergedOp)
                completion(failure)
            }
        }
    }
    return mergedOp
}


var counter1 = 0
func step1(input: Int, completion: (StepResult<Double>)->Void) {
    print("performing step 1...")
    counter1 += 1
    if ( counter1 > 1 ) {
        print("Step 1 will succeed...")
        let result = 2.0 * Double(input + counter1)
        completion(.Success(result))
    } else {
        print("Step 1 fails...")
        completion(StepResult.toFailHandlerWithInput(input, processor: step1))
    }
}


var counter2 = 0
func step2(input: Double, completion: (StepResult<Double>)->Void) {
    print("performing Step 2...")
    counter2 += 1
    if ( counter2 > 2 ) {
        print("Step 2 will succeed...")
        let result = 3 * input + Double(counter2)
        completion(.Success(result))
    } else {
        print("Step 2 fails...")
        completion(StepResult.toFailHandlerWithInput(input, processor: step2))
    }
}


var counter3 = 0
func step3(input: Double, completion: (StepResult<Double>)->Void) {
    print("performing Step 3...")
    counter3 += 1
    if ( counter3 > 1 ) {
        print("Step 3 will succeed...")
        let result = 4 * input + Double(counter3)
        completion(.Success(result))
    } else {
        print("Step 3 fails...")
        completion(StepResult.toFailHandlerWithInput(input, processor: step3))
    }
}


func comboHandler(result: StepResult<Double>) {
    switch result {
    case .Success(let output):
        print("output: \(output)")
    case .Failure(let failHandler):
        failHandler(comboHandler)   // call again until success
    }
}


let combinedSteps = step1 => step2 => step3
combinedSteps(5) { result in
    comboHandler(result)
}

答案 2 :(得分:0)

我在这里提出的具体问题的答案,如果它对其他任何人有用,结果是对Memoization采取了类似的方法。严格来说,它是缓存而不是memoization,但是将step函数包装在另一个采用缓存键参数的函数中的概念是相同的。