Javascript 是一种无类型语言
不,Javascript 没有多态性的概念,因为它是一种无类型语言。简单地说,多态意味着严格的类型系统在受控条件下不那么严格,也就是说,它不会因为多态的行为而失去类型安全。
无类型语言虽然有些简化。从静态类型系统的角度来看,Javascript 有一个巨大的联合类型,一个值或其底层表达式可以在运行时采用它的任何表示形式(变量甚至可以在它们存在期间适应不同的类型)。这种类型也称为动态类型。
动态类型语言具有在运行时检查值类型的自省功能。但这种手段是有限的。例如,只要没有完全应用函数,您就无法内省函数的类型。
但是,原始意义上的函数式编程意味着使用大量小的、专门的一阶和高阶函数,这些函数在curried form 中声明。这种方法会导致在您的代码中部分应用函数。现在的问题是,您不仅要推断初始函数的类型,还要推断部分应用函数的中间类型。这很快就会变得艰难:
// what's the type of this function?
const comp = f => g => x => f(g(x));
// and this partially applied one?
const inc = n => n + 1;
comp(inc);
// and even worse:
comp1 = comp(comp);
comp2 = comp(comp) (comp);
我确定我在字里行间的某个地方迷失了你。从头脑中推断出这些类型需要很多时间。作为一名开发人员,你真的应该有责任像编译器一样行事吗?我不这么认为。
问题的替代解决方案
幸运的是,Javascript 社区一直在积极开发此类问题的解决方案。
语言之上的静态类型检查器
Flow 和 TypeScript 是静态类型检查器,它们试图将类型系统添加到 Javascript 中。我个人认为这不是一个有前途的方法,因为通常在创建新语言时首先设计类型系统。 Javascript 可以在任何地方执行副作用,这使得创建一个健全可靠的类型检查器变得非常困难。查看 Flow 存储库中的 issues 以获得您自己的图片。
将 Javascript 降级为编译目标
是的,这个标题可能有点基于意见,但这就是我的感觉。 Elm、purescript、Facebook 的Reason 就是这种方法的代表。好吧,如果你想放弃 Javascript,这些都是合理的可能性。但是赌哪匹马呢? Javascript 生态系统的碎片化真的很可取吗?还是我们想要一个依赖像 Facebook 这样的供应商的社区?我无法真正回答这个问题,因为我有很大的偏见,正如你即将看到的那样。
运行时类型检查器
注意:这是一个无耻的插件!
作为一种动态类型语言,Javascript 具有成熟的自省功能。除了 ES2015 代理之外,我们还拥有构建虚拟化运行时类型检查器所需的一切。在这种情况下,虚拟意味着它是可插拔的,即您可以打开和关闭它。运行时类型系统需要是可插拔的,因为它对性能有重大影响,并且仅在开发阶段才需要。
我已经为这样的类型检查器工作了几个月,到目前为止,这是一段令人兴奋的旅程。 ftor 远非稳定,但我相信这种方法值得探索。
这是上面的 comp 组合子,作为带有类型提示的类型化版本(TS 只是一个内部 Symbol,它包含一个类型的当前签名,您可以使用它来请求此签名以进行调试):
import * as F from ".../ftor.js";
F.type(true);
const comp = F.Fun(
"(comp :: (b -> c) -> (a -> b) -> a -> c)",
f => g => x => f(g(x))
);
const inc = F.Fun(
"(inc :: Number -> Number)",
n => n + 1
);
comp(inc) [TS]; // "(comp :: (a -> Number) -> a -> Number)"
const comp1 = comp(comp),
comp2 = comp(comp) (comp);
comp1 [TS]; // "(comp :: (a -> b0 -> c0) -> a -> (a0 -> b0) -> a0 -> c0)"
comp2 [TS]; // "(comp :: (b1 -> c1) -> (a0 -> a1 -> b1) -> a0 -> a1 -> c1)"
comp1 的中间类型签名告诉你它期望...
- 二元函数
- 一个值
- 一元函数
- 还有另一个值
也就是说,您可以像 comp1(inc) (1) (add) (2) (3) 一样应用它。
comp2 的中间类型签名告诉你它期望...
- 一元函数
- 二元函数
- 两个值
您可以像comp2(inc) (add) (2) (3) 一样应用它。所以comp2实际上是有用的,因为它允许我们应用一个二元函数作为组合的内部函数。
诚然,如果您不熟悉这些类型签名,则很难阅读它们。但根据我的经验,学习曲线相当短。
ftor 支持参数和行多态,但目前不支持 ad-hoc。