4 ms·
On one thread, I am defending FP from critics who are unaware from the state of the art. EDIT: Just to make it clear early on, I agree with the article's concl
by KirinDave 10y ago
On one thread, I am defending FP from critics who are unaware from the state of the art.
EDIT: Just to make it clear early on, I agree with the article's conclusion that Nodejs is not as good at compute heavy workloads as Haskell. I simply object to any use of "the nested callback problem" as valid in 2016. It's an issue exclusively for legacy code and developers who take pride in writing outdated code.
It seems only fair, then that I also should defend Javascript from people obviously unaware of the state of the art in pseudo-imperative programming. And by state of the art, I mean "has been around in some languages for 3+ years."
The example:
request('http://example.com/random-number', function(error, response1, body) {
request('http://example.com/random-number', function(error, response2, body) {
request('http://example.com/random-number', function(error, response3, body) {
...
});
});
});
But modern Javascript (before you start, yes, it runs on every browser with preprocessing, which is normal for this ecosystem) would make it look more like this:
// rp is a request promise, multiple options for creating them
async function make3StaticRequests() {
try {
var res1 = await rp('http://example.com/random-number')
var res2 = await rp('http://example.com/random-number')
var res3 = await rp('http://example.com/random-number')
// ...
}
catch(error) {
// ...
}
}
// And of course the promise library allows for many things
// you'd like with applicative functors, like binding groups
// of operations together and evaluating them all.
function randomNumberPromise() {
rp('http://example.com/random-number')
}
async function make3StaticRequests() {
var [res1, res2, res3] = Promise.all([randomNumberPromise(),
randomNumberPromise(),
randomNumberPromise()])
// ...
}
I don't really understand why people feel comfortable writing up comparison articles without doing sufficient research into what they're comparing things to.
That said, the articles point about large compute workloads starving other operations is very much true and a good example of what the weakness of V8 as a server-side programming environment brings.
- spion 10y agoFor starvation there is also clustering, which can shift the load towards worker processes that aren't busy with cpu-intensive tasks, as well as simple modules such as `https://www.npmjs.com/package/process-pool` https://www.npmjs.com/package/process-pool` which can be used to offload (known) expensive tasks. While Haskell is truly better at concurrency (no need to serialise when passing messages, green threads yield not only at IO but also at memory allocations), that part of the comparison isn't very good. Spawning a cluster of NUMCORES threads using the built in cluster module would be an improvement.
- KirinDave 10y agoPersonally I think this is a total non-answer to the question. It enormously complicates the concurrency and data sharing story for a solution that literally every programming language has access to. Nodejs has a poor story for compute-intensive loads. People need to be comfortable saying that, because it's reality.
- spion 10y agoFirst of all, there is no enormous complication. Clustering in node is super-easy, and file descriptors of requests are sent automatically to one of the processes in the process pool. This benchmark should have at least done that. Secondly, the service presented there doesn't even need to take advantage of shared memory concurrency at all. This is the case for a vast majority of web service problems too: they either talk a lot to each other or do a bunch of cpu-intensive work, but rarely both. Finally, when you use shared memory concurrency/parallelism to solve web service problems, there is a risk that the resources of a single machine will not be enough. And then you are back to serialising things and sending them through an even slower channel. Haskell also has poor facilities for compute-intensive stuff, although they are different facilities. For example, laziness makes reasoning about performance more difficult. Space leaks are fairly easy to create unless you know the common gotchas. Most naive/idiomatic Haskell code performs several orders of magnitude worse than whats possible with optimized code. Etc etc.
- KirinDave 10y ago> But those are not web-server problems. I disagree. Totally and utterly. API servers are webservers, and in fact a pretty big subgroup of them. API servers can often run into computational requirements in mid request, and it sucks that ONE slow request can cause latency ripples across your entire process. > Haskell also has poor facilities for compute-intensive stuff, although they are different facilities. For example, laziness makes reasoning about performance more difficult. This is changing the subject, and ultimately a non-sequitur. "This also has problems which are different" is not actually an answer to the criticism that nodejs is bad at these workloads. Say it with me. It's okay to say. "Nodejs is bad at compute-intensive workloads."
- harpocrates 10y agoTo be fair, the fact that JavaScript needs a custom syntax (requiring preprocessing) for a problem this specific is upsetting. By contrast, the Haskell solution has just uses the IO monad - effectful-computations returning something of type `a` become first class and have type `IO a`. If you want to get dicey about atomic operations you have the `STM` monad. If you want to make very clear when you sequence versus race operations (and more along those lines) you have the `Async` monad. None of these need a special syntax - they are just regular monads. That said, I agree that the article should at least mention the likes of `await`...
- KirinDave 10y ago"To be fair" Haskell has so many syntactic and compiler extensions that it's a genuine problem for code maintenance over time. Begrudging Javascript 1 or 2 seems somewhat petty given how common it is to see compiler extension preambles at the top of every haskell module.
- harpocrates 10y agoPoint taken, but this is more a reflection of the fact _GHC_ is used as a playground for every new PL idea. Haskell itself has no extensions, and you certainly don't need any extension to get what I was talking about above.