This has been an unusual year in several ways. This month ends it fittingly, in an unusual way: my fewest ever postings to this blog in any calendar month since its inception. I have nor blogged much this month for a couple of reasons. The first is that I have tried to make my break time a break, and so have stayed away from my computer more than usual.
The second is less sedentary. My family bought a new house this month. We made an initial offer in October, worked through a lot of details and last-stage construction issues in November, and closed in early December. The last few weeks have been a combination of finishing fall term, tryine to rest a bit, and moving a car- or minivan-load at a time. Moving over the course of several weeks is how my wife and I planned to do it. Baby steps is an interesting way to move, as we grow into each space a bit at a time, with time to think before being buried in boxes labeled "downstairs bedroom". I am still enjoying it and seeing the advantages of it (not the least of which is time to throw out all of the stuff we don't want to move!), but I think it is starting to tire my family. They would like to be "moved". Come to think of it, so would I. It's about time to bring this iteration to a close.
Happy New Year to all.
My daughters received a new game from their mom for Christmas. It's called Apples to Apples. Each round, one of the players draws a card with an adjective on it. The rest of the players choose noun cards from their hands that match the adjective. The judge chooses one of the nouns as the best match, and the player who played it wins that round. The objective of the game is to win the most rounds.
I could tell you many things more about the game and how it's played in my family, but there is really only one thing to say:
I stink at this game.
If I am in a three-person game, I finish third. Four players? Fourth. You name the number of players, and I can tell you where I'll finish. Last.
It doesn't seem to matter with whom I play. Recently, I've been playing with my wife and daughters. Last night, my wife's brother joined us. My wife and I have played this game before, with friends from my office. The result is always the same.
My weakness may be heightened by the lobbying that can be part of the game. Players are allowed to try to sell their answers to the judge. I'm not a good salesman and besides don't really like to sell. But that doesn't account for my losing. If we play in silence, I lose.
It's not that I'm bad at all word games. I like many word games and generally do well playing them. If nothing else, I get better after I play a game for a while, by figuring out something about the strategy of the game and the players with whom I play. But in this game, the harder I try to play well, the worse I seem to do.
This must be how students feel in class sometimes. There is some consolation -- that I might become more empathetic as a result of feeling this way -- but, to be honest, it's just a bad feeling.
Early last week, I spent the last couple of days before Christmas wrapping up the grades on my Programming Languages course for fall semester. While grading the final exam, I seemed surprised by something on almost every problem. Here are a few that stand out:
cons and list
... are not the same. We spent some time early in the semester looking at how cons allocates a single new cell, and list allocates one cell per argument. Then we used them in a variety of ways throughout the rest of the course. After fifteen weeks programming in Scheme, how can so many people confuse them?
Overuse of accumulator variables
... is endemic to undergraduate students learning to program functionally. Two of the exam problems asked for straightforward procedures following the structural recursion pattern. These problems were about as simple examples of structural recursion as you can find: The largest value in a binary tree is the larger of
The zip of two lists is a list with a list of their cars consed into the zip of their cdrs. Many students used an accumulator variable to solve both problems. Some succeeded, with unnecessarily complex code, and some solutions buckled under the weight of the complexity.
Habits are hard to break. I have colleagues who tell me that OOP is easy. I look at their code and say, "yes, but...." The code isn't really OO; it just bears the trappings of classes and methods. Sequential, imperative programming habits run deep. An accumulator variable is often a crutch used by a sequential programmer to avoid writing a functional solution. I see that in my own code occasionally -- the accumulator is as often a code smell as a solution.
At a time when the world is looking to parallel computing in a multicore world, we need to find a way to change the habits programmers form, either by teaching functional programming better or by teaching functional programming sooner so that students form different habits.
Scheme procedure names
... are different than primitive procedure names in most other languages. They are bound to their values just like every other symbol. They can be re-bound, either locally or globally, using the same mechanisms used to bind values to any other names. This means that in this code:
(lambda (f g) (lambda (x) (+ (f x) (g x))))
the + symbol is a free variable, bound at the top level to the primitive addition operator. After we talked about this idea several times through the semester, I threw the students a bone on the final with a question that asked students to recognize + symbol as a free variable in a piece of code just like this one. The bone sailed past most of them.
Bound and free variables
... remain a tough topic for students to grasp, at least from my teaching. We spent several days in class talking about the idea of bound and free variables, and then writing code that could check a piece of code for bound and free variables. One of those sessions made a point of pointing out that occurs bound does not equal does not occur free, and that occurs free does not equal does not occur bound. For one thing, a variable could occur both bound and free in the same piece of code. For another, it might not occur at all! Yet when a final exam problem asked students to define an occurs-bound? procedure, several of them wrote the one-liner (not (occurs-free? x exp)). If only they knew how close they were... But they wrote that one-liner without understanding.
... is an idea that befuddles many of my students even after half a semester in which we work with the idea. Our Quiz 3 is tough for many of the students; it is often their lowest quiz grade of the course. In past semesters, though, students seemed to go home after being disappointed with their Quiz 3 score, hit the books, and come away with some understanding. This semester, several students came to the final exam with the same hole in their knowledge -- including students with two of the top three scores for the course. This makes me sad and disappoints me.
I can't do much to ensure that students will care enough to hit the books to overcome their disappointments, but I can change what I do. The next time I teach this course, I will probably have them start by working with for and while constructs in their own favorite languages. Maybe by stripping away the functional programming wrapping and the Scheme code we use to encounter these ideas, they will feel comfortable in a more familiar context and see the idea of syntactic abstraction to be really quite simple.
Am I romanticizing the good old days, when men were men and all students went home and learned it all? Maybe a little, but I had a way to ground my nostalgia. I went back and checked the grades students earned in recent offerings of this course, which had very much the same content and structure. The highest score this semester was higher than the top score in the two most recent offerings, by 2-4%. Overall, through, grades lower. In fact, after the top score, the entire group had shifter down a whole letter grade. I don't think the students of the recent past were that much smarter or better prepared than this group, but I do think they had different attitudes and expectations.
One piece of evidence for this conclusion was that this semester there were far more 0s in the final grid of grades. I even had a couple of 0s on quizzes, where students simply failed to show up. This is, of course, much worse than simply scoring lower, because one things students can control is whether they do their work and submit assignments. As I wrote recently, each person must control what he or she can control. I am not sure how best to get juniors and seniors in college to to adopt this mindset, but maybe I'll need to bring Twyla Tharp -- or Bobby Knight -- into my classroom.
As I type, my students are taking the final exam in my programming languages course. A couple might prefer to be reading my blog than taking an exam, but perhaps not. I always enjoyed final exams as a student. They marked the end of something and offered a challenge.
My students can also rest comfortable tonight in the notion that their duties for the course are behind them, yet I still face grading the behemoth I am foisting on them right now. Even still, I am already beginning to put this course behind me, thinking back to what we've learned and ahead to what comes next for a few us, the course on programming language translation.
In my mind, I keep coming back to a punch line I heard on TV the other night:
All I'm saying is, if you keep living and dying on whether or not a person changes, well... you're not gonna make it as a doctor, that's all.
This is Dr. Cox, the "sarcastic, bitter mentor" of the protagonist on the show Scrubs. I don't watch the show a lot, but I have seen this episode a few times, and it ends with a scene in which this line is the lone heartfelt moment among Cox's typical schtick. His protege is having a hard time accepting that one of his patients, who had sidestepped a cancer scare, lights up a cigarette as he leaves the hospital, fully intent on resuming a habit that may kill him. Cox wants the newbie to see that a doctor simply cannot take personally the behaviors of all his patients; all he can do is treat them and, as best he can, try to teach them how to be healthy. The rest is up to them.
Students are a lot like patients. They come to us for help -- not treatment, but presumably education. My role as "doctor" is, as best I can, to teach them how to think and act like a computer scientist or programmer. But not all students have the same commitment, or motivation, or resources as the rest. It's easy to get caught up our own desire to teach, excite, and communicate and forget that not every student will leave the room inspired or changed. If a teacher lives and dies in his own mind on whether or not every student in a class "gets it" or leaves the room changed, well, he is going to have a long and painful life in the classroom. Every course will seem a failure.
The news is not all that bleak, though. Some students leave the room inspired. That energy can carry me a long way. And most students leave a course changed, if only a little bit. For some, that may be all the farther it goes. But for others, that little change is a seed waiting for the right conditions to come along some time in the future. At that moment, it will bloom into something no one can predict.
I guess this week I'm feeling a bit like Uncle Bob, who has been writing a lot of code lately -- hurray! -- but is disappointed in his performance. Programmers think about what it is like to program in an ideal world, just as teachers idealize what will happen in the classroom. When they get into the trenches, though, they encounter their own weaknesses and frailty. I run into that as a programmer sometimes, and as a teacher, too. Like Uncle Bob, I can recite a litany of how badly I do what I do: continually fighting the demons that say, take it easy and just lecture; they'll get it; the constant allure of not grading an assignment thus not being able to give the prompt feedback I know that some students need; do what is expedient, not what's best; it will all work out in the end. But does it?
Uncle Bob closes with:
There is much I have yet to learn about writing software well. So, although after 56 years of life, and 43 years of programming, I have achieved a modicum of success, and even some glory, Chef Baglio is right. It is from that point that you really start to learn.
You start learning _here_, at this point, for whatever the current value of "this" is.
As I was thinking about my final exam last week, I started to wonder what terms and concepts would be most on my students' minds as they studied. So I created a wordle:
This makes for an odd summary of the semester, a flashlight onto my vocabulary, and an unusual piece of art.
Last month my wife and I had the good fortune to see a Broadway touring company perform the Tony Award-winning Movin' Out, a musical created by Twyla Tharp from the music of Billy Joel. I've already mentioned that I am a big fan of Billy Joel, so the chance to listen to his songs for two hours was an easy sell. Some of you may recall that I also wrote an entry way back called Start with a Box that was inspired by a wonderful chapter from Twyla Tharp's The Creative Habit. So even if I knew nothing else about Tharp, Movin' Out would have piqued my interest.
This post isn't about the show, but my quick review is: Wow. The musicians were very good -- not imitating Joel, but performing his music in a way that felt authentic and alive. (Yes, I sang along, silently to myself. My wife said she saw my lips moving!) Tharp managed somehow to tell a compelling story by stitching together a set of unrelated songs written over the long course of Joel's career. I know all of these songs quite well, and occasionally found myself thinking, "But that's not what this song means...". Yet I didn't mind; I was hearing from within the story. And I loved the dance itself -- it was classical even when modern, not abstract like Merce Cunningham's Patterns in Space and Sound. My wife knows dance well, and she was impressed that the male dancers in this show were actually doing classical ballet. (In many performances, the men are more props than dancers, doing lifts and otherwise giving the female leads a foil for their moves.)
Now I see that Merlin Mann is gushing over Tharp and The Creative Habit. Whatever else I can say, Mann is a great source of links... He points us to a YouTube video of Tharp talking about "failing well", as well as the first chapter of her book available on line. Now you can read a bit to see if you want to bother with the whole book. I echo Mann's caveat: we both liked the first chapter, but we liked the rest of the book more.
Since my post three years ago on The Creative Habit, I've been meaning to return to some of the other cool ideas that Tharp writes about in this book. Seeing Movin' Out caused me to dig out my notes from that summer, and seeing Mann's posts has awakened my desire to write some of the posts I have in mind. The ideas I learned in this book relate well to how I write software, teach, and learn.
Here is a teaser that may connect with agile software developers and comfort students preparing for final exams:
The routine is as much a part of the creative process as the lightning bolt of inspiration, maybe more. And this routine is available to everyone.
Oddly, this quote brings to mind an analogy to sports. Basketball coaches often tell players not to rely on having a great shooting night in order to contribute to the team. Shooting is like inspiration; it comes and it goes, a gift of capricious gods. Defense, on the other hand, is always within the control of the player. It is grunt work, made up of effort, attention, and hustle. Every player can contribute on defense every night of the week.
For me, that's one of the key points in this message from Tharp: control what you can control. Build habits within which you work. Regular routine -- weekly, daily, even hourly -- are the scaffolding that keep you focused on making something. What's better, everyone can create and follow a routine.
While I sit and wait for the lightning bolt of inspiration to strike, I am not producing code, inspired or otherwise. Works of inspiration happen while I am working. Working as a matter of routine increases the chances that I will be producing something when the gods smile on me with inspiration. And if they don't... I will still be producing something.
This is the busy end to a busier-than-usual semester. As a result, my only opportunity and drive to blog come from echoes. Sometimes that's okay.
... or not. After six weeks or so of 26-28 miles a week -- not much by standards, but a slow and steady stream -- November hit me hard. Since 11/03 I've managed only 6-8 miles a week and not felt well the days after. My doctors are running out of possible diagnoses, which is good in one way but bad in another. In addition to the blog echo, I have an actual echo running through my head, from John Mellencamp's "Junior": Sometimes I feel better / But I never do feel well.
As we wrap up the semester's study of programming languages, my students took their final quiz today. I used the free time before the quiz to show them how we could imperative features -- an assignment operator and sequences of statements -- to a simple functional interpreter that they have been building over the course of the last few homework assignments. After writing a simple cell data type (10 lines of code) to support mutable data, we added 25 or so lines of code to their interpreter and modified 5 or so more. That's all it took.
I'm always amazed by what we can do in a few lines of code. Those few lines also managed to illustrate several of the ideas students encountered this semester: map, currying, and even a higher-order type predicate. Today's mini-demonstration has me psyched to add more features to the language, to make it more useful both as a language and as an example of how language works. If only we had more time...
After class, I was talking with a student about this and that related to class, internships, and programming. He commented that he now groks what The Pragmatic Programmer says about writing your own tools and writing programs to generate code for you. I smiled and thought, yep, that's what programmers do.
Today was one of the 40th anniversaries I mentioned six weeks ago: Douglas Engelbart's demonstration of a mouse-controlled, real time-interactive, networked computer. SFGate heralds this as the premiere of the PC, but this event has always seemed more about interaction than personal computing. Surely, the kind of interactivity that Engelbart showed off was a necessary precursor to the PC, but this demonstration was so much more -- it showed that people can interact with digital media and, yes, programs in a way that connects with human needs and wants. Engelbart's ideas will out-live what we know as the personal computer.
No matter, though. The demonstration inspired a generation. A friend of mine sent a note to all his friends today, asking us to "drink a toast to Douglas Engelbart" and reminiscing on what personal computing means to many of us:
Think how much this has changed our lives... The communication capabilities allow us to communicate extremely quickly, throughout the globe. The PC, and Internet, allow me to have friends in Australia, Belfast, Brazil, China, Holland, India, Japan, London, Switzerland, and many other places. ... Can you even picture a world without PC's? I've seen and used them in remote places like Nosy Be, Madagascar, and Siem Reap, Cambodia.
The world is a different place, and many of us -- my friend included -- contribute to that. That humbles and awes me.
At Google, a typical team might be 1 software architect, 5 software designers who are also responsible for development, testing, and production, and one product manager. All 7 would have CS BS degrees, and maybe 2 MS and 2 PhDs. Programming is a significant part of the job for everyone but the product manager, although s/he typically has programming experience in the past (school or job). Overall, programming is a very large part of the job for the majority of the engineering team.
Sure, Google is different. But at the end of the day, it's not that different. The financial services companies that hire many of my university's graduates are producing business value through information technology. Maximizing value through computing is even more important in this climate of economic uncertainty. Engineering and scientific firms hire our students, too, where they work with other CS grads and with scientists and engineers of all sorts. Programming matters there, and many of the programmers are scientists. The code that scientists produce is so important to these organizations that people such as Greg Wilson would like to see us focus more on helping scientists build better software than on high-performance computing.
Those who can turn ideas into code are the masters of this new world. Such mastery can begin with meager steps, such as adding a few lines of code to an interpreter make imperative programming come alive. It continues when a programmer looks at the result and says, "I wonder what would happen if..."
An acquaintance of mine sent a link to James Shore's The Decline and Fall of Agile to a mailing list with a warning: "You probably don't want to read it if you're a follower of the religion." His use of the pejorative reveals that he's not a fan. He is a strong believer in design -- if not BDUF, at least more than he perceives agile methods to encourage. I read Shore's articles anyway, so I was happy to now.
I don't think of myself as religious on this matter, but I often speak positively about agile approaches (I might say "fairly") when a colleague expresses what I think os a misconception, so here goes...
Shore is right. He also echoes something that comes up every so often when I am discussing agile approaches with folks who have had a bad experience: When a company does something it calls "agile" but which is unsound (perhaps because they leave something important out), things usually do not go well. But that's because they've done something unsound, not because they call what they did "agile".
I really try not to be a knee-jerk apologist for agile methods. They don't solve every problem in the world; nothing does. So when someone tells me that agile has failed them, I try to listen carefully to what they've been doing. Otherwise, it's too easy to say, "Oh, you did it wrong." That's the sort of behavior that leads people to talk about religion. And it's intellectually sloppy, which bothers me more.
But sometimes people do have a misconception, or do leave out an essential practice, or do something else that counters the desired benefit. When someone tells me,
[Extreme Programming] says you should spend no more than 10 minutes on design during a three-week sprint. 10 minutes!
... I have to say something. Maybe some agile evangelists have said this; maybe not. But it's wrong, and someone has to say so.
For what it's worth, my understanding of XP goes back to the original spirit: If doing something is valuable, then why not do it all the time? As a result, I encourage the teams I work with at the university and in industry to think about design all the time. But I also encourage them not to design too far ahead of their code base, because that is too often untested thought. Refactoring, a practice that some design-oriented people like to deride as "not doing it right the first time", is a key part of the design process. (Teams that don't refactor all the time are usually in big trouble -- and then unhappy with the other agile practices they have adopted.)
As with any idea that becomes more buzzword than idea, there is always a huge risk that the energy of the "movement" will flame out early when things don't go as predicted by eager creators and early adopters. We see the same thing happen in educational settings all the time. Many of you have lived through objects-first in the CS curriculum.
Software patterns encountered the same fate. Hype outran practice. When this happens, it is usually best to let the fad die out. The good news is that some people will persevere with the good part of the idea and do wonderful things. The most recent patterns example I know of is the resurgence of interest in patterns in the parallel programming community, which faces many of the same challenges that faced a world moving to object-oriented programming faced fifteen years ago. The Parallel Computing Laboratory at Cal-Berkeley is undertaking an ambitious pattern language project in this area.
What frustrates me the most is that this situation is entirely avoidable. In a green-field environment, the solid agile engineering practices included in Extreme Programming pay for themselves within the first few months.
When this passage leads the person with whom you are speaking to say,
I didn't know anyone used XP anymore. I feel sorry for them.
... you'll know it's time to stop talking. The religion that is getting in the way of developing software better may not be on the agile side of the conversation.
While reading on-line this summer, I ran across a description of how keyless remote entry systems work. I can't find that page just now, but here is a nice description. I never knew how those things worked. Very cool indeed! As I was reading, the programmer in me soon was thinking, "I'd like to write that code". The teacher in me almost as quickly thought, "What a great example for for my programming languages course...": programs with state and shared data. A homework problem was waiting to be written!
This week we got to the point in the course at which this is a perfect homework problem. I started thinking about it, but the week was busy... Finally, the night before I was to post the assignment, I sat down to write my solution. Within an hour I have less than a page of code that implements:
While thinking before programming -- even agile programmers are allowed to do that -- I realized that "re-programming" a desynchronized transmitter/receiver pair would be beyond scope of my homework assignment and that multiple behaviors (say, unlock and lock, turning on an alarm, etc.) added complexity to the code but no interesting ideas. I also realized that there was no shared data in this problem, but two pieces of state held in common: identical random number generators and a key code.
As I programmed, it slowly dawned on me that this problem, in its full form, was surely beyond the scope of my homework assignment. I had a couple of other tasks for them to do, and the keyless entry problem would require both a whole assignment to itself and fairly detailed guidance for the students on how to implement a solution. The interaction between the transmitter and the receiver means that the solution code has to be developed and tested in parallel, and there turned out more to be more layers of indirection in the solution than I had expected: a lambda wrapping a letrec wrapping a let wrapping a letrec wrapping a lambda wrapping one final lambda!
That's more complexity than I care for my students to encounter in the context of this assignment. With more time and more of the assignment "real estate", I could perhaps leave design a solution to students and then guide them during the process. But this is a programming languages course, and I'm trying to keep the focus of the course on features of languages, not on the application of functional programming or Scheme.
(I do love to have students see functional programming and Scheme applied to "real problems", because so often they view the programming language applications as, well, not real. This problem is especially cool for that purpose, because the ability to create a simple closure over two simple functions is so useful here.)
Sigh. The opportunist in me, though, thought, "No problem. Perhaps can will demo the solving of this problem in class."
And now I had a new problem: The only code I have is the final version of my program.
I can still build a demo, designing a session around how I grew this program, but I will have to re-grow it in my mind and in my lecture notes. Unfortunately, the second time through a solution is never quite like the first for me, and in a way that effects the quality of result. The second implementation usually feels and looks a little too pat, a little too straightforward, too obvious. Most people don't learn much about how to build something by looking only at the final product, and when the final product looks inevitable at every turn, all hope is lost. The process of writing the code, and the decisions made along the way matter -- the insights and the false starts; the intermediate steps.
All I have is my final version. If only I had developed this program under version control! At least then I'd have a sequence of intermediate solutions that could help me recover the sequence of decisions and insights and false starts.
I know some people use version control for more than developing software; Martin Fowler has even wrote about putting his whole file system under Subversion. I may not want to go that far, but what about the particular case of developing code for a new demo, lecture, or presentation?
This seems like one of those ideas someone has already had and profited from. I'm just now getting it. Do you ever do this for purposes of teaching or exposition?
Oh, well. I had a lot of fun implementing this code, and that is always a welcome joy. I may yet show it off in class, if only as a finished product. We programmers are often like artists and other creators, and even like parents: we are so proud of our children that we simply must show everyone and brag on them! But turning this solution into a powerful class session about using state and mutually-referential functions must wait until I have more time. Maybe next time I'll try building my new idea code under version control and be able to move more quickly.
Now that keyless remote entry is off the table as a homework problem, I am back to the drawing board for a couple of new problems to round out this assignment. Well, the random number generator is a nice stateful problem in its own right. And if I can just simplify the idea of the transmitter/receiver pair, maybe I can create a problem in the spirit of keyless entry systems -- only with security circa 1970 -- that salvages some of my initial excitement for this problem. Hmm....
Postscript. I wrote the first draft of this entry last night, right after whipping up my solution and despairing. Current students of mine know that I did salvage the idea, because I have already set the homework problems in question before them! The simplification was easier to make than I had feared. The transmitter and receiver use a fixed code and can never be synched. That made all the difference in the complexity of the solution. I left the other parts for extra-credit...