If “technology is stuff that doesn’t work yet”, then patterns are code we don’t know how to writeyet.
In the Go Programming Language , the authors show how to iterate over elements in a map, sorted bykeys:
To enumerate the key/value pairs in order, we must sort the keys explicitly, for instances, using the Strings function from the sort package if the keys are strings. This is a commonpattern.
―Go Programming Language, Alan A. A. Donovan & Brian W. Kernighan,p94
The pattern is illustrated by the followingcode:
import "sort" var names []string for name := range ages { name = append(names, name) } sort.Strings(names) for _, name := range names { fmt.Printf("%s\t%d\n", name, ages[name]) }Peter Norvig calls this an informal design pattern : something referred to by name (“iterate through items in a map in order of keys”) and re-implemented from scratch each time it’sneeded.
Informal patterns have their place but they are a larval form of knowledge, stuck halfway between intuition and formal understanding. When we see a recognize a pattern, our next step should always be to ask, “can we make it goaway?”
Patterns are one way of expressing “how to” knowledgebut we have another, better way: code. Source code is a formal expression of “how to” knowledge that we can execute, test, manipulate, verify, compose, and re-use. Encoding “how to” knowledge is largely what programming is . We talk about replacing people with programs precisely because we take the knowledge about how to do their job and encode it such that even a machine can understandit.
So how can we encode the knowledge of iterating through the items in a map in order of keys? How can we replace this pattern withcode?
We can start by following Peter Norvig’s example and reach for a dynamic programming language, such aspython:
names = [] for name in ages: names.append(name) names.sort() for name in names: print("{}\t{}".format(name, ages[name]))This is a very literal translation of the first snippet. A more idiomatic approach would looklike:
names = sorted(ages.keys()) for name in names: print("{}\t{}".format(name, ages[name])To turn this into a formal pattern, we need to extract a function that takes a map and returns a list of pairs of (key, value) in sorted order, likeso:
def sorted_items(d): result = [] sorted_keys = sorted(d.keys()) for k in sorted_keys: result.append((k, d[k])) return result for name, age in sorted_items(ages): print("{}\t{}".format(name, ages[name]))The pattern has become a function. Instead of a name or a description, it has an identifier , a True Name that gives us power over the thing. When we invoke it we don’t need to comment our code to indicate that we are using a pattern because the name sorted_items makes it clear. If we choose, we can test it, optimize it, or perhaps even prove itscorrectness.
If we figure out a better way of doing it, suchas:
def sorted_items(d): return [(k, d[k]) for k in sorted(d.keys())]Then we only have to change oneplace.
And if we are willing to tolerate a slight change inbehavior,
def sorted_items(d): return sorted(d.items())Then we might not need the function atall.
It was being able to write code like this that drew me towards Python and away from Java, way back in 2001. It wasn’t just that I could get more done in fewer lines―although that helped―it was that I could write what Imeant.
Of course, these days I’d much ratherwrite:
import Data.List (sort) import qualified Data.HashMap as Map sortedItems :: (Ord k, Ord v) => Map.Map k v -> [(k, v)] sortedItems d = sort (Map.toList d)But that’s anotherstory.