结。 / 匹配子 —— Ruby 的类型论,实现参数多态和交换律、结合律(三)

这是一个了断,
在写完这篇 匹配子 理论之后,
我不会再去学习和消费 数学游戏 了。
函数多态方式
类似 f :: a, b, c, d, e -> e 这种的
或者 f(a, b, c, d)
f :: a, b, c, d, e -> e 应当看成一个整体,也就是一个序列。
(a, b, c, d) 叫 输入序列。 -> e 叫 返回序列。
为了不混淆,也就可以叫 serie(系列)。
而函数的类型,等价于匹配序列的一个模式表达式。
函数类型 (int, int, int) -> int 匹配 (1, 2, 3)。
-> int需要匹配调用它的函数的 槽位 的参数类型。
可以看到,类型——匹配子的对应关系如下:
设:
其中:
- 可以展开成一个树,被调用的函数称作某函数的上游函数,调用函数的函数称作某函数的下游函数。
a() 有调用它的函数f(),a()称作f()的 上游函数,而f()称作a()的 下游函数。
g() 没有调用者,故,g()称作该函数链的 根函数、最终下游函数。
a() 没有上游函数,故,a()称作该函数链的 顶流函数,或末函数。
f() 既有上游又有下游,称 介函数 或 间函数。
- 在 f(...) 中:
- 第一个 a() 匹配下游调用 f() 的第一个槽位,f() 需要返回值为 int 类型的参数。于是 a() 调用了 (->int) 的分支函数
- f() 的第二个参数槽位 需要 str 类型的参数,于是 a() 调用了 (->str)
- 在 g(...) 中:
- g() 需要 int 类参数,于是 f() 调用 了返回值为 int 的分支函数
- 对于a():
- 第一个a() 匹配下游f()的第一个槽位(int),f() 需要 int 类的参数,因此,a 调用了(->int) 的分支函数
- 第二个a() 匹配 f() 的第二槽(str)。因此调用了(->str) 分支函数。
- 对于g():
- 他需要一个 int 类的参数,返回 int 类的值
- 对于f():
- 他在 g() 的第一个槽位,需要 int 类参数,因此,f() 确定了返回值类型,(...->int)
- (...->) 和 (int str->...) 组合一起,就确定了要调用 (int str -> int) 分支函数。
更复杂的情况:
展开成树后是:
此时,f() 返回值重载和他的下游函数 g() 该槽位的参数重载同时存在。
a() 和 f() 互有重载冲突。
根本无法确定匹配哪个分支函数。
此时,为了确保只有一个结果——函数类型的确定性,可采取如下 解叠加(消叠加态) 规则:
函数链 *全匹配* 原则:将函数链展开成树,对所有函数采取完全匹配成功的类型。
先后原则:上述步骤还有多分支,则采取第一个定义的分支函数。
!!! 如果采取了 先后原则,可能无法实现异步定义。
如,整个函数链只有 a(),根据先后原则,返回第一个分支函数—— a :: int。
匹配子、类型、集合的行为类似。

单子 Just()
Just(1) 只匹配 1,故 Just(1) 是一个 单子。
int int 匹配 1 2 也匹配 2 3,故 int int 不是单子,他的 tokens 全由非匹配子组成,他是个 终匹配子。
顺序标记
有序匹配子 <int str> 匹配 一个 第一项是int,第二项是str 的序列。
涵序匹配子不关心序列的顺序, [int str] 匹配 int str 和 str int。
因此 [int str] 蕴含 <str int>。

数量标记
[int str {..3}] 蕴含 <int> 、<int str>、<str int>...<str int str>...等1+2+3+4=10 个终匹配子。
同时也蕴含 <int str{2..3}> 子匹配子。
类型、类集、单子、终匹配子的区别
本质上说,类型和匹配子一样,可以匹配实例:
匹配子
结。