Tuesday 28 August 2012

Even Typos Can Teach You Something: BE CAREFUL!

CoffeeScript is an interesting language, in both senses of that word. Whereas it tries to (and largely succeeds in) insulating the hapless coder from what Douglas Crockford calls "the bad parts" of JavaScript, it does not filter out all of the mind-blowing bits of JavaScript. Arrays are a good example.

Think back to your CS 101 class (or your K&R for you self-taught folks). What is an array?

In most languages, an array is a numbered sequence of elements, using (usually) sequential non-negative integers for identifying a specific element. Many statically-typed languages (such as C and Java) require that array elements each be of the same type; dynamic languages such as Python relax that to one degree or another.

But CoffeeScript and its underlying JavaScript (which I collectively call just Script) suck that up and then do some anatomically improbable things with/to it. Consider this bit of CoffeeScript interaction from a terminal (in Mac OS X 10.8, CoffeeScript version 1.2.0):

Jeffs-iMac:tmp jeffdickey$ coffee
coffee> foo = []                      # declare an empty array
[]
coffee> foo[4] = 'a'                  # assign offset 4; 0-3 have undefined values
'a'
coffee> foo[2.718281828459045] = 'e'  # Floating-point array index? Why not?
'e'
coffee> foo[-2] = 'b'                 # Negative numbers are just fine, too
'b'
coffee> foo['c'] = 27.4               # A string can be an index. It's still an array.
27.4
coffee> foo                           # What do we have now?
[ ,
  ,
  ,
  ,
  'a',
  '2.718281828459045': 'e',           # Non-sequential offsets look a lot like object fields
  '-2': 'b',
  c: 27.4 ]
coffee> foo.length
5
coffee>                               # Control-D to get out of the REPL
Jeffs-iMac:tmp jeffdickey$

So? What's the upshot?

Well, one thing many languages do to/for you is to throw an exception when your program gets too fast and loose with its indexing into an array. In Script, that doesn't happen; any scalar is a usable index, and if you pass something in that isn't a scalar (like an object), it'll have its toString() method called to generate an index value. And did I mention that this is all case-sensitive? Your typo opportunities are limited only by your imagination and by the accuracy of your fingers…

In case it wasn't obvious from the preceding incredulous rant, I really wonder why this "feature" is in the lnaguages; would it have really been that coercive to say "I'm sorry, Dave; I'm afraid I can't do that. Perhaps you'd rather use an object hash instead?'