Copy
View this email in your browser
Arch-Engineer
Code better

On the nature of loops

Every time you reach for a loop in programming, you do so for one of a handful of purposes. As understanding a program is the art of reconstructing intention from code, I've taught my advanced students to cultivate the skill of rapidly recognizing the few varieties of loops.

For example, most languages' standard libraries these days feature some variant of the fold function. Taking the idea that a "fold" refers to "any kind of loop that can be written using the fold function" (i.e.: looping over a data structure, each iteration computing a result based on current node + previous result), those practiced with using the fold function can handily recognize this loop as a fold:

 
for (int i = 0; i < str.length(); i++) {
  validStr = validStr && Character.isAlphabetic(str.charAt(i));
}

A lesser-known family of loops is the unfold, a loop that produces a stream of values based on a small, ever-updating piece of state. An example is this line-drawing function (from an Easter egg in Redis):

    while(1) {
        lwDrawPixel(canvas,x1,y1,color);
        if (x1 == x2 && y1 == y2) break;
        e2 = err*2;
        if (e2 > -dy)
         ...
        // code to compute new err, x1, y1 elided
    }


Yet faster than reading a loop is having it never be written in the first place. And, according to Sean Parent's 2013 talk C++ Seasoning, this is a goal we should all be striving for.
Parent starts this talk with a slide of code from a previous revision of the Chromium OS. It's a loop of nested if-statements, and all it does is implement part of the functionality of rearranging panels. Just a handful of slides later, it's done to a few lines of straight-line code: more readable, more efficient, and more general.

Behind this is his aspiration to have "no raw loops," meaning, for every use of a loop in the middle of a larger function, one ought to attempt to replace it with generic functions. This example, specific as it may be, turns out to be a special case of a function in the C++ standard library.

I've spent some time in previous newsletters diminishing other presented refactorings, arguing that either they did not get to the root of the problem, or that one should cultivate a more general skill which makes the presented refactoring trivial. This time, I am impressed. And while the skill invoked here, of recognizing a particular family of loops, is moderately specific, he prescribes the more general aspiration of believing that every loopy function could be refactored in this manner, and the constant push to seek out such generalizations. I'm reminded of Jessica Kerr's old post The Silver Pill, where she argues that habitually searching for code abstractions trains one in an increasingly large toolset.  "No raw loops," then, is like a silver pill dispenser. 

Something particularly interesting to me was his domain of examples. I have a heavy functional programming background, so the families of generic loops that I am most familiar with, such as the fold and unfold examples above, tend to be pure functions, for which you have to squint a little to recognize their imperative instantiations (e.g.: printing every element of a list could be considered a map). However, Parent comes from the C++ world, which has built its own vocabulary of imperative generic functions. And so I was in the majority that did not recognize that the operation "take an arbitrary selection of icons and move them to the top" is an instance of the operation stable_partition.

The other two goals advocated by the talk, "no raw synchronization" and "no raw pointers," are less readily transferable outside of C++. (In fact, I confess, I lack the C++ knowledge to fully appreciate them in their original C++ context.) Yet on the strength of the first half alone, I recommend C++ Seasoning as the best software engineering talk I've seen in the last several years.

Some thoughts on the future of programming


Star programmer and algorithms coach Elliott Jin recently asked me to appear on his upcoming podcast. He's running a series on the future — future of money, future of learning, etc — and asked me to discuss the future of programming.

Some things we discuss:
  • Why game programming is a harbinger of the future of programming tools
  • How pressure on universities to teach "job skills" looked in the 60s,
  • How we know when we've succeeded at curing the ills of software maintenance
  • How to know whether a 100-year-old system is a success or failure.
While the podcast is not yet launched, Elliott has authorized me to release a sneak peak. You can listen to it on YouTube, or read the transcript here
Copyright © 2020 James Koppel Coaching, All rights reserved.