Triple-Equals In Javascript

Molly wants to know what === means in JavaScript, so I thought I’d write it up, from as close to first principles as my brain can think about. Fun!

Most programming languages use the = operator for variable assignment 1:

1
x = 5;

Now any time the variable x is referenced, the expression referencing it will operate on the value 5. You’ll recall this from algebra, of course:

x = 5
f(x) = 3 + x 2

That whole system evaluates to 28. If x were 3, or 17.2, it would evaluate to something else. Variables in software work the same way. 2

Of course, if the = sign means variable assignment, it could be ambiguous when we want to check if two variables are equal:

1
2
3
if ( x = 27 ) {
    do_a_thing();
}

Whuh-oh, did we mean to assign 27 to x, or did we mean to check to see if x is already 27? Most popular programming languages resolve the ambiguity by using a double-equals to check equality:

1
2
3
if ( x == 27 ) {
    do_a_thing();
}

So we have single-equals for variable assignments, and double-equals for equality comparison. How do we get up to triple-equals? Better put on your boots, we’re heading into the mucky bits. Not the nice new sage ones—those are nice, Molly, by the way, I meant to mention—put on some old ones that you won’t mind getting dirty.

Suppose you have a bit of text from a user. In JavaScript, and most languages, this bit of text is a string: a series of numbers, each of which represents a letter or other character. For example, if a user has typed “27” into a textbox, the computer will store it with three numbers: 50,55,0 3. The 50 is ASCII for “2”; the 55 makes 7, and the 0 means “that’s all there is in this string.”

Now, suppose you have this string that the user gave you, and you want to know if it’s equal to the number 27. You might try comparing it:

1
2
3
if (string_from_user == 27) {
    do_a_thing();
}

This will, in fact, work just as you wanted it to. However, in many early languages, it would’ve been totally illegal. The compiler would yell at you that you can’t compare strings to numbers, because what does that even mean? The string just is a bunch of numbers. How do you compare a bunch of numbers to a single number? I don’t know.

JavaScript, however, has learned a few tricks since those early languages were all the rage. It knows how to perform type coercion. When you use == to check to see if a string is equal to a number, you’re telling JavaScript that you want to know if the string’s contents describe a number that is equal to your number. You’re saying you want to compare your 27 to the user’s “27”, not to the machine’s 50,55,0.

Unfortunately, JavaScript’s rules for type coercion are broken. As you recall from math class, one of the very important rules for equality is that it needs to be transitive: if a = b and b = c, then a = c. However, because of the way JavaScript coerces types, this is not the case. Let’s check it out, using the string "0" for our a, the number 0 for our b, and the string ""—a string containing nothing at all—for our c.

"0" == 0
true

0 == ""
true

"0" == ""
false

Aaaaaa what just happened? Well, JavaScript gave us just enough rope to hang ourselves. A couple minutes ago, we earnestly wanted it to use the number represented by a string when comparing that string to a number. So when we compared "0" to 0, it obligingly said “yep, they’re equal.”

Then we asked it to compare 0 to "". 4 “Sure,” JavaScript told us, “those are both pretty no-ey. I’ll say they’re equal.”

Finally, we asked it to compare "0" and "". Well, now there’s no coercion necessary. These are both strings, so JavaScript compares their contents, without worrying about whether they’re maybe representing the same thing. Their contents are totally not the same, so JavaScript tells us they’re not equal.

So, for people who like their software to do what they expect, JavaScript also provides ===. === performs no type coercion: if you try to compare any string to any number using ===, it will tell you “not equal,” no ifs ands or buts.

And that’s why === is important, and why working with JavaScript makes Selena feel stabby.

1 Some languages use other operators to avoid this whole froofaraw. Pascal, for example, uses := for variable assignment.

2 Except for all the ways they don’t work the same way at all, which aren’t interesting at this time.

3 This is a filthy filthy lie. The internal representation is much more complicated. The complications are not interesting at this time, and the lie is at least one that points roughly toward the truth.

4 Because of the lie I told you about the internal representation of strings, you might be tempted to think that JavaScript is comparing the number 0 to the 0 that is the only thing in the empty string. But remember, that was a lie; that’s not what the string looks like inside at all. It might look like that, in some languages! It does not look like that in JavaScript. Good on you for thinking of it, though!

The System Pip On Lion Is Completely Broken??

I sat down to hack on Pullme for the first time on my new macbook. I ran into unreasonable roadblocks:

1
2
3
4
5
6
7
8
9
$ mkvirtualenv pullme
Running virtualenv with interpreter /usr/local/Cellar/python/2.7.2/bin/python
Overwriting pullme/lib/python2.7/orig-prefix.txt with new content
New python executable in pullme/bin/python
Installing setuptools.............done.
Installing pip...............done.
Traceback (most recent call last):
  File "<string>", line 1, in <module>
ImportError: No module named virtualenvwrapper.hook_loader

Augh, what is happening? Well, for some reason the pip that comes with OSX Lion puts things in a directory that’s totally unrelated to the directories that the system Python searches:

1
2
3
4
5
6
7
8
$ python -c 'import sys; print "\n".join(map(repr, sys.path))'
''
'/usr/local/Cellar/python/2.7.2/lib/python2.7/site-packages/distribute-0.6.24-py2.7.egg'
'/usr/local/Cellar/python/2.7.2/lib/python2.7/site-packages/pip-1.1-py2.7.egg'
...snip...
'/usr/local/Cellar/python/2.7.2/lib/python2.7/lib-dynload'
'/usr/local/Cellar/python/2.7.2/lib/python2.7/site-packages'
'/usr/local/Cellar/python/2.7.2/lib/python2.7/site-packages/setuptools-0.6c11-py2.7.egg-info'

Python is only looking in /usr/local/Cellar/...

1
2
3
4
5
6
7
8
$ locate virtualenvwrapper
/Library/Python/2.7/site-packages/virtualenvwrapper
/Library/Python/2.7/site-packages/virtualenvwrapper/hook_loader.py
/Library/Python/2.7/site-packages/virtualenvwrapper/hook_loader.pyc
...snip...
/Library/Python/2.7/site-packages/virtualenvwrapper-3.0-py2.7.egg-info/requires.txt
/Library/Python/2.7/site-packages/virtualenvwrapper-3.0-py2.7.egg-info/top_level.txt
/usr/local/bin/virtualenvwrapper.sh

Meanwhile, virtualenvwrapper is installed in /Library/Python/...

I solved the problem with a new pip:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
$ easy_install pip
Searching for pip
Reading http://pypi.python.org/simple/pip/
Reading http://www.pip-installer.org
...snip...
Installed /usr/local/lib/python2.7/site-packages/pip-1.1-py2.7.egg
Processing dependencies for pip
Finished processing dependencies for pip

$ type pip
pip is hashed (/usr/local/bin/pip)

$ hash -r 

$ type pip
pip is /usr/local/share/python/pip

$ pip install virtualenvwrapper
Downloading/unpacking virtualenvwrapper
  Downloading virtualenvwrapper-3.0.tar.gz (717Kb): 717Kb downloaded
  Running setup.py egg_info for package virtualenvwrapper
...snip...
    Installing virtualenv script to /usr/local/share/python
Successfully installed virtualenvwrapper virtualenv
Cleaning up...

$ mkvirtualenv pullme
New python executable in pullme/bin/python
Installing setuptools.............done.
Installing pip...............done.
virtualenvwrapper.user_scripts creating /Users/andrewlorente/Envs/pullme/bin/predeactivate
virtualenvwrapper.user_scripts creating /Users/andrewlorente/Envs/pullme/bin/postdeactivate
virtualenvwrapper.user_scripts creating /Users/andrewlorente/Envs/pullme/bin/preactivate
virtualenvwrapper.user_scripts creating /Users/andrewlorente/Envs/pullme/bin/postactivate
virtualenvwrapper.user_scripts creating /Users/andrewlorente/Envs/pullme/bin/get_env_details

Success!

It’s possible I actually installed pip some crazy way, but I don’t know where I would’ve gotten it other than easy_install. If Lion really is shipping with a pip that doesn’t install packages to Python’s sys.path, well geez, that’s just terrible.

Code Is Evil And Should Be Destroyed

Writing code, while sometimes necessary, is inescapably evil. Yes, deploying an empty directory to production will be ineffective. Yes, most version control systems are so obstinate that they won’t even accept an empty directory, and, ok, all right, I’ll admit that perhaps if we intend to have features, we’ll need to write some code at some point. However, it is completely unavoidable that this code will have bugs! Completely unavaidable. All code has bugs. There’s a reason second-language learners often practice verb conjugation with the sentence “Your code/my code/his/her/its code has bugs.”

So what can we do? It’s simple: we kill as much code as we can possibly lose.

First of all, get acquainted with your Eastern European friend, Yagni. If you’re adding code and you don’t know, with absolute certainty, that you’re going to need it, you’re gambling on the chance of later ease with the certainty of bugs and lost development time.

Second, stop hand-rolling things that someone else has already done. Whatever you’ve been working lately, I’ll bet you there’s a library out there right now that can solve your problem already. It’s better-tested than your code, it’s more feature-complete, and the author came up with a cuter name for it than you did. Look, I know writing glue code isn’t sexy. I know it’s not the sort of exciting project that looks super-cool on a resume. But you know what else doesn’t look good on a resume? “My project was notable for its cost overruns, missed deadlines, and constant bugs.”

Now I hear you over there saying, “but Andrew, if all I write is glue code, how will my program stand out from the other ones that glued those libraries together?” Well, if you don’t know that already, what exactly is it that you’re doing? What value, exactly, do you intend to create? Answer that question, write that code, and knock off for the day, because the minute you go outside those bounds you’re creating needless bugs.

Finally, never do the same thing three times. In fact, if you can get away with it, don’t do it more than once. Every time you do it, there’s a chance—a good chance, in fact—that you’ll make a mistake. Put it in a utility function, put it in a little script, put it in a wiki where you can copy-paste it, whatever—write it down somewhere and use what you wrote down, so when you realize you made a mistake, you can fix it once and stop making it.

The single most important role a programmer has is software deleter. Get out there and make a red diff!