Functional Reactive Programming

What is functional reactive programming (FRP)?
FRP is the combination of functional and reactive paradigms. In other words, it is reacting to data streams using the functional paradigm. FRP is not a utility or a library — it changes the way you architect your applications and the way you think about your applications.

Functional programming:
Functional programming is a programming paradigm where you model everything as a result of a function that avoids changing state and mutating data.
Example:
let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9]
let numbersLessThanFive = numbers.filter { $0 < 5 }
Explanation:
We feed the filter function with a closure containing a certain criterion. That criterion is then applied to each element in the numbers array, and the resulting array contains elements that satisfy our criteria.

Reactive programming:
Reactive programming is the practice of programming with asynchronous data streams or event streams. An event stream can be anything like keyboard inputs, button taps, gestures, GPS location updates, accelerometer, and iBeacon. You can listen to a stream and react to it accordingly.

OOP v.s. FRP
OOP 遇到的問題:Mutable State × Time = Complexity
OOP 的四大特性的目的是:
利用封裝,提高內聚力:將高度相關的 data structure 和 function 之間的封裝成物件的 property 和 method。利用繼承多型抽象,來降低耦合度:當需求改變時,減少需要更改代碼的範圍。

OOP 的問題:無法有效解決執行期狀態的複雜度
「當執行期的複雜度提高 (邏輯、多線程、非同步等),OOP 的開發維護成本很容易會以等比級數上升;尤其是當沒有良好的開發習慣、累積技術債的時候。」

FRP是要解決目前 OOP 開發時遇到的問題。因為 FRP:

  • 同時兼顧開發直覺、良好封裝、擴展彈性
  • 降低代碼的複雜度
  • 解決複雜的資料對時間的依賴
  • 解決複雜的資料處理、同步
  • 對依賴和副作用重新封裝

Sample code:

以下範例程式是使用者在UITextField欄位輸入資料,並根據輸入資料去更新UILabel顯示的字串。底下程式碼包含使用FRP和無FRP的實作。

無使用FRP的code:

  1. UITextField呼叫addTarget function,針對editingChanged事件去聽,如果聽到editingChanged,就呼叫指定的selector function textChanged
  2. 在textChanged function內去更新ViewModel的property的值
  3. ViewModel property textString更新值時,觸發didSet block,執行updateLabel

使用FRP的code:

  1. 使用NotificationCenter的default instance去創造一個publisher,當收到mTextField的textDidChangeNotification時,這個publisher將會傳送event出去
  2. 用給定的closure對從上面publisher收到的所有elements進行轉換
  3. 指定接收到上面傳遞下來的elements時,用哪個scheduler執行
  4. 把上面傳遞下來的element設定給viewModel的textString property
  5. ViewModel property textString更新值時,觸發didSet block,執行updateLabel

有無使用FRP比較:

雖然使用FRP的code一開始會需要先創一個publisher,但是之後asynchronous event(i.e. 收到使用者在UITextField欄位的輸入資料)的處理,能夠直接利用publisher提供的public functions(ex: map、receive、assign、sink等)去進行處理,且每一個function的output都能夠當作下一個function的input,達成能在同一行code中,處理在不同時間產生變動的資料。讓我們能夠更容易寫出直覺與易維護的程式碼。

Note:

IOS scheduler: https://blog.logrocket.com/understanding-swift-schedulers/

import UIKit
import Combine
class ViewModel {
var updateLabel: ((String?) -> Void)?
var textString: String? {
didSet {
updateLabel?(textString)
}
}
}
class ViewController: UIViewController {
@IBOutlet var mTextField: UITextField!
@IBOutlet var mLabel: UILabel!
var sub: AnyCancellable?
var model = ViewModel()
override func viewDidLoad() {
super.viewDidLoad()
// Update UI
model.updateLabel = { val in
self.mLabel.text = val
}
// With FRP
sub = NotificationCenter.default
.publisher(for: UITextField.textDidChangeNotification, object: mTextField)
.map({ ($0.object as! UITextField).text } )
.receive(on: DispatchQueue.main)
.assign(to: \ViewModel.textString, on: model)
// Without FRP
mTextField.addTarget(self, action: #selector(textChanged(_:)), for: .editingChanged)
}
// Without FRP
@objc func textChanged(_ textField: UITextField) {
model.textString = textField.text
}
}
view raw gistfile1.txt hosted with ❤ by GitHub

Ref:
(1) https://www.freecodecamp.org/news/functional-reactive-programming-frp-imperative-vs-declarative-vs-reactive-style-84878272c77f/

(2) http://xareelee.github.io/tech_note/2016/05/15/why-you-should-learn-FRP.html