As reported in this New York Times obit, Arthur Ashe once said this about the whooping he put on my old favorite bad boy, Jimmy Connors, in the 1975 Wimbledon championship:
"If you're a good player," he said softly, "and you find yourself winning easily, you're not surprised."
I've never been a good enough tennis player to have his feeling on court. Likewise with running, where I am usually competing more with my own expectations than with other runners. In that self-competition, though, I have occasionally had the sort of performance that tops my realistic expectations but doesn't really surprise me. Preparation makes success possible.
In the non-competitive world of programming, I sometimes encounter this feeling. I dive into a new language, tool, or framework, expecting slow and unsteady progress toward competence or mastery. But then I seem to catch a wave and cruise forward, deeper and more confident than I had right to expect. In those moments, it's good to step back and remember: we are never entitled to feel that way, but our past work has made them possible.
When those moments come, they are oh, so sweet. They make even more palatable the tough work we do daily, moving one shovel of dirt from here to there.
That's the correct question for the clue, "The languages used to write Watson", courtesy of the IBM Watson team answering questions in the usual way on Reddit. I'm looking forward to learning more about how Watson operates at the software level, especially how it handles the uncertainty of multiple knowledge sources working in parallel on imprecise data.
I meet with prospective students and their parents frequently as part of on- and off-campus recruiting events. Last week and this are Up Close Days on campus, when students who have been admitted to the university but not yet accepted the offer visit in hopes of making their decision. I usually try to use part of our time together to help them see what computer science is and what graduates of our program can do, because so few of either the students or parents have any idea. With the match on my mind for teaching and research, it occurred to me that the timing of the match offered a perfect opportunity.
Here was a story that everyone had heard about, the kind that captures the attention of even the least technically minded among us: a battle of man versus machine in an arena that has historical been ours alone. Watson is not only a triumph of computer science; it covers the full spectrum of what we do, from racks of servers and an open-source operating system, through algorithms and data structures and programs that embody them, to the dreams of artificial intelligence and the promise of applications that help people all over the world. So I built a short presentation around the match, showing short clips from the show, offering commentary at key moments, taking wide-eyed questions at any time, and tying what the program was doing to what computer science is and what computer science graduates do.
Last week's first session went very well. If nothing else, all of our visitors could see my excitement at what we do and what remains to be done. Sharing the thrill, indeed!
Yesterday, Lance Fortnow tweeted:
Asked: What is Computer Science? Answered: Everything that happens after you ask a question to Google until you get a result.
I don't know what all the future implications of Watson's performance will be for AI, CS, or the world. I do know that, for now, the match has given us a compelling way to talk to others about computer science ripped right from the headlines and our TV screens. When we can connect our story to something like Google, we have a chance of helping others to get what we do. When the story connects to something that has already excited our audience, our job is perhaps a little bit easier.
Over the weekend, I enjoyed re-reading Brian Foote's The Craftsmen vs. the Scavengers, which is subtitled "The Ruminations of a Foot Soldier on the Reuse Revolution". Foote expressed an idea that has been visited and re-visited every so often in recent years:
In a world dominated by code reuse, all programming, will, in one sense, be maintenance programming.
Foote was a student of Ralph Johnson, who has written and spoken occasionally about the idea that software development is program transformation. I blogged about Ralph's idea and what it meant for me nearly five years ago, just before teaching an intro CS course that emphasized the modification and extension of existing code.
Some people worry that if we don't start students off with writing their own code from scratch they won't really learn to program. Most of the students in that CS1 course have turned out to be pretty good programmers; that's just anecdote, of course, and not evidence that the approach is write or wrong. But at least I don't seem to have done them irreparable harm.
This idea is comfortable to me as an old Smalltalk programmer. As Foote elaborates, the Smalltalk toolset supports this style of programming and, more importantly, the Smalltalk culture encouraged code reuse, sharing, and a sense of collective code ownership. We all felt we were in the same boat -- the same image -- together.
The commingling of Foote's assertion and my recollection of that CS1 course caused my mind to wander down another path. What about those times when we do start with a blank slate on a new project? If we approach the task as programming from scratch, we might well design a complete solution and try to implement it as a single piece. When I do maintenance programming, I don't usually think that way, even when I have a major change to make to the program. I'm usually too scared to change too many things at once! Instead, I make a series of small changes to the code, coaxing and pruning the system toward the goal state. This is, I think, just what XP and TDD encourage us to do even when we code on a blank slate. It's an effective way to think about writing new programs.
The very next section of Foote's paper also caused echoes in my mind. It suggest that, in a culture of reuse, bad design might drive out good design simply by being there first and attracting an audience. Good design takes a while to evolve, and by the time it matures a mediocre library or framework might already control the niche. This may have happened in some parts of the Smalltalk world, but I was lucky not to encounter it very often. Foote's idea comes across as another form Gresham's Law for software design, so long as we are willing to mangle the original sense of that term. The effect is similar: the world ends up trafficking in a substandard currency.
It sobers me to note that Foote wrote this paper in the summer of 1989. These ideas aren't new and have been on the software world's mind since at least the shift to OOP began twenty-five years ago or so. There truly is nothing new for me to say. As a community, though, we revisit these ideas from different vantage points as we evolve. Perhaps we can come away with new understanding as a result.
As a blogger who sometimes worries that I am a person you've never heard of, "writing uninterestingly about the unexceptional", and that other people have already written about whatever I have to say, only better, I really enjoyed William Zinsser's recent takedown of a New York Times Book Review editor on the subject of writing memoirs. His paragraph that starts
Sorry to be so harsh, but I don't like people telling other people they shouldn't write about their life.
The Times can use its space more helpfully than by allowing a critic to hyperventilate on an exhausted subject. We don't have that many trees left.
is one of my favorite paragraphs in recent memory. I admit a juvenile fondness for hoisting someone with his own petard, and Zinsser does so masterfully.
One of the great beauties of the web is that anyone can write and publish. Readers choose to give their attention to the work that matters to them. No trees are harmed along the way.
In reading and writing together, we can all become better writers -- and better thinkers.
I recommended Zinsser's On Writing Well, among several other books, in an earlier entry on reading to write. It remains at the top of my suggested reading list.
My world this week has been agog with the Watson match on Jeopardy!. The victory by a computer over the game's two greatest champions may well signal a new era, in much the same way as the rise of the web and the advent of search. I'd like to collect my thoughts before writing anything detailed about the match itself.
In anticipation of match, last week my Intelligent Systems students and I began to talk about some of the techniques that were likely being used by Watson under the hood. When I realized how little they had learned in their AI course about reasoning in the face of uncertainty, I dusted off an old lecture from my AI course, circa 2001. With a few tweaks and additions, it held up well. Among its topics were probability and Bayes' Law. In many ways, this material is more timely today than it was then.
Early in the week, as I began to think about what this match would mean for computing and for the world, I was reminded by Peter Norvig's The Machine Age that, in so many ways, the Jeopardy! extravaganza heralds a change already in progress. If you haven't read it yet, you should.
The shift from classical AI to the data-driven AI that underlies the advances Norvig lists happened while I was in graduate school. I saw glimpses of it at conferences on expert systems in finance and accounting, where the idea of mining reams of data seemed to promise new avenues for decision makers in business. The data might be audited financial statements of public corporations or, more tantalizing, collected from grocery store scanners. But I was embedded pretty deeply in a particular way of thinking about AI, and I missed the paradigm shift.
What is most ironic for me is that my own work, which involved writing programs that could construct legal arguments using both functional knowledge of a domain and case law, was most limited by the problem that we see addressed in systems like Google and Watson: the ability to work with the staggering volume of text that makes up our case law. Today's statistical techniques for processing language make extending my work and seeing how well it works in large domains possible in a way I could only dream of. And I never even dreamed of doing it myself, such was my interest in classical AI. (I just needed a programmer, right?)
There is no question that data-driven, statistical AI has proven to be our most successful way to build intelligent systems, particularly those of a certain scale. As an engineering approach, it has won. It may well turn out to be the best scientific approach to understanding intelligence as well, but... For an old classicist like me, there is something missing. Google is more idiot savant than bon vivant; more Rain Man than Man on the Street.
There was a telling scene in one of the many short documentary films about Watson in which the lead scientist on the project said something like, "People ask me if I know why Watson gave the answer it did. I don't how it got the answer right. I don't know how it got the answer wrong." His point was that Watson is so complex and so data-rich it can surprise us with its answers. This is true, of course, and an important revelation to many people who think that, because a computer can only do what we program it to do, it can never surprise or create.
But immediately I was thinking about another sense of that response. When Watson asks "What is Toronto? in a Final Jeopardy! on "U.S. Cities", I want to ask it, "What in the world were you thinking?" If it were a human player, it might be able to tell about its reasoning process. I'd be able to learn from what it did right or wrong. But if I ask most computer programs based on statistical computations over large data sets "Why?" I can't get much more than the ranked lists of candidates we saw at the bottom of the screen on Jeopardy!
There is still a romantic part of me that wants to understand what it means to think and reason at a conscious level. Perhaps my introspection misleads me with explanations constructed post hoc, but it sure seems like I am able to think at a level above my neurons firing. That feeling is especially strong when I perform more complex tasks, such as writing a program or struggling to understand a new idea.
So, when it comes to the science of AI, I still hope for more. Maybe our statistical systems will become complex and data-rich enough that they will be able to explain their reasoning in a meaningful way. It's also possible that my desire is nothing more than a form of chauvinism for my own species, that the desire to sit around and talk about stuff, including how and why we think the way we do, is a quaint feature peculiar to humans. I don't know the answer to this question, but I can't shake the deep belief in an architecture of thought and intelligent behavior that accounts for metacognition.
In any case, it was fun to watch Watson put on such an impressive show!
If you are interested in such things, you might want to (re-)read Newell and Simon's 1975 Turing Award lecture, Computer Science as Empirical Inquiry: Symbols and Search. It's a classic of the golden era of AI.
It's relatively easy to make these diagrams yourself. grep the log of your VCS for lines that depict adds and modifications. Strip everything except the file names, sort it, run it through 'uniq -c', sort it again, and then plot the first column.
Ah, the Unix shell. A few years ago I taught a one-unit course on bash scripting, and I used problems like this as examples in class. Many students are surprised to learn just how much you can do with a short pipeline of Unix commands, operating on plain text data pulled from any source.
You can also do this sort of thing almost as easily in a more full-featured scripting language, such as Python or Ruby. That is one reason languages like them are so attractive to me for teaching programming in context.
Of course, using a powerful, fun language in CS1 creates a new set of problems for us. A while back, a CS educator on the SIGCSE mailing list pointed out one:
Starting in Python postpones the discovery that "CS is not for me".
After years of languages such as C++, Java, and Ada in CS1, which hastened the exit of many a potential CS major, it's ironic that our new problem might be students succeeding too long for their own good. When they do discover that CS isn't for them, they will be stuck with the ability to write scripts and analyze data.
With all due concern for not wasting students' time, this is a problem we in CS should willingly accept.
Several articles caught my eye this week which are worth commenting on, but at this point none has triggered a full entry of its own. Some of my favorite bloggers do what they call "tab sweeps", but I don't store cool articles in browser tabs. I cache URLs and short notes to myself. So I'll sweep up three of my notes as a single entry, related to programming.
Programmer as Craftsman
Seth Godin writes about:
... the craftsperson, someone who takes real care and produces work for the ages. Everyone else might be a hack, or a factory guy or a suit or a drone, but a craftsperson was someone we could respect.
There's a lot of talk in the software development world these days about craftsmanship. All the conversation and all the hand-waving boil down to this. A craftsman is the programmer we all respect and the programmer we all want to be.
Dan Meyer is an erstwhile K-12 math teacher who rails against the phony problems we give kids when we ask them to learn math. Textbooks do so in the name of "context". Meyer calls it "pseudocontext". He gives an example in his entry Connect These Two Dots, and then explains concisely what is wrong with pseudocontext:
Pseudocontext sends two signals to our students, both false:
- Math is only interesting in its applications to the world, and
- By the way, we don't have any of those.
Are we really surprised that students aren't motivated to practice and develop their craft on such nonsense? Then we do the same things to CS students in our programming courses...
... Are Everywhere These Days
Finally, Greg Wilson summarizes what he thinks "computational science" means in one of his Software Carpentry lessons. It mostly comes down to data and how we understand it:
It's all just data.
Data doesn't mean anything on its own -- it has to be interpreted.
Programming is about creating and composing abstractions.
The tool shapes the hand.
We drown in data now. We collect faster than we can understand it. There is room for more programmers, better programmers, across the disciplines and in CS.
We certainly shouldn't be making our students write Fahrenheit-to-Celsius converters or processing phony data files.
A useful educational service: examine a person's codebase. Devise a new feature request that would be hard, given existing code and skill...
... Keep repeating as the codebase and skill improve. Would accelerate a programmer's skill at dealing with normal unexpected change.
This could also be a great way to help each programmer develop competencies that are missing from his or her skill set. I like how this technique would create an individualized learning for each student. The cost, of course, is in the work needed by the instructor to study the codebases and devise the feature requests. With a common set of problems to work on, over time an instructor might be able to develop a checklist of (codebase characteristic, feature request) pairs that covered a lot of the instructional space. This idea definitely deserves some more thought!
Of course, we can sometimes analyze valuable features of a codebase with relatively simple programs. Last month, Michael Feathers blogged about measuring the closure of code, in which he showed how we can examine the Open/Closed Principle in a codebase by extracting and plotting the per-file commit frequencies of source files in a project's version control repository. Feathers discussed how developers could use this information intentionally to improve the quality of their code. I think this sort of analysis could be used to great effect in the classroom. Students could see the OCP graphically for a number of projects and, combined with their programming knowledge of the projects, begin to appreciate what the OCP means to a programmer.
A serendipitous side effect would be for students to experience CS as an empirical discipline. This would help us prepare developers more readily in sync with Feathers's use of analytical data in their practice and CS grads who understand the ways in which CS can and should be an empirical endeavor.
I actually blogged a bit about studying program repositories last semester, for the purpose of understanding how to design better programming languages. That work used program repositories for research purposes. What I like about Marick's and Feathers's recent ideas is that they bring to mind how studying a program repository can aid instruction, too. This didn't occur to me so much back when one of my grad students studied relationships among open-source software packages with automated analysis of a large codebase. I'm glad to have received a push in that direction now.
It is easy for me to get sucked into a mindset in which I equate myself with what I do. In Five Lies I No Longer Believe, Todd Henry writes:
I am not my work, and I am especially not defined by how my work is received. That is not an excuse for laziness; it's permission to engage fully and freely and to bless everyone I encounter without worrying about what they think of me. This is hard medicine to swallow in a culture that celebrates title and the little spaces we carve for ourselves in the marketplace. Not me, not any longer.
"I am what and whom and how I teach."
"I am the programs I create, the papers I write, the grants I receive."
"I am the head of the department."
It is dangerous to think this way when I fail in any one of these arenas. It undervalues who I am and what I can offer. It closes my eyes to other parts of my life.
It is also dangerous to think this way when I succeed. Even in success, this view diminishes me. And it creates an endless cycle of having to succeed again in order to be.
When we think we are what we do, we often constrain our actions based on what other people will think of us. That makes it hard to teach the hard truths, to make tough decisions, to lead people. It makes it hard to create things that matter.
Even if we tune out what other people think, we find that we are always judging ourselves. This is as restrictive and as counterproductive as worrying about other peoples' idea of us.
Having different roles in life and doing different kinds of things can help us avoid this trap. Activity, success, and failure in one arena are only a part of who we are. We have to be careful, though, not to equate ourselves with our the sum of our activities, successes, and failures in all these areas. Whatever that sum is, we are more.
A long discussion on the SIGCSE members listserv about math requirements for CS degrees has drifted, as most curricular discussions seem to do, to "What is computer science?" Somewhere along the way, someone said, "Computer Science *is* a science, by name, and should therefore be one by definition". Brian Harvey responded:
The first thing I tell my intro CS students is "Computer Science isn't a science, and it isn't about computers." (It should be called "information engineering.")
I think that this assertion is wrong, at least without a couple of "only"s thrown in, but it is a great way to start a conversation with students.
I've been seeing the dichotomy between CS as science and CS as system-building again this semester in my Intelligent course. The textbook my students used in their AI course last semester is, like nearly every undergrad AI text, primarily an introduction to the science of AI: a taxonomy of concepts, results of research that help to define and delimit the important ideas. It contains essentially no pragmatic results for building intelligent systems. Sure, students learn about state-space search, logic as a knowledge representation, planning, and learning, along with algorithms for the basic methods of the field. But they are not prepared for the fact that, when they try to implement search or logical inference for a given problem, they still have a huge amount of work to do, with little guidance from the text.
In class today, we discussed this gap in two contexts: the gap one sees between low-level programming and high-level programming languages, and the difference between general-purpose languages and domain-specific languages.
My students seemed to understand my point of view, but I am not sure they really grok it. That happens best after they gain experience writing code and feel the gap while making real systems run. This is one of the reasons I'm such a believer in projects, real problems, and writing code. We don't always understand ideas until we see them in a concrete.
I don't imagine that intro CS students have any of the experience they need to understand the subtleties academics debate about what computer science is or what computer scientists. We are almost surely better off asking them to do something that matters them, whether a small problem or a larger project. In these problems and projects, students can learn from us and from their own work what CS is and how computer scientists think.
Eventually, I hope that the students writing large-ish AI programs in my course this semester learn just how much more there is to writing an intelligent system than just implementing a general-purpose algorithm from their text. The teams that are using pre-existing packages as part of they system might even learn that integrating software systems is "more like performing a heart transplant than snapping together LEGO blocks". (Thanks to John Cook for that analogy.)