Wednesday, April 13, 2011

Douglas Crockford, for JavaScript: The Good Parts

Let me first say that I learned several interesting things from JavaScript: The Good Parts - and will definitely be using some of the ideas therein. That doesn't save Douglas from owing me a personal apology. Let's follow the standard "compliment sandwich" structure with this first post -- by which I mean surround a small amount of compliment with the ample wheaty bounty of criticism.

Global variables

First and foremost, the overtly hypocritical (and most likely innately ignorant) assault on global variables. Global variables are (probably rightly) one of the first things listed in the "Awful Parts" appendix of the book. They can cause conflict between libraries, they're hard to control access to... Of course, that doesn't stop Douglas from suggesting that the reader actually modify the globally shared JavaScript prototypes. He adds properties to Object and Function (in order to facilitate his particular object oriented programming model). Never mind that Object and Function are shared by everything. Something else already add the excellently and uniquely named Object.create? Tough fucking shit. Let's add some methods to String, too, while we're at it, I bet no library we might employ uses Strings.

False robustness and needless adversity

Perhaps while tearfully washing dishes in my kitchen as part of an extended expression of genuine contrition, Douglas can explain to me why he keeps writing about object users as "attackers" and "tamperers". Maybe it's a reflection of childhood trauma that manifests itself as a pathological distrust that he's struggling to overcome. I do not know the answers, yet. Web programming (which I am very new to) is full of security concerns - malicious users will try to compromise your site or fellow site visitors with bogus inputs. They might take your scripts, modify them, and try to use the modified version to molest the backend in twisted ways. But a fellow coder - someone who's just using the random library you wrote - is not your enemy.

This is not to say that you shouldn't code defensively. Private variables are a good way to enforce proper code behavior, free of shortcuts and with fewer API misunderstandings (and, like static types, they're not in JavaScript). Douglas is good enough (thank you!) to provide a method of creating private variables from closures. I like this system. But in his language and logic, he goes too far. For example, he extols a pattern by claiming that it makes your object's methods continue operating correctly, even if some properties have been overwritten. He calls this robustness.

As someone who comes from a C and C++ background, and is all too familiar with memory errors, I am not taken in by this false sense of security. Once something has accidentally overwritten the properties of your object, you are fucked. The error has happened, it is something we speak of in the past tense. Being "resilient" to the error is merely poorly hiding its existence. The only way to be 100% resilient to having some property overwritten is for that property to not matter. In that case, why did you have it in the first place? (And that's where private variables come in -- by making your variables private, you make there be less stuff to accidentally clobber).

The greatest insult: floating point

Now, for the greatest insult, that requires the greatest redress. In the middle of the Awful Parts appendix, while skimming through a litany of JavaScript's numerous design errors (oh, if you designed JavaScript, you also owe me an apology, just a heads up), I found a section labelled "Floating Point". I imagined that there would be some small grievance about how JavaScript doesn't have enough math functions or something -- only to find that Douglas Crockford was going after the entire idea of floating point math.

Apparently, 0.1 + 0.2 does not equal 0.3, and this is "the most reported bug in JavaScript". (I'm pretty sure the most reported bug in JavaScript is "help, this language is bullshit"). He lays the blame squarely at floating point's feet, saying that JavaScript was wrong to implement IEEE 754. The nausea I experience relaying this idiocy is alone deserving of an apology. People doing financial calculations have a reasonable expectation of exact results, he says - I say we have a reasonable expectation that people writing code that does financial calculations have a fucking clue as to how their math will work on a computer. He goes on to suggest that the right way to handle this is by scaling your values to be in cents (or whatever quanta you happen to care about). It's interesting to see DSP-world fixed point evangelism suddenly materialize in JavaScript. Be warned: unless you heavily wrap this scaled approach in objects, you will make a mistake somewhere.

First, a pedantic point. IEEE 754 doesn't specify a base. You could, hypothetically, have a system that does standard floating point arithmetic in base 10, giving you the precious decimal precision you hunger for. So the Douglas's anger should be directed at the language designers, who dared to use binary just because that's how CPUs and other programming languages have worked for decades. But, maybe he would be happier if JavaScript ran a software floating point library in base 10. It could be implemented with strings of characters or something, make it nice and easy to read. Performance might be a problem for a while, but hey, Moore's law, right?

But somehow, I don't think Douglas is asking for a software floating point implementation. I think he's profoundly upset that information is not being stored precisely. Douglas, maybe you would be happier using Mathematica. Because a symbolic algebra package is the only way you can actually ever avoid every class of this problem. Sure, you can fix 0.1 + 0.2 = 0.3 by using a decimal approximation. Next thing he'll want is 1/3 + 2/3 = 1. Or sqrt(20)^2 = 20. There's no end to this. Maybe we should turn Maple into a Firefox extension.

I'd like to call IEEE 754 a godsend. But it wasn't handed down to Moses on stone tablets. It was wrenched from the void of the universe by a collection of mathematicians and computer scientists an order of magnitude smarter than me or Douglas Crockford. It salvaged numerical computing -- powering not just the scientific and financial applications of that time, but nearly every game and application that we use today. Even the designers of Cray supercomputers didn't do floating point math exactly the right way - they made significant design mistakes (involving guard digits, etc). (The fact that JavaScript makes it nearly impossible to write decent floating point code, and the reasons for it are issues for a separate vein-throbbing angry rant).

And here comes Douglas, with a bone to pick. He think he knows better -- why can't math on a computer just work right? Fuck you, Douglas. Before you apologize to me for penning this travesty, please travel around the country, apologizing first to Kahan, then to the other IEEE 754 architects. 


 In short, while I like the idea of using closures to store private variables, and appreciate that I should use === instead of ==, I do not think these concepts make up for the gross lack of computer science savvy that this book represents. Perhaps the world of JavaScript book writers has nothing better to offer. (I have been liking the JQuery Cookbook, but that's for a library, not the language). Perhaps people whose books I haven't even looked at owe me preemptive apologies for their work. I may never know.

Appendix: Supplementary apology for incorrect philosophy

As an addition to the above essay, I'd like to make some comments on Crockford's language design philosophy. In Chapter 10, Beautiful Features, Douglas waxes poetic about language design, feature creep, and what makes a language beautiful. Unfortunately, his argument boils down to "Features that offer value to a minority of users impose a cost on all users". With some word substitution, this sounds like some sort of ultra-libertarian political screed against funding for leukemia research.

I think Douglas owes cancer sufferers an apology (While the offense is not great, I think he should apologize to them first, because they might not be around that long.)

On the surface, this argument for simplicity makes sense. Of course we want to get the core parts of a language right. Of course features need to be specified and documented. Clearly we should go back to basics and use something that "just work[s]". But on any depth of reflection, this is empty platitude and nonsense.

There are a thousand languages out there, and dozens new every year, that perform "hello world" in three lines. Languages that let you write state machines and web servers in simple, elegant ways. What differentiates these languages (besides breadth of platform and library support) is not how good their "cores" look. They're divided by three differences:

Adaptability to new problems. Of course, if you're writing simple example apps for a book, having a cat object say "meow" is all you need to think about. The reader smiles and nods and turns the page. When some new application or platform comes along and we ask a language to stretch - that's when its mettle is tested. It has to have a great core, yes - but that's not enough. (A lot of languages fail this because they fail to adapt to multi-threaded environments, for example).

Uncommon use cases. You start writing some little spreadsheet app. You let the user add up numbers, and its great. Before you know it, it's popular and used by a lot of people. A company asks you to make them a version that can solve linear equations. And then tells you to fuck off because the answers you're providing don't add up. Guess what? Without decent floating point support (exceptions, ulps, etc), it's hard to write robust code. You didn't think you'd be in this game when you started -- it was just another web app -- but projects change. Think of this as voting to cut NIH funding and then getting rib cancer. Cancer of the ribs.

Unobtrusiveness. And this is where Crockford has it right -- while you have to have support to be adaptable and to handle uncommon use cases, you can't let those get in the way of writing simple code when you're just trying to get something done. But just because you have a feature doesn't mean it has to be annoying. You just have to design it well.

Thanks to a correction, I've changed "strong types" above to "static". That is what I meant to say (I keep making that verbal mistake).

While rational numbers do solve the 0.1 + 0.2 and 1/3 + 2/3 problems, they do not solve the sqrt problem (which I've re-written above to be more clear and correct (I had the square inside the root instead of outside and used a perfect square).

Thanks to a reader whose comment I can only see in email, for whatever reason, for pointing out IBM's specification for (nearly) IEEE 754 compliant decimal arithmetic. If you're really into your cents being cents, this may be for you.

While discussing this with a friend, I remembered that earlier in the book, Douglas praises JavaScript for abandoning integer math -- "a number is a number", he says. He points out that this helps avoid "integer overflows" (never mind the fact that those almost never happen). So his bitching about floating point is truly an ironic outcome - he gladly traded integer overflows for the plethora of complexities that is floating point programming. Douglas, after apologizing to me, I think you should apologize to the integers. It might take a while (but at least they're countable).

For some harshness on Crockford that I'm not expert enough (at JavaScript) to make, reader Doug Holton points out Inheritance Patterns in JavaScript, a document discussion the drawbacks of following Crockford's advice on JS OOP (It pushes the Closure Library/Closure Compiler and a pseudoclassical object model)

I've decided to remove two "Cockfords" and replace them with "Crockfords". Being thoroughly lambasted on purely technical grounds  is not nearly penance enough - hence the apology and dishwashing - but Cockford didn't really fit in with the vibe I was going for. But you're totally thinking it now.

Added appendix.