I agree with the comments by Misha and JGWeissman, saying that the “fundamental law of software” given here doesn’t focus tightly on what’s especially interesting about programming. Still, I think I know what Morendil is saying is interseting here; and if it’s a misrepresentation of the post, it at least represents what I think makes programming interesting, and hard.
(I’m going to try to explain this without assuming any programming jargon. Catch me if I fail!)
The first lesson of programming is the rules of the game: the syntax and semantics of a programming language, and how to run such code. For our purposes, this is just a substrate. Now, as anyone who’s tried to teach an Intro to Programming class can tell you, this substrate is either hard to learn or hard to teach—I’m not really sure which. But that’s not the point.
The second lesson of programming, though, is this: In a program of even moderate size, there are too many details to keep in your head. When you’re writing line 20,001 of code in your program, it will be wrong—unless you can make strong assumptions about the behavior of the previous 20,000 lines.
Thus, you learn to write your code in small, modular pieces, and you try to summarize each module’s behavior clearly and concisely. Each module needs to be small enough that you can hold all the relevant details in your head—the meaning of each line, including the summaries of all the code that it refers to. [1]
This introduces a key map-territory distinction. The territory is the code that implements a module; the map is the short description of the module. This distinction is unavoidable when you write large programs. You simply can’t manipulate the entire territory in your head all at once, but you expect the code of any module to be frequently dependent on the behavior of several others.
In my experience, the toughest programming errors to fix arise as errors in some programmer’s map. Suppose I wrote a module two weeks ago, and now my mental model of that module is slightly wrong, and so I misuse the module in a subtle way. Depending on the subtlety of the misunderstanding, the bug might not even show up until I change the program further—which makes the bug hard to find, as it won’t seem causally linked to the last change I made.
Some specialized map-territory distinctions exist in any field of endeavor. But, a programmer must learn a new map, over new territory, for every new project. That is, the mapped territory changes, sometimes frequently. Thus, vital programming skills relevant to LW interests are:
Accurately mapping territory,
Accurately conveying one’s maps, and
Creating territory that can be accurately mapped.
What’s more, computers usually give you sharp feedback about whether or not the program works as expected. As such, programming can be a tight feedback loop. So, programming has the right sort of conditions in which to learn these skills.
[1] Ok, you really just need to keep track of some of that detail—especially if you maintain explicit, logical properties in your code. But you need to know exactly how much detail you can ignore; when you can safely ignore earlier detail, it’s due to something like micro-modularization. (I could go into greater detail on this, but it’s not the point.)
Actually even relatively simple programs can be hard to understand deeply and give rise to confusion, because they describe a complex computation; “complex” precisely in the sense that the computation’s detailed shape is hard to deduce from the program text. (Langton’s Ant being a good example, though without much practical relevance.)
For an interesting real-world example around which I had trouble bending my mind recently see mustache, a Ruby program which does template expansion. The challenge was to get this to handle indentation correctly. It’s written well enough, it even has a nice set of unit tests.
But it made me realize that “indentation” was very much a harder concept to define formally than to grasp intuitively, and that it cut across the grain of the program’s current design in interesting and non-obvious ways.
OK—that’s entirely fair. The map-territory distinction I describe above reinforces a somewhat different set of rationality skills of rationality.
I’ve developed a sense for when I understand an algorithm to the point of being able to program it, rather than understanding it sufficiently to explain it or expect it to work. Feeling like I know how to code an algorithm is just like feeling sure that a mathematical proof is correct. They have a specific sort of robust clarity that most thought lacks. I hadn’t really thought about it, but that must be a learned feeling. I expect it’s a valuable sense to have; if programming can teach it, then more power to learning programming.
I agree with the comments by Misha and JGWeissman, saying that the “fundamental law of software” given here doesn’t focus tightly on what’s especially interesting about programming. Still, I think I know what Morendil is saying is interseting here; and if it’s a misrepresentation of the post, it at least represents what I think makes programming interesting, and hard.
(I’m going to try to explain this without assuming any programming jargon. Catch me if I fail!)
The first lesson of programming is the rules of the game: the syntax and semantics of a programming language, and how to run such code. For our purposes, this is just a substrate. Now, as anyone who’s tried to teach an Intro to Programming class can tell you, this substrate is either hard to learn or hard to teach—I’m not really sure which. But that’s not the point.
The second lesson of programming, though, is this: In a program of even moderate size, there are too many details to keep in your head. When you’re writing line 20,001 of code in your program, it will be wrong—unless you can make strong assumptions about the behavior of the previous 20,000 lines.
Thus, you learn to write your code in small, modular pieces, and you try to summarize each module’s behavior clearly and concisely. Each module needs to be small enough that you can hold all the relevant details in your head—the meaning of each line, including the summaries of all the code that it refers to. [1]
This introduces a key map-territory distinction. The territory is the code that implements a module; the map is the short description of the module. This distinction is unavoidable when you write large programs. You simply can’t manipulate the entire territory in your head all at once, but you expect the code of any module to be frequently dependent on the behavior of several others.
In my experience, the toughest programming errors to fix arise as errors in some programmer’s map. Suppose I wrote a module two weeks ago, and now my mental model of that module is slightly wrong, and so I misuse the module in a subtle way. Depending on the subtlety of the misunderstanding, the bug might not even show up until I change the program further—which makes the bug hard to find, as it won’t seem causally linked to the last change I made.
Some specialized map-territory distinctions exist in any field of endeavor. But, a programmer must learn a new map, over new territory, for every new project. That is, the mapped territory changes, sometimes frequently. Thus, vital programming skills relevant to LW interests are:
Accurately mapping territory,
Accurately conveying one’s maps, and
Creating territory that can be accurately mapped.
What’s more, computers usually give you sharp feedback about whether or not the program works as expected. As such, programming can be a tight feedback loop. So, programming has the right sort of conditions in which to learn these skills.
[1] Ok, you really just need to keep track of some of that detail—especially if you maintain explicit, logical properties in your code. But you need to know exactly how much detail you can ignore; when you can safely ignore earlier detail, it’s due to something like micro-modularization. (I could go into greater detail on this, but it’s not the point.)
Actually even relatively simple programs can be hard to understand deeply and give rise to confusion, because they describe a complex computation; “complex” precisely in the sense that the computation’s detailed shape is hard to deduce from the program text. (Langton’s Ant being a good example, though without much practical relevance.)
For an interesting real-world example around which I had trouble bending my mind recently see mustache, a Ruby program which does template expansion. The challenge was to get this to handle indentation correctly. It’s written well enough, it even has a nice set of unit tests.
But it made me realize that “indentation” was very much a harder concept to define formally than to grasp intuitively, and that it cut across the grain of the program’s current design in interesting and non-obvious ways.
OK—that’s entirely fair. The map-territory distinction I describe above reinforces a somewhat different set of rationality skills of rationality.
I’ve developed a sense for when I understand an algorithm to the point of being able to program it, rather than understanding it sufficiently to explain it or expect it to work. Feeling like I know how to code an algorithm is just like feeling sure that a mathematical proof is correct. They have a specific sort of robust clarity that most thought lacks. I hadn’t really thought about it, but that must be a learned feeling. I expect it’s a valuable sense to have; if programming can teach it, then more power to learning programming.