【问题标题】:Are there any conventions for Haskell-like type signatures in JavascriptJavascript中是否有类似Haskell的类型签名的约定
【发布时间】:2016-02-19 10:51:36
【问题描述】:

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

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

Hoogle 使代码重用成为一流的 :) 由于 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 中是任意数字。

是否有任何约定如何使用 Haskell 的类型签名系统来表达 Javascript 中的不纯语言特性(我猜它是基于 Hindley-Milner 的)?是否有(标准化)对 javascript 的适应?

【问题讨论】:

  • 一个更大的问题是首先没有输入 Javascript。
  • 我喜欢 javascript 的动态(原型)类型,干杯!
  • @IvenMarquardt,您当然知道动态性质直接与这些签名形成对比?
  • 我认为没有任何约定。类型签名对纯函数最有意义,因为它们很容易;如果你想表达一些动态的东西,比如可变参数,你必须想出自己的语法。我见过很多,大多数都可以理解,其他的则不然。
  • Number -> ((a1, …, aN) -> b) -> (a1 -> … -> aN -> b) 感觉不错。也许(Number, (a1, …, aN) -> b) -> (a1 -> … -> aN -> b)。你甚至可以使用依赖类型来表示Number 是数字或参数N

标签: javascript types functional-programming type-signature


【解决方案1】:

是否有任何约定如何使用 Haskell 的类型签名系统来表达 Javascript 中的不纯语言特性?

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

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,这有点违背单子签名的目的。

两年后我来到这里,并认为我可以拼出这会产生的实际签名: 请注意,Addable 可能需要能够将两种不同的类型添加到另一种类型,第三种类型以实现所有当前的 JS 行为。您还需要指定RWS

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>

【讨论】:

  • 感谢您的回答以及对 Haskell 类型系统的深入了解!但这太正式了。我猜大多数 JS 开发人员根本不使用类型签名(类似结构)。而那些这样做的人,做他们自己的事。我正在寻找的只是更多的标准化。
  • 对非Haskellers 的澄清:RWST IO 是一个由 reader、writer 和 state 转换器组成的组合 monad 转换器,它被添加到一个 IO monad。 Haskell 中需要 Transformer 来组合不同类型的 monad
猜你喜欢
  • 2012-06-21
  • 1970-01-01
  • 2013-11-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多