阅读过 Node.JS 文档(以及不错的 slide deck)后,我认为这里的其他答案没有抓住重点:Node.JS 是基于这样的想法,即我们所期望的编写程序的风格他们阻止 I/O 是错误的,相反我们应该启动 I/O(例如读取数据库或读取套接字)并传递一个函数来处理 I/O 的结果以及请求。
所以不要这样做:
var result = db.query("select.."); // blocking
// use result
Node.JS 就是基于这样的思路:
db.query("select..", function (result) {
// use result
});
作者(Node.JS)指出,这种编程方式在许多系统中非常笨拙,因为这些语言没有闭包或匿名函数,而且库主要阻塞 I/O。然而,Javascript 提供了前者(鉴于 JS 在浏览器中以类似事件的方式使用,程序员已经习惯了它),而 Node.JS 填补了后者:作为一个完全事件驱动的 I/O 库,没有阻塞调用完全没有。
这与函数式编程有何关系?所有函数式编程语言都提供了足够强大的闭包结构来完成 Node.JS 试图用 Javascript 完成的工作。大多数使编码变得更加容易,因为传递闭包通常是该语言的基础。
对于 Haskell,使用 Monads,这种东西可以很容易构建。例如:
doQuery :: DBConnection -> IO ()
doQuery db = do
rows <- query db "select..."
doSomething rows
doSomethingElse rows
这些非常连续、命令式的代码行实际上是在IO monad 控制下的一系列闭包。就像你用 JavaScript 写的一样:
db.query("select...", function (rows) {
doSomething(rows, function () {
doSomethingElse(rows, function () { /* done */ })
})
})
本质上,当用函数式语言编写一元代码时,您已经按照 Node.JS 作者希望我们编写的形式编写它:顺序计算的每一步都作为闭包传递给前一个。但是,看看这段代码在 Haskell 中的表现要好得多!
此外,您可以轻松地使用并发 Haskell 功能来轻松实现这种非阻塞操作:
forkQuery :: DBConnection -> IO ThreadId
forkQuery db = forkIO $ do
rows <- query db "select..."
doSomething rows
doSomethingElse rows
不要将 forkIO 与您习惯的昂贵的进程分叉甚至操作系统进程线程混淆。它基本上与 Node.JS 使用的轻量级执行线程相同(仅具有更丰富的语义)。就像 Node.JS 的目标一样,您可以拥有 1,000 个。
所以,简而言之 - 我认为 Node.JS 建立在 JavaScript 中存在的工具之上,但在函数式语言中更自然。此外,我认为 Node.JS 中的所有元素都已经存在于 Haskell 及其包中,还有一些。对我来说,我只会使用 Haskell!