by Leon Rosenshein

More WAT?

Avoiding the Wat is important. It’s especially important when you’re designing APIs. APIs are for life. People will be using them long after you’ve stopped thinking about them and using them in ways you didn’t originally intend. When it comes to language design the stakes are even higher.

Consider the JavasScript language. It’s pretty widely used, so choices in the language design there are pretty impactful. JS has a lot of built in functionality. Among them are the in operator and the includes() method for arrays.

According to Mozilla, the in operator returns true if the specified property is in the specified object or its prototype chain, otherwise false, and the includes() method determines whether an array includes a certain value among its entries, returning true or false as appropriate. Both of those seem completely reasonable by themselves. The Wat? comes when you use them together.

Consider this little snippet of code:

let x = [1, 2, 3]
console.log("x: " + x + "\n\n")
console.log("1 in x        : " + (1 in x))
console.log("x.includes(1) : " + x.includes(1) + "\n")
console.log("2 in x        : " + (2 in x))
console.log("x.includes(2) : " + x.includes(2) + "\n")
console.log("3 in x        : " + (3 in x))
console.log("x.includes(3) : " + x.includes(3) + "\n")
console.log("4 in x        : " + (4 in x))
console.log("x.includes(4) : " + x.includes(4) + "\n")
console.log("0 in x        : " + (0 in x))
console.log("x.includes(0) : " + x.includes(0) + "\n")

Run that in your favorite REPL and you’ll get

x: 1,2,3
1 in x        : true
x.includes(1) : true
2 in x        : true
x.includes(2) : true
3 in x        : false
x.includes(3) : true
4 in x        : false
x.includes(4) : false
0 in x        : true
x.includes(0) : false

At first glance the results for 1, 2, and 4 make complete sense, while the results for 0 and 3 seem wrong. Technically they’re not. Because the in operator operates on the keys of an object and includes() looks at the values. And JS has decided that the indexes into an array are it’s keys. So as I said, it’s technically correct, but WAT?

Code is read way more often than it’s written, and you never want to confuse the reader. Clear, unambiguous names are important. includes() is pretty clear and unambiguous. Are any of the elements of the array you’re checking? No problems there. It’s the in operator that makes me go WAT?

First of all, it operates on the keys of a map, not the values. Standard usage of English says that if you ask if an element is in a collection you’re talking about values, not keys, so the operator should be keys or something like that. That would go a long way to avoiding the WAT. The other problem is that the in operator treats arrays as maps, with the indices as keys. WAT? Sure, if you dig down into the implementation you can see why/how that works, but no. It’s not right. It’s not an emergent feature that adds customer value. It’s a footgun just waiting to go off.