Monad新解-FRP對比——ReactiveCocoa、RxSwift、Bacon以及背後的Functional
ReactiveX
Rx的Observable的本質就是一個Event Monad,即上下文(就是圖文教程中包裹的盒子)為Event的一個Monad,這裡的Event定義,可以對應語言的struct或者enum,包括了next、error和complete三個上下文即可。這裡擷取的是Swift語言的實現,map方法實現拆裝箱(類似Optional,即Haskell的Maybe)
public enum Event<Element> {
/// Next element is produced.
case next(Element)
/// Sequence terminated with an error.
case error(Swift.Error)
/// Sequence completed successfully.
case completed
}
extension Event {
/// Maps sequence elements using transform. If error happens during the transform .error
/// will be returned as value
public func map<Result>(_ transform: (Element) throws -> Result) -> Event<Result> {
do {
switch self {
case let
return .next(try transform(element))
case let .error(error):
return .error(error)
case .completed:
return .completed
}
}
catch let e {
return .error(e)
}
}
}
而Rx的subscribe方法就是一個解包,也就是Monad<Event>.map(),接收一個(Event) -> void的引數。或者使用更一般直觀的三個引數onNext: (Element) -> Void、onError: (Error) -> Void、onCompleted: (Void) -> Void方法(在其他語言實踐上,RxJS就是三個function引數,而RxJava為了支援Java7可以使用匿名內部類)
理論:
Monad Event <$> subscribe
示例:
let subscription = Observable<Int>.interval(0.3)
.subscribe { event in
print(event) // unwraped event
}
let cancel = searchWikipedia("me")
.subscribe(onNext: { results in
print(results)
}, onError: { error in
print(error)
})
Rx的Operator是Functor,也就是說(Event) -> Event,因此可以通過Monad不斷bind你想要的組合子,直到最終符合UI控制元件需要的資料
理論:
Monad Event >>= map >>= concat >>= filter >>= map <$> subscribe
示例:
let subscription = primeTextField.rx.text // Observable<String>
.map { WolframAlphaIsPrime(Int($0) ?? 0) } // Observable<Observable<Prime>>
.concat() // Observable<Prime>
.filter { $0.isPrime } // Observable<Prime>
.map { $0.intValue } // Observable<Int>
Promise / Future
Promise本質上也是一個Monad,包裹的上下文就是resolve和reject。
你可能反駁說Promise.then(f)中的f,可以是value => value,而並不是一個被Promise包裹的型別啊。但是實際上,由於JavaScript型別的動態性,Promise.then中直接返回value型別是個語法糖罷了,實際上會處理為value => Promise.resolve(value)
Promise.resolve(1)
.then(v => v+1) //便捷寫法罷了,返回的是resolved狀態的Promise物件
.then(v => Promise.resolve(v+1)) //完整寫法
.then(v => Promise.reject('error ' + v)) //想要返回rejected狀態,無便捷方法
.catch(e => console.log(e)) // error 3
原理:
Monad Promise >>= then >>= then >>= catch >>= then
示例:
Promise.resolve(1)
.then(v => {
return v + 1; // 1
}.then(v => {
throw new Error('error'); //reject
}.catch(e => {
console.log(e); // error
return Promise.resolve(0);
}.then(v => {
console.log('end', v); // end 0
}
https://dreampiggy.com/2016/11/17/FRP簡介—ReactiveCocoa、RxSwift、Bacon以及背後的Functional/