Coding Fast and Slow
Daniel Kahneman's classic Thinking Fast and Slow introduces the idea of two modes in the human brain:
- System 1: fast, pattern matching
- System 2: slow, rational decision making
In my previous post, Code Doesn’t Happen To You, I argued for why students of computer science should exercise System 2 while programming. Programs have deterministic behavior, and adopting an attitude that programs can be understood from first principles is invaluable.
Comments to Code Doesn’t Happen to You post largely agreed about the value of methodically understanding how programs work, but emphasized that in practice experimentation and guessing are required. While the behavior of programs are to a large degree deterministic, modeling large systems probabilistically is often required for efficiency’s sake. In teaching computer science, leveraging System 1 can also be powerful for quickly building student intuition and skills.
Infinite Regress and Abstractions
What is printf really? Taking a bottom up, System 2 approach to explaining this can be fatal. In order to explain how to show “Hello, World” in the console, we’re going to need to discuss a lot. Functions, linkers, output streams, interprocess communication, compilation, bytecode, rasterization and a large degree of electrical engineering will get us only part of the way there. (If you're curious, check out this deep dive from Adam Sawicki)
Clearly, we need to draw the line somewhere. Every time a student sees a print statement, they can’t spend 45 minutes worrying about what instruction set their operating system is using and the color system supported by their monitor. Meaningful abstractions like printf allow us to prune our use of System 2. While mysterious at first glance, determining the behavior of print statements quickly becomes rote and relegated to System 1.
Top Down and Bottom Up
An if statement in C consists of:
- A boolean expression (the condition)
- A block or statement to execute if the condition evaluates to true
- A (optional) block or statement to execute if the condition evaluates to false.
One way to teach if statements is to explain this definition, dive into the differences between expressions, statements, and blocks, and demonstrate the execution of if statements with a debugger. Alternatively, you can give students 20 if statements, have them predict their behavior, and have them copy paste their way through making a trivia game or something similar.
A student who really understands the difference between an expression and a statement is on a stronger academic footing, but if they never assimilate programming patterns that use conditional logic they won’t make it very far when it comes time to build something nontrivial. Conversely, a student who spent their first years programming copy-pasting sample code will at some point face bugs that they will have no ability to overcome (code will happen to them).
Coding Fast and Slow
A hybrid approach to programming is clearly required to make quick progress while retaining the ability to confidently deal with novel situations. Educators need to give students the skills to use both System 1 and System 2, but more critically, they need to teach them how to switch between them. This is at the core of Kahneman’s work as well: System 1 and System 2 are both necessary, but misapplication of either system can lead to serious errors.
I’ve told students that you can’t arbitrarily permute &’s and *’s until your code stops seg faulting, you can’t add and remove braces until your code compiles, and you can’t just guess that switching that += to -= will fix your code. In reality… you basically can. The critical point is determining when the System 1 party is over: when you need to roll up your sleeves, take a deep breath, return to your definitions, and get to work.