Having read through the Node.JS docs (and the nice slide deck), I think the other answers here are missing the point about it: Node.JS is based on the idea that the style of writing programs where we expect them to block on I/O is wrong, and that instead we should initiate I/O (such as reading a database, or reading a socket) and pass along a function to handle the result of the I/O along with the request.
So rather than do this:
var result = db.query("select.."); // blocking
// use result
Node.JS is based on the idea of doing this:
db.query("select..", function (result) {
// use result
});
The authors (of Node.JS) point out that this way of programming is very awkward in many systems as the languages don't have closures or anonymous functions and the libraries are primarily blocking I/O. However, Javascript supplies the former (and programmers are used to it given how JS is used in an event like manner in the browser), and Node.JS fills in the later: being an entirely event driven I/O library with no blocking calls at all.
How does this relate to functional programming? All functional programming languages provide closure constructs powerful enough to do what Node.JS is trying to do with Javascript. Most make it even easier to code, as passing closures is generally fundamental to the language.
As for Haskell, using Monads, this sort of thing could be very easy to construct. For example:
doQuery :: DBConnection -> IO ()
doQuery db = do
rows <- query db "select..."
doSomething rows
doSomethingElse rows
These very sequential, imperative lines of code are actually a sequence of closures under control of the IO
monad. It is as if in JavaScript you had written:
db.query("select...", function (rows) {
doSomething(rows, function () {
doSomethingElse(rows, function () { /* done */ })
})
})
In essence, when writing monadic code in a functional language, you already are writing it in the form the Node.JS authors want us to write: Where each step of the sequential computation is passed as a closure to the prior one. However, look how much nicer that code looks in Haskell!
Furthermore, you can easily use concurrent Haskell features to achieve this non-blocking operation easily:
forkQuery :: DBConnection -> IO ThreadId
forkQuery db = forkIO $ do
rows <- query db "select..."
doSomething rows
doSomethingElse rows
Don't confuse that forkIO
with the expensive process fork you're used to, or even OS process threads. It is basically the same light weight thread of execution that Node.JS is using (only with somewhat richer semantics). You can have 1,000s of 'em just like Node.JS aims for.
So, in short - I think Node.JS is building on a facility that exists in JavaScript, but that is much more natural in functional languages. Furthermore, I think all the elements that are in Node.JS already exist in Haskell and its packages, and then some. For me, I'd just use Haskell!