I follow a number of weblogs; one of them is dy/dan, written by Dan Mayer. He taught high school for a number of years, and has headed to Stanford for a PhD in (I think) education.
He pointed to this bit of Sal Kahn's TED talk:
Imagine learning to ride a bicycle, and maybe I give you a lecture ahead of time, and I give you that bicycle for two weeks. And then I come back after two weeks, and I say, "Well let's see. You're having trouble taking left turns. You can't quite stop. You're an 80 percent bicyclist." So I put a big C stamp on your forehead and then I say, "Here's a unicycle." But as ridiculous as that sounds, that's exactly what's happening in our classrooms right now.
Dan came up with this graphic in response:

In other words, Dan is saying that "sometimes, the best way to learn to ride a bike is to ride a bike."
From Bikes to Design Recipes
We have been learning Scheme by getting on the bike. It is different from most learning you are accustomed to in the classroom. At least, I hope that I'm doing a good job of keeping myself out of the role of "lecturer." I don't want to be in a position where I am telling you how to ride a bike–instead, I want you trying to ride, skinning your knees, getting up, and trying again.
At the start of class on Thursday, I'll briefly share my own experience in learning the Scheme programming language. I think it captures a number of the challenges you articulated in class the other day–how do I loop, debug, store values, and so on. In short, I didn't love the language from the start, and my experience learning it changed the way I think about computer science and the development of software.
In terms of our metaphor of bike riding, I ultimately had to get a new bike–my initial experience learning Scheme was so bad, the bike was unusable by myself or anyone else when I was done with it. But, in part this is because it forced me to think about the way I think, and to give up some bad habits and some long-standing assumptions about how one writes programs.
Specifically, I took away three big lessons:
1. A method to my madness. I entered computer science at the Masters level from an undergraduate degree in physics. I was a self-taught hacker, working primarily in C. I had no method. I knew nothing about data structures, I had never heard of objects... put simply, I had no idea what I was doing.
2. How to understand code. I had learned C from Petzold's Programming Windows (3rd ed.). I was not good at reading and understanding code. Scheme was the first new language I learned, and I had to work hard to look at it with fresh eyes—to learn to read the code carefully, and understand what the pieces meant. In the terminology of the class, I had to learn to overcome listception. In part, I did this by taking copious notes and doing a lot of work on paper, as it forced me to think about what each thing did, and why.
3. Seeing loops, types, and structure. Computation requires one infinite loop, and Scheme forced me to acknowledge that there is no one right way to loop. In BASIC, you did this with GOTO:
10 PRINT "HELLO WORLD"
20 GOTO 10
In Java, you do it with a while loop:
while (true) {
System.out.println("Hello, World\n");
}
In Scheme, a function call.
(define (loop)
(begin
(printf "Hello, World~n")
(loop)))
If your compiler is good, all three versions become the same efficient jump in assembly.
Scheme also forced me to think about types in a way that C did not, because I don't declare them in the same way. The Design Recipe did this: it taught me to identify the type of my arguments to functions, the type of expressions that are being evaluated, and so on. In fact, it made me think about the evaluation of expressions as opposed to the assignment of values to variables.
As a side-effect of thinking about loops and types, I was forced to think about how my code followed the structure of my data. If a data definition says that a structure is either A or B, then I will have a cond statement:
(cond
[(isA? item) ...]
[(isB? item) ...])
I don't need to think about this, because I have learned to structure my code in a way that follows from the definition of the data I am working on. Brooks said this in his seminal text The Mythical Man Month:
Much more often, strategic breakthrough will come from redoing the representation of the data or tables. This is where the heart of a program lies. Show me your flowcharts and conceal your tables, and I shall continue to be mystified. Show me your tables, and I won't usually need your flowcharts; they'll be obvious.
And, in truth, I should have started here: the recursive loop in processing a list does not come about because we "have to do recursion." It comes about because the definition of the data is self-referential. Therefore, it is obvious from the definition where the recursive call must be. "Obvious," of course, if you really understand your data.
