This is a breakdown of all the ES-Harmony features I’ve paid enough attention in order to have a coherent opinion about them. An article of this size is a long time coming so…sorry for the length, buckle up, enjoy the ride, and watch the fireworks in the inevitable responses to it.
My second concern is reducing confusion. We have three primary sources of confusion in the language: inconsistency, boiler plate, and flat out bugs. Two things that look identical are sometimes used differently. Code that is pervasive and identical is a great place for bugs to hide that you won’t find because you continually pass over the code blocks and new users are horribly confused about when and where issues occur. And the bugs, we all know the damn bugs, except new people who find them everyday and wonder why we chose this damn language.
Those are my only concerns. If you share them you’ll likely agree with me, if you don’t I guarantee you’ll hate everything I have to say. There may be, no, there has to be a lot I can’t see though this lens. This article is not the whole picture, but I can only write about what I care about.
A current source of confusion, especially for new users, is the many meanings of the keyword
The language says
function might describe two things: 1) A function object executed by calling
name() and 2) A constructor which can be used to instantiate a new object using prototypal inheritance by calling
new Name(). The fact that these are the same thing and that you can forget the
new keyword on a constructor and it won’t fail early is something even seasoned programmers trip on. It’s actually the policy in node.js that users do not call
new on APIs in core and most modules follow the same patten of returning an instantiated object from a normal function to remove this confusion from taking place.
function. In some cases, it may actually be a constructor for an object that uses classical inheritance. Well, isn’t that just great. For years now, if you named a constructor beginning in anything but an uppercase letter you would be brought outside and shot in the leg, so we have a somewhat obvious distinction between functions and constructors but we have no such indicator of what inheritance model that object might be using.
Alex might be surprised to find out that I’m actually not entirely opposed to the notion of a class keyword. I was actually a proponent of the thread Jeremy Ashkenas started as “Minimal Classes” which was basically the exact same pattern and semantics we have currently for classical inheritance but with a class keyword. In that form, I think it reduces confusion and is a good idea. I haven’t had time to watch what that proposal has become as it makes its way through committee. When it passes through the TC-39 meat-grinder I might actually hate it, but I don’t hate it on principal the way Alex assumes. Having two inheritance patterns in the language is confusing, but we already have that—calling out which one could actually help.
This is the part where I rant about generators.
I spent about 5 years writing lots of Python. In that time I can say that generators were always confusing to write, edit, and especially to debug. In Python, I found that they were mainly used to work around the terrible performance of Python function execution or because someone was just trying to be really clever (that person was routinely me).
I’ve heard several people say, “We need generators in node.” These people are rarely node programmers. Generators are used as a blocking return in Python and Ruby to iteratively return data being received by a file descriptor (socket, file, stdout/stderr, etc). We have something much better in node, we have Stream objects, and they work better than generators. The node community has built a huge amount of value on top of them that we could never have built on generators so even if generators make their way in to the language they’ll only be used for iterative computation tasks and never for IO.
Oh, and the best part,
function gets to be another thing, because it wasn’t confusing enough already. NO, NO, NO, there’s a star after the
function keyword, which somehow makes me more confused when I read it.
But wait, there’s more confusing function work. You can use that old, busted, 8 character (HOLY SHIT!)
function keyword to define your functions or you can use the new hotness straight outta coffeescript (SUCKA!) arrow syntax functions (WHAT WHAT!).
While I can certainly remember many people asking me, “Why do I have to type 8 characters, why can’t I hit a key next to delete and then shift+. ?,” the worst part is not the syntax, it’s that arrow syntax functions actually have different semantics from regular functions. Arrow syntax functions scope
this to the outer scope rather than the function scope.
All kidding aside, I do have to explain to a lot of new people how
this is scoped. It is confusing, but not as confusing as having to explain that there are two sets of scoping rules for functions depending on the syntax you use.
This double semantic theme persists throughout the work in Harmony. TC-39 has decided that, unlike the failed ES4 effort, there will not be breaks in reverse incompatibility with ES5-strict, any code that runs today will run in this future Harmony mode. But, it’s perfectly fine to define grammar for Harmony that breaks in ES5-strict. This means that, among other things, features can only be added and none can be taken away or even changed.
We’re left with an effort that, by design, cannot fix bugs. Instead it can only add features that, when used, will work around our existing bugs but not save us from them. We’re left with the choice of either living with bugs or doubling semantics.
I’ve talked a lot about things that the language can do to confuse programmers but the biggest culprit of exaggerating that confusion is other programmers.
Going back to my Python days again, I can remember being enamored with the advanced features in the language. I learned how to use a new feature in the language every 4-6 months and it would infect all of my code. All of a sudden my loops were all replaced with big conditional generators and list comprehensions. There is a lot of incentive to do this. First off, you feel like a total badass. Second, you feel smarter than all your coworkers cause they don’t understand anything about what that code does. And finally, you’re using a feature that is, to some degree, optimized by the interpreter and better performance gives you warm & fuzzies.
What I was doing that whole time was making my code less readable, less maintainable, and terribly confusing not just for others but for myself when I would have to return to it. Offering features that enable obfuscation will lead to an optimistically 90% failure rate after people get their hands on them.
WeakMap is something we would find useful in node. Because an author may want to keep a table of “inflight” objects for dynamic debugging you need a structure with different garbage collection semantics than regular objects or else it’s likely to leak.
And oh god, please, 64bit integers, please, please, please.
Now, why can’t these objects just be added to the global namespace in a version of ES that is compatible with ES5? Probably for the same reason we don’t backport features in new versions of node.js, to encourage adoption of the new version. I can’t knock this reasoning because I engage in it but it does mean that developers will be waiting much longer before they can use these, and we need them, like, yesterday.
var, good! Oh wait, it’s not
var though, it’s
var is still around and scopes differently. sigh. Also,
const is stupid. That’s a very plain way to describe it, but it’s true. It’s based on a false premise, that someone editing my code is likely to overwrite something they shouldn’t. Because you control the scope of your variables you control the mutability, so code outside your scope is no issue, and someone editing your code can just change it to
var like I used to do whenever I encountered them during my time at Mozilla. But we’re suppose to be talking about positive things :)
Destructuring reduces boiler plate, which is great. It’s also in a rare class of new features whose syntax feels like it really belongs. The semantics are obvious the first time you see it, more things like this please.
Don’t add classes and the world at large thinks of JS as a joke and a toy
Because of the language’s history and varied implementation it lacks a strong identity. Rails and Python are not for everyone and when a feature doesn’t appeal to a particular group they don’t see that as a failure of the language but more of a cultural difference.
We don’t know what makes this language so sticky but when I ask people like Ryan Dahl and Isaac Schlueter, they tend to say, “Because it’s not changing.”