iT邦幫忙

2021 iThome 鐵人賽

DAY 13
0
Modern Web

Flutter web 的奇妙冒險系列 第 13

Day13 | Dart 中的 Functional Programming

Functional Programming

如果OOP是以物件為主那FP就是以Function作為主體來思考。但也須有人會好奇 Function 這種語法很多程式語言都有支援但FP這個概念為什麼很少聽過,除了因為語言特性限制我個人是認為FP進階的概念實在是太抽象,且充滿許多數學推演的過程。

但這個狀況直到近幾年慢慢的有變化了,如果剛好你是前端工程師的話應該知道 FP 應該是近幾年前端領域的buzz word了,前端中有很多借鑑FP概念的實作案例:Promise(參考monad)、reudx(pure function)、react hook (使用closure實作)

聽起來很厲害那我們該如何開始?基本上有幾個基礎概念可以聊聊

  1. side effect
  2. pure function
  3. Higher-order function & First-class Function

Side effect

所謂Side effect就是指在functtion中除了回傳外還會造成其他影響的行為,基本上side effect都很有可能是造成Bug的主要原因。

我們來看看常見的Side effect有哪些包括但不限於:

  1. 非同步事件
  2. console.log
  3. 更改傳入函式的參數
  4. 更改外部的變數
  5. DOM操作

為什麼他們會造成Bug?舉幾個例子我們可能會因為沒有管理好非同步事件,導致我們資料存取失敗進而導致畫面異常甚至crash又或者我們更改了全域變數導致其他地方產生了預期外的結果等等。

但畢竟我們無法完全消滅side effect,所以我們要想幾個方法可以好好的管理,而這也是 FP 主要目標,或者可以說是程式設計的目標。

Pure function

pure function如同他的名字就是一個純淨的函式:相同的輸入永遠只會得到相同的輸出,且沒有side effect。

int addNumber(x, y) => x + y;
int multNumber(x, y) => x * y;
bool isOdd(x) => x % 2 != 0;

Higher-order function

在真正講解Higher-order function(HOF)之前我們必須先知道一個名詞First-class Function,意即函式跟一般的值一樣,可以被存在變數裡、可以做為函式的參數或者回傳值。

前幾篇文章一直提到的callback function這個概念,它在早期的JS中很常運用在非同步操作時,

而callback的形式大概會像是:

void y(Function x){
//some stament
x()
}

我們將一個函式 x 傳進去一個函式 y 裡,當 y 執行時會在裡面執行 x ,這已經滿足是Higher-order function的條件了。

HOF在定義上是:一個可以接受以函式作為參數或者最後會回傳函式的函式

運用這些特性可以很簡單的讓我們各個function compose在一起。

final a = addNumber(1,multNumber(2,3)); //7

或者是在 List 操作中很常看到的:

final ListA = [1, 2, 3, 4, 5, 6];
print(ListA.map(addTen).toList()); // [11, 12, 13, 14, 15, 16]
print(ListA.fold<int>(0, addNumber)); // 12
print(ListA.where(isOdd).toList()); // [1, 3, 5]
print(ListA.map(addTen).fold<int>(0, addNumber)); // 81
print(ListA.where(isOdd).map(addTen).fold<int>(0, addNumber)); //39

從上面了例子我們可以簡單地看出這個 List 怎麼被操作例如這行

ListA.where(isOdd).map(addTen).fold<int>(0, addNumber);

可以看出是先挑出奇數的數字然後將剩下的每個都加10然後在全部加總,這種我們不必管實際上「如何」操作,而只關注我要去做「什麼」的概念又被稱為:declarative programming

也拿來實作 curry function (柯里化):

Function curryiedAdd(int x) => (int y) => x + y;
final addTen = curryiedAdd(10);
final b = addTen(2);
print(b);
//12

這邊可以看到 curryiedAdd 是會回傳一個function的function,所以我們在

final addTen = curryiedAdd(10);

這裡的 addTen 是一個function

而我們也能這樣呼叫 curry function

final c = curryiedAdd(2)(10);
print(c);
// 12

今天的程式碼:
https://github.com/zxc469469/dart-playground/tree/Day13/FP

這篇文章也只是稍微提到 FP中的概念,但實際上還有很多進階的概念或者議題:point-free、functor、monad。但其實在沒了解這些之前我們還是能享受到一定的好處,像是function composition 及declarative programming這種可以簡化程式碼或者提升易讀性的概念,又或者是好測試的pure function。

而我自己在開發網頁前端時會盡量的運用FP的概念在實作,像是有一些function的行為會同時需要上層component及子component的變數且是在子component被呼叫。
通常我會將這類function curry起來將一些上層component的變數或函式(通常是不想往下傳的值像是:setState之類的function)包裝起來,這樣子component就可以少開一些props。


上一篇
Day 12 | Dart 中的 Sound null safety
下一篇
Day 14 | Flutter 基本介紹
系列文
Flutter web 的奇妙冒險30

尚未有邦友留言

立即登入留言