Wednesday, April 9, 2014

TRIPLE EQUALS CONSIDERED OVERRATED === = :-(

So, a recent popular idea in Javascript land is that it's always better to compare values using "triple equals", ===, which won't do type conversion, over good ol' "double equals", ==.

So quick example:
  var foo = 123;  //foo is an integer
 foo == "123"; //true, foo "double equals" the string 123
 foo === "123"; //false, because they are different types

I find myself disagreeing strongly with the preference for "===", for 3 iffy reasons, and I think one good one.

1. What I'm used to

I can sound like a cranky old hack sometimes, and remembering to type === and !== seems like a pain, and just kind of looks ugly.  Moreover, I cut my teeth on loosely-typed "Duck languages" like Perl. So if I read in a string and wanted to compare it to a numeric value such as 123, I wouldn't want to fail just because they were "different things", or came from a different source.

2. How I think about Java

Java's String primitive/object has a parallel situation that is deceptively similar, with .equals() vs ==, and I think I draw a false analogy.  To whit: for Java objects, == is only true when you have two references to the same object in memory, while equals() should be true if two different objects are equivalent. So for instance,
 String foo = "bar";
 foo.equals("bar"); //of course true!
 foo == "bar"; //don't count on it, buddy

So I learned to be careful, and use the "less strict" equals() for most stuff I was interested in. This makes me nervous about ===. Like, I'm almost skeptical that Javascript
 "foobar" === "foo" + "bar";  //true (amazingly ;-)
Yeah it works, but because I think of it in terms of what it looks like in Java, I don't want to trust it.

FOLLOWUP in 2017: I just noticed that
foo == new String('foo')

but not
foo === new String('foo')
which is exactly parallel to the Java case, but arguably much less significant because String as an object is rather rare in Javascript land.


3. === is used to show off

It feels like an elitist shibboleth to me. (And yes, I love that knowing what "shibboleth" means is in itself a shibboleth.) To be fair, these people probably point to funkiness in the truthiness tables, and just odd asymmetries there, but in my heart of heart I think it's just a way for the with-it hipster coders to show they are with-it hipsters.

4. if(arg == undefined) //true for argument not passed, and null, but not for zero

Finally, this might be my most serious argument.

Say I have a function funk(arg), and returns its argument or a default of -1, if no argument is passed. (And of course it should work with 0 as a value) So:

function funk(arg){
  if(arg == undefined) arg = -1;
  return arg;
}

That works pretty well... (if I used the conditional if(!arg), that would accidentally catch 0 and return -1, and so in general I shun simple boolean expressions, preferring to check for undefined.)

So what I like about == is that is that funk(null) would be caught, and replaced with -1, but if I used === funk(null)would return null, because undefined === null is false.  (Of course, "null" and undefined are kind of odd beasts in Javascript land; some people think the "undefined" value is just an accident of history anyway.) (PS don't do stuff like using undefined as a real value, like calling funk(undefined) - that's just R,O,N,G, wrong.)

So basically, I prefer the behavior of double equals, and prefer its made up "this would fail!" arguments to the ones made in favor of trouble equals.

(Quick tip: if you don't know it already, the "console" you can get to in Chrome inspector or Firefox Firebug is great for doing this kind of little functional definition, tooling around with making and calling functions, just ignoring the page it's on. And of course it's an invaluable tool for poking around your Javascript in general.)

FOLLOWUP: A friend of mine points out "NaN" kind of messes up my simple != undefined filter, since it is defined, but probably not what we were expecting to work with what I described.

POSTSCRIPT FROM THE END OF 2017
Listening to a podcast with scientists pontificating, I realize I treat triple-equals usage similarly to how I treat the correct usage of "data are plural", and for similar reasons: a begrudging respect for people using a shibboleth correctly, set against a personal bias for the looser usage; with that split masking a philosophical difference in worldview.

My worldview is: people and things are more important in how they interact than in their internal makeup. Take "Data". It's technically a plural world from Latin, with "Datum" being the singular. But a "Datum" is useless to the point of meaninglessness on its own - ONLY through multiplicity does a datum go from being a one-off anecdote to a statistically meaningful bit of information. Casual use would treat "Data" as a kind of singular group noun - "what this data suggests" vs "what these data suggest", and since that group-making is the only useful way people interact with data, "this data", the street usage, makes much more sense.

(I'm not a big fan of the old tradition of presuming Latin rules need apply to English anyway- like how you should never split an infinitive ("to boldly go") since such a construction is impossible in Latin where the infinitive is a single word.)

With triple equals, I return to the basic idea that it means "reject the comparison if the things being compared aren't exactly the same type" - an internal analysis. Double equals says if two things have the same value when they interact, that's fine! We don't care about the history or composition of the things, just how they'll interact now. (A long history with Perl and other duck-type languages helps inform my view, I think.)

To wax philosophical, I've realized this difference in worldview- whether what's important is the history and internals of a thing (since that will be the surest guide to predicting long-term behavior, and/or give you a special revelation of how things "should be") or whether we should attend to how things are capable of interacting with the outside world - is profound and tough to bridge.

I'd recommend the book "Surfaces and Essences: Analogy as the Fuel and Fire of Thinking"... and then when I hear some punk like Scott Adams say that because analogies are always imperfect they can never be persuasive, and that it's where "reason is embarrassed to show its face"... balderdash. Finding parallels in how different systems are interacting makes up one of the most critical tools in understanding the world, no matter that there will ALWAYS be some difference in intrinsic makeup. (Of course, saying there are only surfaces or only essences is a false dichotomy; some analogies run deep, that two systems are interacting in parallel ways because of parallel functioning in their guts. And some analogies are just shallow and rhetorical and are of less value.)


PS to the PS: Regarding point 4, my college buddy (and freshman roommate) Rob points out some cases where keeping the null vs undefined is useful - the differenc between a missing key and a key that points to a null valye - like an API where the object.user will not be set at all if a call hasn't been null, or if the call completed but there was no user or whatever, it would be null....



No comments:

Post a Comment