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. 

Conclusion

 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.

Updates/changes:
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.

29 comments:

  1. First, awesome post. Now, the nitpicks:

    (and, like strong types, they're not in JavaScript)

    Do you mean static instead of strong? Presumably adding a function to 3 will blow up in JS, which suggests that the types are strong, they're just enforced dynamically at runtime....

    He lays the blame squarely at floating point's feet, saying that JavaScript was wrong to implement IEEE 754.

    To be fair, there was literally no other choice; I mean, it is not like there exists a standard for decimal arithmetic with well defined semantics.

    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).

    Does that even work? Since JS implements all numeric variables as 64-bit floats, arithmetic operations on sufficiently large "integers" will be inexact, you'll still get the wrong answer.

    Because a symbolic algebra package is the only way you can actually ever avoid every class of this problem.

    There are other ways.

    I like the idea of using closures to store private variables

    You've grown soft in your old age. There's no fucking excuse for anyone designing programming languages in the last 20 years without thinking about modularity and isolation. When the designers of Windows-Fucking-95 gave more thought to isolation than you did, it is time to shoot yourself in the head.

    ReplyDelete
  2. Thanks! I've made some corrections above.

    "Does that even work?"
    With regards to your scaling question - it only works if you only ever add, subtract, multiply by whole (unscaled) numbers, or divide by something you're divisible by. So, basically, not well at all :) Hey, it's his idea, not mine.

    "You've grown soft in your old age"

    Anyways, I could go on for another barfload of paragraphs about my hatred of JavaScript -- but this wasn't about the language, it was about the book.

    ReplyDelete
  3. With regards to your scaling question - it only works if you only ever add, subtract, multiply by whole (unscaled) numbers, or divide by something you're divisible.

    I don't think it even works for that. If I want to multiply 119106029 * $11035152.45, then I'd need to do 119106029 * 1103515245, but that gives the wrong answer in rhino (JS interpreter) versus python (which correctly implements arbitrary-precision integers). The point is that a 64 bit double has a mantissa that is a whole lot less than 64 bits, which means that it only models integer math exactly for integers much smaller than 64 bits....

    Anyways, I could go on for another barfload of paragraphs about my hatred of JavaScript -- but this wasn't about the language, it was about the book.

    Sounds like a good topic for a whole series of posts!

    ReplyDelete
  4. See also here for some arguments for the pseudoclassical inheritance pattern (used in Google Closure Library), instead of the functional pattern Crockford espouses:
    http://www.bolinfest.com/javascript/inheritance.php

    ReplyDelete
  5. Thanks Doug! That's a good read, especially for someone like me who's actually just getting started with JavaScript.

    ReplyDelete
  6. Brendan Eich, the designer of Javascript, has already apologized. :)

    But in his defense, he had 10 days to implement it. It was either that,or VBScript.

    (I know which one I prefer.)

    ReplyDelete
  7. A fair excuse! Almost understandable.

    But a public apology is still not a personal one.

    ReplyDelete
  8. Python has a wonderful decimal library, just saying.

    ReplyDelete
  9. JavaScript is not strongly typed.
    "Presumably adding a function to 3 will blow up in JS", nope!
    (function(){}) + 2 === "function () {}2" in Google's V8, for example.

    ReplyDelete
  10. It's not "hypo", it's "hyper" critical.

    ReplyDelete
  11. > Perhaps the world of JavaScript book writers has
    > nothing better to offer.

    Try http://eloquentjavascript.net/

    ReplyDelete
  12. Marijn: thanks for the pointer, I'll have a look.

    ReplyDelete
  13. I hate to say this, as Crockford is one of the worst false idols out there, but about doing your financial work in integer basis, he's correct - you need to work in cents, and attempting to use floating or fixed point math in something which is precision loss intolerant is a sign of a garbage developer.

    ReplyDelete
  14. Jonas: that's strong dynamic typing, which is a kind of strongly typed.

    ReplyDelete
  15. You know, some languages both avoid integer overflows _and_ have sane numeric towers...

    ReplyDelete
  16. Let's summarise:

    You're new to JavaScript, you come from a C/C++ background, and you demand a public apology from someone who contributes his time and efforts freely to the community, and has the audacity to exercise free speech and publish his opinions in a book?

    Further, you're not even so honest as to reveal your identity, but prefer to post from an anonymous blogger profile...

    Dear Sir: PISS OFF!

    Please go back to writing C/C++, the JavaScript community does not need wankers like you.

    ReplyDelete
  17. Dear Morgan,
    Don't troll the trolls.

    While you might be comfortable outing yourself as the head of Roderick IT in Sweden, I don't think posting insane, curse-filled rants in my own name would be productive for my career. Unless my name were Glenn Beck. Which it isn't. Maybe.

    Please apologize to your own sense of humor, which you apparently beat severely and locked in the cellar. Also, I find it hilarious that your blog post on 2010.11.03 says

    "Over the years the Prototype library has been getting a lot of grief over extending the native prototypes and extending the DOM, both of which are considered bad practices."

    Given that extending native prototypes is exactly what Crockford suggests in his book (see my first paragraph above).

    Have fun getting angry at me in the future!

    ReplyDelete
  18. There aren't enough insane, curse-filled rants about javascript on the internet... I appreciate you doing your part.

    Suck it, Crockford!

    ReplyDelete
  19. I think that it's bad that book for beginners and clearly advertised as book for beginners has such a wide effect on entire community.

    For me the problem is that for lots of people Crockford's book is like a source of indisputable truth. And no book is like that (even much better non-beginners books).

    It does have errors. Like this:

    var walk_the_DOM = function walk(node, func) {
    func(node);
    node = node.firstChild;
    while (node) {
    walk(node, func);
    node = node.nextSibling;
    }
    };

    which is _invalid_ ES3.

    ReplyDelete
  20. This comment has been removed by the author.

    ReplyDelete
  21. #fail
    The Good Parts comes from an era when you couldn't find any CTO-readable book on javascript to convince your boss that JS was a serious language for frontend developement. This book is still an excellent introduction for anyone new to javascript who wants to get into the buisiness of understanding its functional aspect, and what historically, made the language suck in many people's minds.
    It's a concise book, which sortof counts, for an introductory one.
    you can actually read it from front to cover.
    then, ... well, life goes on, right ?

    As for Mr Crockford's idiosyncrasies,... well, we're supposedly educated people living mostly in democracies, we can make our own choices.

    ReplyDelete
  22. I found it impossible to read this lengthy article of criticism of someone with decades of experience in front-end development by a self proclaimed novice. Like listening to Justin Beiber bitch about what a jerk Frank Sinatra was...

    ReplyDelete
  23. If you found it impossible to read, how did you read it and why did you comment?

    First off, a factual correction. Crockford has worked on JavaScript for about a decade. Front-end development, in the modern sense, has been around for a little longer than that, but not 20 years. He worked on video games and some other stuff before 2001. He has no formal CS education. He's done nothing that would suggest that he would know the slightest bit about numerical programming (but he feels free to criticize Turing Award winning work on the subject in his shitty book).

    The most accurate musical metaphor for Douglas Crockford would be someone who has ten years experience on the vuvuzela (that's JavaScript) and some time in a college band (that's his work in the 90's).

    While I may be new to JavaScript, I assure you that I am not a novice to computer science or numerical programming. If you found The Good Parts to be an amazing book without flaws, you probably lack the breadth of experience needed to really grasp what makes good language and good style.

    ReplyDelete
    Replies
    1. All right, you finally got me to laugh out loud with the likening of javascript to a vuvuzela.

      Delete
  24. Actually I was wrong in example I've posted. It's entirely valid ES3.

    ReplyDelete
  25. Thru ur blog also, i have learnt something new and interesting part of JavaScript.
    web design company

    ReplyDelete
  26. Hello...
    The JavaScript Programming Language and then go on to his Advanced JavaScript Talks and also his DOM talks ... i hope you're good with programming theory to begin with.
    Thanks,
    JavaScript Countdown Timer

    ReplyDelete
  27. Guess what, I call him now crocky because he hasn't apologized yet for being crocky.

    ReplyDelete