As someone who has spent the last six months coding in Node.js, I completely disagree with this notion.
One of my favorite advantages of Node is in fact that it is based on javascript. When building my application I can share code between client/sever side (string formatting methods for example) and don't need to do any mental context switching when it comes to languages. I've developed in javascript for years and all that I have learned doesn't go out the window here, all I need to do is learn how to use a few libraries.
The "faults" of javascript are primarily inexperience with event looped architectures that use callbacks (people complaining about having to use callbacks and the spaghetti code messes they make), people remembering the past horrible browser implementations of it (where something would work/be fast in one browser and not another ... Internet Explorer I'm looking at you) and confusion over how prototypical languages work vs. standard OOP.
Callbacks aren't that bad and a few extra nesting levels shouldn't create chaos in your code. Since its based entirely off of one javascript implementation (V8) the cross browser stuff is gone. How it handles OOP is different, just watch these videos first and you'll be fine: http://www.yuiblog.com/blog/2007/01/24/video-crockford-tjpl/
As far as speed goes it gets huge benefits by being javascript. Every time the Google coding machine feels like making V8 a little faster, Node gets faster too.
Yes its different, no its not worse ... its better but takes some getting used to.
I think the fundamental objection fogus has is that the language does not support the programming model. For example, I can program object-orientedly in C, but that language does not help me do it. I have to do much of the work myself. So you can program in JavaScript in an event-based way using Node.js, but JavaScript itself does not help you much in doing so. It requires that you do most of the work yourself - work that is mechanical, and basically requires that you be a human compiler.
Disclaimer: I don't program in JavaScript or Node.js. So I'm talking from conceptual understanding, not experience.
Fair enough, but the examples I've seen look like continuation passing style (http://en.wikipedia.org/wiki/Continuation-passing_style), which is something we know how to abstract behind language syntax and semantics. In what way am I wrong?
Programming in an "event-based way" is a huge part of writing traditional JavaScript in the browser. You're handling user events, Ajax responses, the page load itself. And JavaScript's first-class functions and closures are extremely well-suited to the task.
"The "faults" of javascript are primarily inexperience with event looped architectures that use callbacks"
Maybe for a lot of people this is true, but for me the faults of javascript are ingrained language "features". Actually, I'm not the only one who thinks so, because theres a website dedicated to it[1].
The latest example on that blog (from Feb. 5th) examines the unexpected effects of type coercion when using double-equals for comparison. That's fine, as far as it goes, but, like most of these examples, it's a WTF that experienced JavaScript developers know how to mitigate (triple-equals) and not that big of a problem in practice. I'm trying to think of a time that JavaScript's wacky coercion rules actually bit me and I can't.
Well, the wacky coercion rules are just one of many Javascript gotchas. I agree that most of them won't affect an experienced javascript programmer, but that doesn't mean its not a language fault. For example, in C, most programmers will know to check bounds or to use strncpy instead of strcpy, but a lot of C code that has buffer overruns still slips through.
My point is that any language fault can be memorized and worked around, but that doesn't make it go away. Also, I've noticed a lot of non-experts using javascript, so if the obvious solution isn't the correct one (using == vs using === for example), then that does cause real problems.
Note that I'm not saying Javascript is an especially bad language, just that it has, in my opinion, some severe faults.
Since fogus is responding here: Other than hitching your wagon onto the fantastic hype storm that Node.js has generated, which I must say is a good move overall, reading your target description, why not start with Haskell or Erlang? You could start from there and mutate out but actually have a library base to start from. Or one of the languages sitting on top of Erlang like Reia. (Or discover that you don't need to mutate away after all.)
Starting from scratch is going to be difficult at this point; by the time you even have a half-decent stack you're going to be way behind.
(At the very least... you do realize this has been done and you don't have to start from scratch, right? Rather a lot of Node.js users have exactly the cognitive holes I'd expect from not knowing that Node.js is at best "keeping up", rather than being anything like cutting edge. And I'd say this style of programming was actually on its way to the dust heap of history before being brushed off and gotten a shiny new coat of paint sprayed on... it really doesn't work very well....)
I will upvote you if you explain what you mean by the last part about "this style of programming" previously being on its (it's? anyone) way out. Do you mean an asynchronous style using callbacks?
Yes. Asynchronous style using callbacks has been done many, many, many times, and it really doesn't work very well. It does small things quickly, but have you noticed the number of complaints about how Node.js produces spaghetti code increasing lately, quietly and slowly but steadily? It's still buried under the hype, but expect it to get worse. And not "get worse before it gets better", just... get worse.
Javascript doesn't particularly bring any abstraction capabilities to the table that Perl (POE, Event::Lib, IO::Async), Python (Twisted), or Ruby (EventMachine) doesn't, and current Javascript is actually deficient in the abstraction department by comparison. (The latest ECMAscript spec can compare to them, so Javascript will get there. Something like Python generators allow you to string together a couple more tricks together before the complexity becomes a problem, and Javascript will eventually have this.) They all result in programs that end up the same way. There are some tricks you can play that improve the situation a bit over raw callbacks, like trying to sequence them, but composing these tricks together gets harder and harder the fancier you get.
None of those libraries really took off and some of them I know had way more effort poured into them than Node.js has seen yet, like Twisted. And it's because the style, combined with language runtimes that can't manage the scheduling for you while retaining the code context, has certain inevitable results. You can (and should!) delay the inevitable, but it's still inevitable.
At the very least, I would suggest that if you going to consider using Node.js you should be aware of the fact that it's not a blindingly new thing, it's a relatively well-explored space being translated into a new language, and you can get a good sense of the tradeoffs from the many previous explorations of this space. There are still places where it may be the right choice; as I said in that other message, I have chosen event-based coding in another context because it was still the right choice. But I knew the tradeoffs going in, and took steps to mitigate them, rather than being blindsided by them.
what do you think of that erlang version of the V8 interpreter I posted about higher up? I do find the idea of using the erlang VM where it can do a lot of good to be very attractive. While I would probably prefer python running on Beam to Javascript, JS is very "lightweight" if you take my meaning a certain way, and quite ubiquituous even compared to Python. It would also seem to dissolve your specific complaints posted here, would it not?
I use this architecture myself where there's an Erlang thing sitting in the center, dispatching relatively heavyweight jobs to Perl daemons waiting for them, and it does work. That isolates all the "eventish" stuff in Erlang, and turns the Perl daemons into, well, basically webservers, in the sense that they are just serving stateless RPC answers synchronously. This definitely avoids the complexity explosion.
You don't need a project like Beam.js to do that, but having someone else having already written the utility code can certainly be useful. Nothing like that existed for Perl but it hasn't been hard to do what I needed.
"way out" belongs to "it", so you're looking for the possessive form. English pronoun possessives do not use apostrophes (whose, mine, his, her, their, its), while everything else DOES (the dog's, Steve's, Portugal's, the crowd's).
Why would you write this: [long_io_operation with callback]
When you can instead write this: [long_io_operation without callback]
Off the top of my head, I'd say because "var result = long_io_operation(req);" is not only not self-documenting but actively deceptive if long_io_operation is an asynchronous call.
I'm not sure I see what's to be gained by pretending closures don't exist.
It's not deceptive if you have a preemptively multitasking runtime that you trust (like the Erlang VM); it's a much more natural way of writing code. Single-threaded, event-driven callback spaghetti makes code harder to read, and inherently means more code.
If someone took v8, added lightweight coroutines, and wrote a preemptive scheduler for it, I bet you could massively reduce the SLOC count for large node.js apps.
But we aren't talking about Javascript. It's about a theoretical replacement language.
Also, the reason you care about the "synchronicity" in the first place is because your environment forces you to care! When it all Just Works (TM), you don't care whether it's "synchronous" or not. That's a deficiency in your language, not a feature.
I'm not theorizing. I program in this sort of language all the time. You worry much more simply about how long something will take, which you can never not worry about, rather than how many bits are "synchronous".
It is not that simple in real life, unfortunately. Even ignoring the fact that inlineCallbacks's overhead is significant, inlineCallback forces you to serialize things which may be run in parallel. If you do
yield f1()
yield f2()
You force f1 to finish before being able to start f2, which goes against the whole point of using something like twisted in the first place ! I have also noticed that inlineCallback screw up the traceback (e.g. http://twistedmatrix.com/trac/ticket/3622 - I still see this with twisted 10.0.0).
While I am no genius, I am a decent programmer, with quite a few years of experience in python, and I still can't read most non trivial async code in twisted with confidence. It is almost impossible IMO to get a clear idea of the codeflows and especially error flows to ensure error handling is done right. Error handling is already difficult as is, adding exception made it that much harder, but adding async makes it basically impossible except for Vulcans.
In real life inlineCallbacks's overhead is not significant compared to typical IO times (even snail in a park reaches its destination sooner than a rocket makes it to Pluto and back).
inlineCallbacks allows you to write code in a synchronous manner (that's the point). If you'd like to wait for completion of both f1 and f2 functions simultaneously you could:
yield DeferredList([f1(), f2()])
The ticket you mentioned is closed as invalid. Use `failure.printTraceback()`.
Non trivial async code is hard to read (period).
Exceptions allows you deal with errors in the place where you know what to do with them, not in the place where they arise (as in any other Python code).
inlineCallback overhead can certainly become significant. Saying that it is not an issue because it is nothing compared to IO only works if you are not CPU bound in the first place - but for many applications in python, you are CPU bound.
Using deferred lists is a fair point, but then you are kind of back to using deferred directly.
As for tracebacks, I used the ticket as an example: if you have to use failure.printTraceback, it means you caught the exception, which means you need to catch them everywhere. But the problem is when there is a big in your application because you forget to handle an exception - debugging those is a PITA because you don't get the right traceback.
So yeah, async code is hard, inlineCallback sometimes helps, but all this really feel like a big kludge to me - I would rather use a framework where async is abstracted away. Doing all this by hand does not work very well for complex applications.
Seriously, CoffeeScript is amazing. I've been working on a large client-side project in CoffeeScript for the past few weeks and every time I look at the resulting JS output I can't imagine having written it by hand; it's been a massive boon to my productivity. I wrote JavaScript for years before this and hated it at times, and now I can't ever imagine going back if given the choice.
CoffeeScript is much easier to read than javascript, that alone is a blessing. Admittedly I haven't used coffeescript much yet, I just cranked it up with nodemon a couple of weekends ago. I need a good starter coffeescript project that works on the backend with node, and the client side.
The good news for fogus is that everyone who prefers CoffeeScript validates the idea that an alternative syntax is enough of a win to be worth the adoption headaches.
Despite all of the weirdness of JavaScript, the lack of implicit return is it's biggest design flaw imho. The functional/scheme-like virtues of JS are well touted, but this small missing feature means implementing them always requires the slight but constant overhead of ending every expression with 'return', which then means that programming in a way which should be natural to javascript, isn't.
But whatever he is after have to be much more lucrative than writing coffeescript.
Apart from pleasant syntax, coffeescript also improves upon semantics where it can - this binding, lexical scoping etc etc. People are willing to adopt Coffeescript because it compiles to javascript, it has improved semantics and clean & mostly familiar syntax.
To sum it up, the inertia is small when moving from JS to cofffeescript. That doesn't look like the case with funode.
Having coded JavaScript professionally and enjoyed it thoroughly for 6+ years now, I respectfully degree. The Harmony project shows just how much work there is to be done to make JS more fun/practical to use day in and day out.
They're "hoisted" and only local to the function, though. I'd (personally) prefer it if it worked like C, and a variable was defined from the "var" statement until the next closing brace. Then you could have a variable local to one branch of an if statement, etc, without faking scopes by abusing closures (eg (function() { })(); ).
That's because closures actually capture the variable, not the current value of it. They'd be a lot less useful if they just made a copy of the variables...
My intended point was that if Javascript had true lexical scope then each iteration of the loop would create a distinct variable. A reference to that variable would then be captured by each closure. You can simulate the behavior of lexical scoping by "abusing" closures as someone mentioned before. Like this:
for ( var i = 1; i < 10; ++i ) ( function( i )
{
setTimeout( function( )
{
alert( i )
}, i * 250 )
} )( i )
> You can simulate the behavior of lexical scoping by "abusing" closures as someone mentioned before.
I wonder about the word "abusing" here. Languages that have block-level lexical scoping (I hope I am using the correct word) sometimes implement block scoping by renaming the variables and hoisting them to the enclosing function, and they sometimes implement block scoping by creating a new closure just as we do by hand in JavaScript, and then hoisting the result.
For example, naïve implementations of Scheme give you a "let" macro that is expanded into the closure form you give above, and then an optimizing step comes along a little later and performs lambda hoisting for you. So this "abusing" we are doing is what the language would have done for us in certain implementations anyways!
That isn't lack of lexical scope. It's that the for() syntax isn't purely functional in how it binds variables. One could define an equivalent syntax in Scheme and see the same result.
That was me who mentioned it, actually :-) Yes, I see what you mean now, sorry!
This reminds me of bug in the Visual C++ 6.0 compiler, where a variable declared in the "head" of a for loop would continue to exist after the loop scope had closed. Javascript has exactly the same problem, but by design, due to only having global and function-level scope.
Personally, I would like to be able to choose the capture method, but having value capture the default. Most of my code simply wants the value, but occasionally, capturing the variable is needed.
My most common annoyance about Javascript is the lack of bound methods like in Python Ruby etc. When you specify callbacks, you always have to provide the value of 'this' as well. If you forget, it's basically random and you're in for some heavy debugging.
'this' is evil in Javascript. The very first line in any constructor function that I write is:
var that = this;
From then on, I use 'that' instead of 'this', relying on closure magic to make things work. You never have to worry about how the function is bound thereafter.
Thanks. I think I like that solution, as it's extremely simple. Are there any drawbacks?
CoffeeScript is cool, as it is basically "Python in JS" but I have some resistance against languages that convert to other languages. I'm not so sure why.
No, no drawbacks that I'm aware of. You're basically using a closure to bind the object to the callback, instead of using Javascript's weird 'this' binding rules, that's all...
My greatest hope is that the makers of node.js will continue to abstain from language changes. Some inconvenient syntax is a small price to pay for the confidence that node.js isn't going to get turned into another regurgitation of Java meets Perl by hordes of perfectly well meaning but unsatisfiable programmers who want everything to work like the things they know. I don't mean to insult the makers or users of other languages, just to highlight the distinction of Javascript.
Fix the language, then fix event processing approach, then spend 10 yeard maturing in the highly concurrent environments and end up being Erlang. w00t.
Take a look at this, I am sure it has been posted on HN before:
http://beamjs.org/
This allows you to trade off all the cool speed of V8, but running node.js singlethreaded, for running on an alpha port of V8 for the Beam VM. If you want to sidestep the problems with Node.js, but keep the good parts, this might be a way forward.
Additionally, JS on the Beam VM can seamlessly interact with any other Erlang code, so you get all kinds of other benefits that are erlangy.
Don't get me wrong, this could end up being the best thing since sliced bread, but the advantage of closures is that a higher-order approach can work wonders. Just take a look at async.js.
For me, more interesting than a pure assault on callbacks would be to see if you could handle more asynchronous scenarios, using async.js as a baseline.
e.g. callback waterfall, callback when all children are finished, callback when any children are finished
I wish this was rated higher. I don't understand the fuss about "spaghetti code". Do all those people write spaghetti code in other languages? Because I see that not as a language failing but as sloppy programming.
What's so hard about this to get a sequential execution of functions/blocks, error'ing out on the first error:
async.waterfall(
[ function (callback) {
// do thing 1
callback();
}
, function (callback) {
// do thing 2
callback();
}
, function (callback) {
// do thing 3
callback();
}
]
, function (error) {
if (error) {
// ohnoes errors
}
// all done!
}
);
It seems like the article is not finished, so I can understand that there might be questionable sections. Also you'll note that he forgot the `error, results` argument pattern for callbacks, making me think that he is not terribly familiar with node.
I'm all for a new version of Javascript that eliminates the junk and keeps just the "Good Parts", but I'm averse to anything that serves to abstract away the bad parts by building a subset of functionality on top of the existing version javascript for a niche application (server framework in this case, other cases include cappuccino for UI, coffeescript for...everything? etc.)
The Javascript that can be used only via add-ons is not the eternal Javascript...
How about Node.as? Flash doesn't get much love these days but I've always thought ActionScript 3 would make a great server side language. It's more like other OOP languages, has an event model like JavaScript (sync or async), and doesn't have many of JS's idiosyncrasies.
I agree. I had to go back from AS3 to AS2 for a project and it felt like a step backwards, and AS2 is more like Javascript. Adobe has also open sourced the AS3 runtime. But of course the practicality is that we have Javascript in browsers, and there's a big get-the-job-done benefit to using a common language.
AS3 is based on ECMAScript 4, which was expected to become the new standard many years ago but became a dead-end after the ECMAScript committee abandoned it. That's why there's no "4" anymore between ES3.5 and ES5... ActionScript is it.
One of my favorite advantages of Node is in fact that it is based on javascript. When building my application I can share code between client/sever side (string formatting methods for example) and don't need to do any mental context switching when it comes to languages. I've developed in javascript for years and all that I have learned doesn't go out the window here, all I need to do is learn how to use a few libraries.
The "faults" of javascript are primarily inexperience with event looped architectures that use callbacks (people complaining about having to use callbacks and the spaghetti code messes they make), people remembering the past horrible browser implementations of it (where something would work/be fast in one browser and not another ... Internet Explorer I'm looking at you) and confusion over how prototypical languages work vs. standard OOP.
Callbacks aren't that bad and a few extra nesting levels shouldn't create chaos in your code. Since its based entirely off of one javascript implementation (V8) the cross browser stuff is gone. How it handles OOP is different, just watch these videos first and you'll be fine: http://www.yuiblog.com/blog/2007/01/24/video-crockford-tjpl/
As far as speed goes it gets huge benefits by being javascript. Every time the Google coding machine feels like making V8 a little faster, Node gets faster too.
Yes its different, no its not worse ... its better but takes some getting used to.