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 中解释的那样。
假设有两个对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 中解释的那样。