One of my children is in third grade. As part of a “back-to-school” night this year, I sat in a very small chair while a teacher explained to me the “Math Practices” identified as part of the new Common Core standards for math teaching.
Perhaps the small chair simply made me more receptive, taking me back to third grade myself, but as she ran down the list, I found myself thinking: “gosh, these are exactly the same skills that I want to impart to beginning programmers!”
Here’s the list of Math Practices, a.k.a. “Standards for Mathematical Practice”:
- Make sense of problems and persevere in solving them.
- Reason abstractly and quantitatively.
- Construct viable arguments and critique the reasoning of others.
- Model with Mathematics.
- Use appropriate tools strategically.
- Attend to precision.
- Look for and make use of structure.
- Look for and express regularity in repeated reasoning.
Holy Moley! Those are incredibly relevant in teaching programming. Furthermore, they sound like they were written by someone intimately familiar with the How To Design Programs or Bootstrap curricula. Indeed, in the remainder of my analysis, I’ll be referring specifically to the steps 1–4 of the design recipe proposed by HtDP (as, e.g., “step 2 of DR”).
Let’s take those apart, one by one:
Make sense of problems and persevere in solving them.
This is really two things, but they’re both incredibly important in programming. The first one puts the emphasis first on understanding the problem. Don’t charge ahead and try to solve the problem (write the program) before you have some understanding of the problem. This can be clearly expressed by writing a purpose statement (part of step 2 of DR), and by designing data (step 1 of DR).
The second part of this—perseverance—is incredibly important. It’s not directly a step in solving the problem, but it’s one of a family of meta-skills that largely determines whether a student succeeds in an introductory programming class (erm, citation needed).
Reason abstractly and quantitatively.
Bouncing back and forth between the abstract and the quantitative is one of the key skills in programming. Indeed, a program represents a mapping from problem to solution for a large set of problems; the transition from the concrete to the abstract is the raison d’etre of programming itself.
Want to multiply one pair of numbers? use a calculator. Want to multiply seventy million pairs of numbers? write a program.
However, humans work best—and learn best—when we’re thinking about concrete, quantitative problems. That’s why step 3 of the design recipe requires students to come up with concrete examples of problem inputs, and the corresponding results. Without these concrete examples, students quickly get lost in trying to tackle all possible inputs, without being able to focus on concrete, quantitative inputs.
Construct viable arguments and critique the reasoning of others.
One of the key skills that programmers require is that of understanding programs. Learning to program without learning to read others’ programs is like learning to talk without knowing how to listen. It’s true that students see small examples of programs in textbooks and in class, but it’s also vital for students to see other students programs; learning to understand these will help them to see what’s missing in their own programs 1 2 3 4.
Also, programmers frequently engage in the activity of debugging. Okay, extremely frequently. The process of debugging is fundamentally one of regarding one’s own program as a third party. It’s clear to the programmer what they meant the program to do, but debugging it requires them to look at the program as if it were written by someone else, doing their best to peel off the lens of intention, and see what it actually, does, not what they meant it to do. They must then construct viable arguments as to why the program performs as it does, and how to correct it.
Debugging is a truly vital programming skill that is often not well taught.
Model with Mathematics.
“Model with Mathematics” is, more or less, another name for programming.
It’s not clear whether this adds anything to the conversation, but it seems clear that there’s more or less one hundred percent overlap between this “practice” and that of programming.
Use appropriate tools strategically.
Writing a program consists entirely in applying various functions and language constructs to a set of inputs.
In the first few weeks, these tools consist almost entirely of basic mathematical and graphical functions: plus, times, rectangle, and the like.
Later, students can bring to bear their knowledge of the “tools” of program templates (step 4 in the design recipe) to organize programs that operate on more complex forms of data. This is still in the “hand-holding” phase of programming.
Still later, students will learn about more sophisticated programming “tools”—divide and conquer forms of generative recursion, standard iteration functions (map, filter, foldl), and optimization techniques such as memoization. These tools pop instantly to the mind of a seasoned programmer, just as a woodworker might immediately identify the rabbet plane that will create the desired shape without difficulty.
Attend to precision.
Precision arrives somewhat later for programmers than it does for students of math. In the first ten or twenty weeks of student programming, programs tend to be entirely right or catastrophically wrong, especially if they’re following the steps of the design recipe, and using data definitions supplied to them.
Later, though, precision takes on an increasing importance. Programming is largely algebraic—how to combine operators and language forms to build a program—and the “precision” that is most often missing is that of corner cases, and unexpected combinations of data. “Attending to precision” in these cases consists in developing test cases that carefully cover the space of possible inputs.
Look for and make use of structure.
Making use of structure is in some ways the fundamental job of a programmer. The programmer must—before even beginning to write the program—decide how to model the data of the problem as values in some programming language. This is step 1 of the design recipe, and for the first five or six weeks of programming, students can’t be expected to design their own data.
In the next five or six weeks, students develop the ability to choose simple structures to represent well-understood data.
Finally, students move on to tackling problems where there is no single best way to model the data. In these cases, the best model may depend on operational constraints, or data volume, or any number of other criteria. Indeed, the entire fields of databases and data science may be considered to be an expression of this practice.
Look for and express regularity in repeated reasoning.
Yeah, that’s abstraction. Important in programming.
Okay, so that’s it. I hope it’s clear at this point that
Teaching math is a lot like teaching programming.
For more, take a look at Felleisen & Krishnamurthi, “Why Computer Science Doesn’t Matter,” 5.
Kulkarni, Chinmay, Steven P. Dow, and Scott R. Klemmer. “Early and repeated exposure to examples improves creative work.” Design Thinking Research. Springer International Publishing, 2014. 49–62. http://link.springer.com/chapter/10.1007/978–3–319–01303–9_4 ↩
Politz, Joe Gibbs, Shriram Krishnamurthi, and Kathi Fisler. “In-flow peer-review of tests in test-first programming.” Proceedings of the tenth annual conference on International computing education research. ACM, 2014. http://dl.acm.org/citation.cfm?id=2632347 ↩
Hundhausen, Christopher D., Anukrati Agrawal, and Pawan Agarwal. “Talking about code: Integrating pedagogical code reviews into early computing courses.” ACM Transactions on Computing Education (TOCE) 13.3 (2013): 14. http://dl.acm.org/citation.cfm?id=2499951 ↩