在 Javascript 中是否有任何类似 Haskell 的类型签名的约定

Are there any conventions for Haskell-like type signatures in Javascript

当我最终偶然发现 Hoogle 时,我立即清楚了类型签名的重要性。无需寻找模棱两可和不精确的函数名称,您只需搜索类型签名:

inc :: Number a => a -> a
map :: (a -> b) -> [a] -> [b]
head :: [a] -> a

Hoogle 使代码重用成为第一个 class 事件 :) 因为 Javascript 不是一种纯函数式语言,所以很快就会遇到问题。给出的是这个天真的咖喱实现:

function curry(n, f) {
    var args = Array.prototype.slice.call(arguments);
    if (typeof n === 'undefined')
        args[1] = f.length;
    if (n === args.length - 2)
        return f.apply(undefined, args.slice(2));
    return function() {
        return curry.apply(undefined, args.concat(Array.prototype.slice.call(arguments)));
    };
}

function add(a, b) { return a + b; }
var addC = curry(2, add);

addC(2)(3); // 5
addC(2, 3); // 5

对应的类型签名会是什么样子?

Number -> ((a1, ..., aN) -> b) -> (a1 -> ... -> aN -> b)
| Number -> ((a1, ..., aN) -> b) -> ((a1, ..., aN) -> b) // ???

这太可怕了,不是想要的结果。在纯函数式语言中,函数总是只有一个参数 - JavaScript 中的任何数字。

Javascript 中的不纯语言特性是否可以用 Haskell 的类型签名系统来表达(我猜它是基于 Hindley-Milner)?是否有针对 javascript 的(标准化)改编?

Are there any conventions how impure language features in Javascript can be expressed with the type signature system of Haskell?

这比您想象的要复杂。举例说明:

function f(a,b) { return a + b; }

最简单的情况下,这是Int -> Int -> Int。如果你想真正满足 + 对字符串的需求,你需要这样的东西:

Addable a => a -> a -> a

但是嘿! - 你会大喊 - 在 JS 中我们也可以向字符串添加数字! (此时你需要 MultiParamTypeClasses 这是一个 Haskell 扩展)

Addable a b => a -> b -> a

完美。现在我们只需要实现 Addable String StringAddable Int IntAddable String Int。看看你需要多少,我们还没有接触到不纯的功能!

当你介绍 this 时,你几乎介绍了 State。由于每个函数都可以 throw,因此您也需要 MonadError。然后他们都有 IO (console.log) 可用,对吧?

为此 JS 中几乎每个 函数都需要标记为 RWST IO,有点违背 monadic 签名的目的。

I got here two years later and thought I could spell out the actual signature this would produce: Note that Addable would probably need to be able to add two distinct types to another, third type to fulfill all current JS behaviour. You'd also need to specify R, W, and S.

forall a b c m. (MonadError m, MonadIO m, Addable a b c) => a -> b -> RWST R W S m c


你看,Haskell 足够强大,可以将副作用表示为静态签名。其他语言,如 Idris,也可以这样做,但方式略有不同。然而,它付出了巨大的代价:每个函数现在都被限制为只能执行其签名中的内容。

另一方面,JS 最大的优势之一在于它的动态特性。事实上,您可以非常轻松地编写非常复杂的数据操作:

function f(a) {
    console.log(a + this.b);
    this.c(function() {
         setTimeout(console.log(a), 100);
    });
}

巨大的好处。在 Haskell 中为此写签名会花费很长时间,如果你在五分钟后重写它就没有意义了。

遗憾的是,我认为您所要求的不会很快发生。澄清一下,我严格来说是指像 Haskell 一样强大的类型系统,结合 JS(或 Lua 的弹性和快速原型,我认为它相似但更好)。为了不过分悲观,可插入类型系统正在研究中,希望我们将来会看到更多使用它们的语言。

<opinion>我看不到任何类似 java 的 JS 插件,包括可怕的 ES6 class,但在这方面甚至遥不可及。</opinion>