JavaScript中的函数式编程是否有可能存在任何类型的多态性?

时间:2018-01-15 05:37:41

标签: javascript types functional-programming polymorphism parametric-polymorphism

是否可以在JavaScript中使用函数式编程进行任何类型的多态?

我喜欢FP,但是当我想使用JS时,除了使用类/原型之外,我无法弄清楚如何在JS中支持多态性。

例如,如何在JS中用FP实现toString

使用OOP我可以简单地重载toString,以便object.toString()执行特定于object或其原型的toString代码。

2 个答案:

答案 0 :(得分:2)

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&#39}的中间类型签名告诉您它期望......

  1. 二元函数
  2. 一个值
  3. 一元函数
  4. 和另一个值
  5. 那就是你会像comp1(inc) (1) (add) (2) (3)那样应用它。

    comp2&#39}的中间类型签名告诉您它期望......

    1. 一元函数
    2. 二元函数
    3. 两个值
    4. 你会像comp2(inc) (add) (2) (3)那样应用它。所以comp2实际上很有用,因为它允许我们应用二元函数作为合成的内部函数。

      不可否认,如果您不熟悉这些类型签名,则不易阅读。但根据我的经验,学习曲线相当短暂。

      ftor支持参数和行多态,但目前不支持ad-hoc。

答案 1 :(得分:1)

函数上下文中的多态性意味着具有相同名称但具有不同参数类型的函数调用基于参数类型调用不同的函数。这意味着必须根据参数类型进行调度。您可以使用typeofinstanceof语法在运行时在JS中执行此类调度。但这被认为对于通用目的来说太慢了。大多数现代功能性程序(ML,Haskell)使用type inference来编译编译时间。这只会影响编译时间,但不影响执行时间。但JavaScript是一种传统的函数式语言,如Scheme,具有固定的对象系统,没有宏。

所以答案是:不可能在JS中有任何类型的民族主义。