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。


<<:  [Day25] Esp32s + IFTTT + LINE

>>:  若市场总是有效率,我就是在街上乞讨的流浪汉。

[Day18] POPCAT in WASM (Part 2)

好 那今天就会完成这个小专案 可能 CSS 的部份写的没有很好 ouo 读者可以自行修改 还是再放一...

NetSuite Glossary

今天继续介绍 NetSuite 几个基本的 Glossary, 这些 Glossary 经常会出现在...

如何衡量万事万物 (1) 衡量的定义

其实在开赛前,我有规划一些软性书单,想说在忙碌或想要休息时,可以拿来挡一下。但我今天早上真正 rev...

10.13 应用系统的防护基准 - 其他(除旧布新)

适用人员: 技术人员(开发人员)。 适用法规: 资通安全责任等级分级办法 - 附表十资通系统防护基准...

Python & SQLALchemy 学习笔记_查询

由於查询部分的篇幅相较於前几者较多,因此将查询的部分独立出来写 另外这边写的只有一些基础的操作,像是...