理解函数式编程语言中的组合--前言(一)

理解函数式编程语言中的组合--前言(一)

函数式编程思想能够用一句话总结,即:可组合的类型+可组合的函数,我在《使用函数式语言作领域建模》一文描述了如何使用可组合的类型进行领域建模。这篇文章就是用来讲明后半部分,即--理解可组合的函数。我假设读者已经对“Higher order function”, “Currying“, ”Immutable“等基本概念有所了解,并拥有基础的TypeScript知识便可。
这一切还得从抽象提及。编程

抽象的重要性

人类可以解决复琐事物的一个重要方法就是抽象,代码也同样。有了抽象,你的代码才不会变成流水帐,实际上那些让人读起来赏心悦目的好代码,必定拥有良好的抽象。抽象可让你避免陷入细节,经过几个重要的类名或者接口,让别人快速识别你的设计。
固然,这一切的前提是,阅读代码的人也拥有相似的抽象思想,他才可以心照不宣你的意图。若是你在读代码的时候,本身是怎么想的,正好代码就是这样设计的,你才会以为代码可读性太强了。
怎么样你们才能设计出一致的抽象呢?
《设计模式》就是这样一本把常见问题总结成一些列模式的书籍。换句话说,《设计模式》提供了常见问题的抽象方式并提供了相关的术语。
固然你的设计好很差,还取决于你是否正确识别到了问题的本质,并恰如其分的实现了某个已知的设计模式。若是阅读代码的人拥有跟你相似的抽象知识,他就可以迅速明白你的设计和意图。设计模式

函数式编程中的抽象

若是说《设计模式》总结了OO思想中的常见抽象方式,那么函数式编程语言也拥有本身的抽象方式。
看下面的这两行数学运算:数组

1 + 2 = 3

这行数学运算很是简单,以致于靠直觉就能判断他的真确性。这行代码能够描述为:两个“数字”经过“相加”任然为“数字”。
若是咱们把"数字“泛化,将"相加”推广开来,就能够用在其余事物身上,例如:编程语言

"a" + "b" = "ab"

对字符串也是适用的,两个字符串经过一个运算符合并为一个新的字符串。没错,这就是组合的基础。
函数式编程中的抽象叫作《范畴轮》Category theory,是一门研究如何组合事物的数学科学。其中,“事物”被称为object, 但这个object并非OO中的那个对象,而事物之间的转化或者映射被称为morphisms,翻译为中文叫射态,对应到编程语言中,就是函数。《范畴论》在函数式编程语言中的地位,能够类比《设计模式》在OO中的地位。函数式编程

为何须要《范畴论》

这要从函数的组合开始提及,咱们知道函数式编程语言没有OO语言中的那些概念,例如类,继承,依赖注入(有的语言有FP和OO两种范式, 例如Scala, F#等,请不要纠结)。只靠函数,为大型的工程化实践带来了挑战。 玩过乐高的同窗都知道,乐高的零件都是拥有独立功能的小部件,然而,却有人用它拼出了汽车,飞机甚至是航空母舰,这充分说明了组合的力量。
那么函数是如何组合的呢?看下面的代码:函数

const add = (a: number, b: number) => a + b
const sub1 = (a: number) => a - 1

声明两个函数add和sub1, 像lodash或fp-ts等库都会提供一个叫flow的函数,flow的做用就是把若干个函数组合起来:翻译

const addThenSub1 = flow(add, sub1)

expect(addThenSub1(1, 2)).toBe(2)

此时addThenSub1变成了一个新的函数,最后再将参数做用在上面就能够获得结果。flow理论上能够支持任何数量的函数组合。然而,flow有个致命的问题,他要求上一个函数的返回值类型跟下一个函数的输入类型一致,例如,上面例子中的add若是返回string, 那么链接就失败了。另外对sub1的参数数量也有要求,即除了第一个函数add, 后面的函数只能接受一个参数。
其实这两条限制就为函数的组合判了死刑,即:
虽然函数是能够组合的,但并非全部的函数都能任意组合。
《范畴论》就是这样一门数学科学,他帮你抽象出了事物的转化或组合模式,你只要按照他总结的模式设计,就能将函数组合起来。设计