FRP 星际、转换、链接信号

FRP Interstellar, transforming, chaining signals

假设有两个对API的异步请求需要按顺序执行。 第一个请求的结果是字典,第二个请求使用它来获得最终字典。 Interstellar (https://github.com/JensRavens/Interstellar) 支持以一种方式链接异步请求,以按顺序一个接一个地执行它们: 例如:

typealias JSONDictionary = [String: AnyObject]

func create(parameters: JSONDictionary, completion: Result<JSONDictionary> -> Void) {
    Request.POST("objects", parameters: parameters, completion: completion)
}

func verify(parameters: JSONDictionary, completion: Result<JSONDictionary> -> Void) {
    guard let id = parameters["id"] as? String else {
        let error = NSError(domain: "", code: 401, userInfo: nil)
        completion(.Error(error))
        return
    }
    Request.POST(String(format: "objects/%@/verify", id), parameters: nil, completion: completion)
}

"create"需要先执行,结果字典会被用来执行"verify": 这很好用:

address.flatMap(Address.create).flatMap(Address.verify)
.next { dictionary in
    expectation.fulfill()
}
.error { error in
    print("There was a an error: \(message)")
}
address.update(self.parameters)

并使用以下代码(来自 Interstellar 幻灯片):

infix operator >>> { associativity left precedence 160 }
func >>> <A, B> (left: Signal<A>, right: A->Result<B>) -> Signal<B> {
   return left.flatMap(right)
}
func >>> <A, B> (left: Signal<A>, right: (A, (Result<B> -> Void)) -> Void) -> Signal<B> {
   return left.flatMap(right)
}
func >>> <A, B> (left: Signal<A>, right: A -> B) -> Signal<B> {
   return left.flatMap(right)
}

可以使用:

address >>> Address.create >>> Address.verify

这就更清楚了。但是,如果验证定义为(接受 id 而不是字典):

func verify2(id: String, completion: Result<[String: String]> -> Void) { ... }

为了链接它,我定义了另一个函数:

static func idValue(dictionary: JSONDictionary, completion: Result<String> -> Void) { ... )

这基本上是从字典中解压 id 键的值(将字典转换为字符串),我可以使用:

address >>> Address.create >>> Address.idValue >>> Address.verify2

这在 FRP 中是常见的做法吗?我可以避免中间的步骤吗?

很高兴听到您正在使用这个库。是的 - 这就是它应该工作的方式。这里有一些小想法如何让它更容易阅读:

  • 自定义运算符在发布 lib 之前被删除,主要是因为开发人员不喜欢它(他们也改变了我的想法。flatMap 在复杂的情况下更具可读性)
  • 函数式编程的主要思想之一是让类型系统为你工作。如果 Address.create returns 一个 Address-struct,下一次调用可以只使用它的 id-属性。一旦通信变得复杂,使用 json 作为参数几乎永远不会奏效。
  • 另一个想法是使用 Lens 正如 Chris Eidhof 在 Lenses in Swift 中解释的那样。