Of vocabulary and contracts

I was asked why one should prefer map over for.

Here’s an analogy: why do we, as a talking species, use different layers of vocabulary? Why do we have abstract terms in our language, as opposed to only concrete terms drawn from tangible things from our physical world? I mean, the Romans did fine without abstract language, right? Or were they limited by Latin? Wasn’t the incredible boom of philosophical thought during the Enlightenment facilitated by the abstraction powers of the German language?

Here’s a different frame: why do we have programming languages of a higher level than Assembly? What’s wrong with CMP AL,10; JNZ loc?

map and friends are more precise, sophisticated ways to talk about consistent patterns in data manipulation. Using them over for is analogous to using the word “cake” instead of “the kind of food that you make by whipping egg whites and maybe adding sugar”. Interestingly, you can eventually add new layers of category on top of established layers: just like saying that butter cakes constitute a specific family of cakes, one could say that pluck is a specialization of map.

for can still be useful, just like goto is not necessarily the devil (we still use switch, which is a goto in disguise). It’s just very low-level, and for most scenarios we have well-known higher-level terms. Just as we are continuously taught vocabulary and expression over the course of our school years (and hopefully beyond that) to improve our communication skills (as speakers and as listeners), together we can and should aim to develop the fineness of our expression as programmers.

Caveat: yes, there is such a thing as too much abstract language, or a language too sophisticated, and it is important that speakers adjust their discourse to the expected reader. Just like in natural languages. But if our primary purpose in any given project is to develop quality products with quality codebases, we can raise the requirements in terms of language comprehension — within reason.

OK, but that’s all theory. If both for and map are used with an inlined callback, pragmatically speaking, how do they represent a different level of abstraction? To trained eyes, they pretty much look the same, and neither forces me to avoid side effects.

With map or filter you are intentionally limiting your power. You establish a contract, wherein you say:

  • you’re not going to mutate the collection;
  • (in the case of map) you’re going to return a collection with the same size and with data derived from the original collection’s individual items;
  • (in the case of filter) you’re going to return a subset of the original collection, preserving the items and the order.

These important statements are part of the abstraction. As for the side effects:

  • map and filter prevent you from yielding side effects by default, whereas the default with for is to mutate, unless you have an additional statement to create a new empty collection beforehand;
  • for inherently requires more noise to be added, as you need to set up the conditions of the loop and handle the iterating variable. Noise dilutes intent. Noise also makes it easier for mistakes to slip through. Anything from a misspelled array.lenght, to a rogue comma or an illogical condition — ultimately, those are technicalities that you didn’t actually need to care about in the first place, yet they lead to pesky bugs creeping in.

Stock photography: Annie Spratt

Author: Miguel Fonseca

Engineer at Automattic. Linguist, cyclist, Lindy Hopper, tree climber, and headbanger.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: