Thinking like a computer scientist

September 5th, 2003  |  Tags:

Dijkstra once asserted that teaching programming should be approached as teaching people how to think logically. (Dijkstra also said that programming was the hardest branch of applied mathematics — and that poor mathematicians should stick with pure math!) My (extremely) limited experience in computer science education has led me to believe that this is not the case in practice. I should step back and clarify: Here, I’m only speaking about introductory computer science education — by the time students are taking courses that demand them to design digital logic, distributed agreement protocols, or compiler passes, the ones who weren’t taught how to think logically early on have found another major.

Why don’t students learn how to think logically in introductory computer science classes (often, programming classes disguised as “CS 101″)? I’ve identified a few factors that I think contribute:

“Cancer of the semicolon” Syntactic sugar, like any unnecessarily complicated syntax, is a barrier to thinking about what problems you’re solving. These two are tied together:

  • Syntactic sugar, under the guise of making programs prettier to look at, disguises what the operational meaning of program text is, hiding method calls, costs, and other potential pitfalls. As usual, the most egregious examples come from C++, where assignment operators, allocators and deallocators, and even the comma can be overloaded on a class-by-class basis. However, even Java has some faults in this arena, notably string concatenation. Admittedly, students in an introductory programming class aren’t likely to be writing or reading code that uses too much syntactic sugar — but when a particular granule becomes part of the idiom of one student (perhaps through more “advanced” friends or via the internet), she will share it with her friends, and it will corrupt their style as well.
  • Wrestling with syntax and compiler placation leads to frustration and confusion, especially when the necessary syntax is tied to language concepts that the student won’t learn about this semester, like Pascal’s var keyword and the distinction with call-by-reference and call-by-value, or like the string of nonsensical (to the beginning programmer) keywords one must spit out in order to write a simple procedural program in Java. Furthermore, time spent teaching syntax is wasted, when it could be spent teaching thinking. (Sadly, because some students have become used to C-like or Basic-like syntax before starting formal study, they will be confused by, say, the use of parentheses in a sparse-syntax language like Scheme. Even worse, they may regard programming as being about syntax — the driving force behind dastardly languages like COBOL and VB.)

Also, syntactic problems (almost necessarily) produce cryptic errors from the compiler or programming environment; as a result, it is best to have a language that makes the possibility of syntax errors slim.

OO and other “trendy” or SE-centric methodologies Object-oriented programming, despite its many intelligent and well-spoken detractors, is a fine methodology for developing (some) software in industry (well, in certain industries). (For the record, I agree with Paul Graham’s assertions that — to paraphrase slightly — OO is an acceptable way to get around the limitations of some language features and also makes approximations of more powerful features palatable and comprehensible to a wider audience.) However, I think OO is a bad way to teach programming, for at least a couple of reasons:

  • It’s unnecessarily hard to understand. I mean this both on the beginner level and on the specialist level. The beginner student is frustrated because it’s hard to think about subtyping and subclassing (blessedly, most courses will only deal with the latter) in a natural way, since many real-world examples they can think about (i.e. “vehicle”->”car”->”acura integra with superfluous Kanji decals“) break down pretty rapidly without a deep and possibly endless foray into Platonic metaphysics. (Alistair Cockburn deals with the problems of OO design pretty well in his coffee machine example.) The specialist is frustrated because the semantics and typing rules for OO languages are so hard to understand, and the fact that most languages are specified in an ad hoc manner doesn’t help. (I’m not just shooting from the hip here: look at some of Luca Cardelli’s papers if you don’t believe me.)
  • It’s pedagogically inefficient. Because a lot of the methods in objects are trivial (i.e. get/set pairs, constructors, etc.), students can write a lot of code that they don’t have to think about; it’s simply “busywork”.

However, the worst problem with OO is that it disguises what you’re telling the computer, to some extent. OO seems to only adequately prepare students for a world in which all data structures are autonomous entities that send each other messages. To be sure, there are worse models (read SICP’s characterization of Pascal), but it is my opinion that beginning programming languages should be taught in a language that is nearly operational (like assembly language, preferably something nice like MIPS) or in a language that is nearly mathematical, like the pure subset of Scheme or ML. These two alternatives allow students to reason about how a solution is expressed at a low level, or about the differences between different high-level expressions of a single solution. Once you’ve got a simple language (see my first complaint), you can easily add OO or other methodology-related semantics on top.

Identity politics I don’t know how many times I’ve helped students who, in begging me to hold their hand through every step of an assignment without their cooperation, claimed “I just don’t know how to think like a computer!” I was unaware of when we were all divided, Brave New World-style, into groups of people who could “think like a computer” and those who could not. However, it seems that this attitude is peculiar to computer science (now that I think about it, I recall hearing it in undergrad philosophy classes as well): who can imagine a literature student saying “I just don’t know how to think like a literary critic!”, or a biology student saying “I just don’t know how to think about cellular respiration!” Of course, the point of these courses is not to reward those who are born into a privileged category, or who have been initiated into some sort of Gnostic succession of secret knowledge — it is to teach people how to think in a certain way. Many of the students who’ve given me this excuse simply were not trying, and it’s easy to dismiss them as lazy. However, the ones who were trying and yet used this excuse made me somewhat sad — it was as if they had accepted this externally-imposed, self-fulfilling and self-defeating prophecy. In a small way, listening to these students reminded me of reading Mahler’s correspondence from the middle 1880s, in which it is clear that Mahler, firmly in his self-conception as the Aristotelian eiron, has begun to believe that the vicious, libelous attacks motivated by anti-Jewish sentiment and directed towards him in the Budapest and Prague presses are true (and essentially and necessarily so). If a student believes that she is congenitally incapable of performing a task, but that she must suffer on regardless, she will begin to see the task as a horrible, arbitrary chore — and the “congenital incapacity” that is preventing her from enjoying the work is a bulwark to be sidestepped, not an obstacle to remove.

Detachment from mathematical models of the function I said earlier that programming languages used for introductory classes should be either operational-level or mathematical-level. In either case, though, the mathematical properties of functions and programs should be stressed from the beginning. If students are required to write, say, data structure traversals in an operational-level language (whether C, Java, or assembly), they should be first required to present an inductive definition for the function first, and to demonstrate that their code corresponds to the inductive definition. I’ve seen that if the mathematical and logical aspect is not emphasized, students will just get confused by simple “existential quantifier” or structurally recursive problems.

I’m sure that some of these are old hat and have been better stated elsewhere, and I am probably almost entirely without authority in matters of CS pedagogy. However, something has to change.

Leave a Response