Site icon Jessitron

Abstractions in adulthood

In Surfaces & Essences[1], Hofstadter talks a lot about abstraction. Adults abstract more than children. For evidence, take the Tower of Hanoi game: move the stack to another peg, moving one disk at a time and never putting a bigger disk atop a smaller disk. It takes 7 moves for 3 disks.

When 8-yr-old children solve this puzzle, they often take many more moves, because they impose on themselves another restriction: move the disks one peg at a time. They never move the disk directly from the left peg to the right peg.

When thinking about journeys, adults abstract them to a state change. I was in St. Louis, now I’m in Kansas City. Kids don’t separate that from the trip. Did we stop at McDonald’s in Kingdom City or Cracker Barrel in Columbia? To a kid, there is no going to Kansas City without these details.

Data takes journeys through our code. We can concern ourselves with the details of getting from here to there — instantiate the new structure, populate it in a for-loop — or we can declare where we want to end up and let the internals handle the rest — calling filter or map on a sequence.

Some people find the details of the journey to be the interesting part of programming. Run a tiny bit faster, or in half the memory, or memoize certain bits. I’m glad these people exist, so they can write libraries and I can use the libraries and ignore all this most of the time.

Stating the end result, and not the means of getting there, is what Bret Victor calls “goal-based programming.” [2] I call it declarative style. This is a tenant of functional programming.

But there’s a catch! Sometimes the kids are right, and the journey is important.
Take that trip from STL to KC. If I abstract the trip to a simple location change, me.inKC(), am I missing something important?
Maybe. If I took the bus or the train, then I can take the same trip over and over, and it doesn’t make a difference to anybody. No matter how many times I take the bus to KC, I’m in KC. The process is idempotent: calling it once is the same as calling it over and over. But if I came by car, and got a speeding ticket, there’s a side effect! Do that four times and I’m going to jail in Boonville.
Or what if I implemented the journey as “drive 200 miles west”? Then the outcome would depend on my current location. me.inKC().inKC() would return me in Hays, KS. In this case, the journey is not referentially transparent. The output of the function depends on something other than its input, so the result isn’t the same every time it’s called. (yes, I’m stretching the metaphor in questionable ways. Be amused.)

Therefore, the journey — the “how” — can be implemented in a way that it matters, or in a way that it doesn’t. If an algorithm writes to the log or the database, such that how many times it’s called affects the world, then we have to care about it. If the code reads from the database or other global state, then it won’t do the same thing every time and we have to care about it.

We choose how to implement our journeys. We can keep them well-behaved: we can take public transit and specify the destination. That way we can leave the details behind the scenes; my travel agent can book a bus or a train, whatever’s cheaper that day, because all that matters is at the end of the journey I’m in KC. This leaves both me (the writer of code) and future-me (the reader of code) with more mental space to spend on why I’m in KC, and what to do once I’m there.

As @runarorama once said: “Write it functionally. Like an adult.”

—-
[1] Surfaces & Essences, Analogy as the Fuel and Fire of Thinking. I don’t recommend the book; it’s incredibly slow. Follow me on twitter instead.
[2] Bret Victor, The Future of Programming.

Exit mobile version