纯函数
什么是纯函数?
- 纯函数:相同的输入,永远会得到相同的输出,而且没有任何可观察的副作用。
为什么要追求纯函数?
- 可缓存性(Cacheable):
JavaScript
中应用最广泛的库为memoizee
; - 可移植性(Portable):可移植性可以意味着把函数序列化。与之相对的是面向对象语言,移植一个对象通常需要将整个庞大的体系迁移,这也是
JavaScript
拥有强大的组件化生态的原因。 - 自文档化(Self-Documenting):不需要过多的 Context 来描述函数执行前、执行后的效果;
- 可测试性(Testable):
Quickcheck
,一个为函数式环境量身定制的测试工具。 - 引用透明性(Referential Transparency):如果一个函数调用可以完全用它的返回值代替,那么称这个函数时引用透明的。
柯里化 (Curry)
Curry 的概念:
- 只传递给函数一部分参数来调用它,让它返回一个函数去处理剩下的参数。
柯里化函数的简单实现:
|
|
函数组合 (Compose)
以下的操作称作为两个函数 f
、g
的组合(相当于数学中的复合函数):
|
|
JavaScript
中有一个为了函数式编程设计的库就聚合了 compose
、curry
等函数:ramda
有一个与组合理念相关的一种设计模式,叫 Pointfree
:
- Pointfree:或称隐形编程,是一种只关心函数实现而并不关心具体参数的设计模式,在实际开发过程中通常不需要把函数的参数显式地表现出来,所以代码通常看起来会更加简洁;
- 下面是一个用
Pointfree
设计模式优化代码的例子:
|
|
- 可以看到用
Pointfree
的设计模式优化之后,word
这个参数就不需要在函数定义时显式声明出来了;
PostScript:
- 小技巧:
compose
函数的阅读有点类似于矩阵的乘法,如果想知道返回函数的签名,只需要知道第一个函数的参数和最后一个函数的返回值即可。
范畴论 (Category Theory)
数学上有一个分支‘范畴论“通过研究一系列抽象的概念形式化地统一了集合论、类型论、群论等不同的分支。
范畴论中定义了 category
,它被定义为以下组件的抽象集合:
- A collection of objects:对象的集合。比如
Boolean
类型可以理解为true
/false
的集合; - A collection of morphism:映射的集合。比如我们在上面提到的纯函数,就是我们主要研究的映射;
- A notion of composition on the morphism:映射的组合。就是我们在前面提到的
compose
操作; - A distinguished morphism called identity:一个特殊的映射,恒等映射。在组合关系中恒等映射即为函数
id = x => x
。
Hindley-Milner 类型系统
什么是类型:
- 函数签名在这个类型系统中可以被认为是“类型到类型的映射”。
- 基于这个逻辑,“类型”在“类型系统”中可以被认为是一个变量。TypeScript 基于这个逻辑通过“泛型”设计了一个图灵完全系统。
Parametricity:
- Parametricity 是一种参数多态化函数,统一都满足的抽象性质。它指明了无论多态化参数实例化为那种真实函数,这些函数都有同样的表现形式;
- 比如说有这样的函数签名
[a] => a
,因为 a 是任意的类型,所以这个函数只能在明确的类型Array
上进行一些操作(比如取它的第一个、最后一个、或随机的一个元素)。
Free Theorem(自由定理,见论文):
- 因为参数多态化函数有上面的 Parametricity 性质,可以推导出函数许多相关的性质。
- 比如下面就是自由定理推导出的一些结果:
|
|
- 这些看起来纯理论毫无价值的公式,实际上是有应用价值的。比如说上面的第一个公式理论上证明它们的计算结果是一样的,但是后者的计算量却比前者要大很多。
Constraints:
- 类型系统可以声明类型映射的参数满足一定的约束。这一理论化的内容在 TypeScript 中是通过
extends
关键字实现的。