While reading this morning I came across a link to this essay. College students should read it, because it points out many of the common anti-patterns in the essays that we professors see -- even in papers written for computer science courses.
Of course, if you read this blog, you know that my writing is a poster child for linguistic diffidence, and pat expressions are part of my stock in trade. It's sad to know that these anti-patterns make up so much of my word count.
This web page also introduced me to Roberts's book Patterns in English. With that title, I must check it out. I needed a better reason to stop by the library than merely to return books I have finished. Now I have one.
I ran across a pattern today that reminded of another I encountered while at ChiliPLoP. Both help developers deal with side effects, changes to a program's state. Let me share these patterns with you all, with a common explanation of their value.
Most programs written these days are in a style where setting and changing the values of one or more variables, via a sequence of imperative statements. Side effects are a common source of programmers' misunderstanding when reading code. A program can change a variable's state almost anywhere, and that makes reading any bit of code uneasy: "Do I really know what this variable means right now?" Using only local variables only in the context of a small procedure is one way to localize effects. Even still, problems can arise.
I won't try write full patterns for these. I'll write short patlets and give you links to the articles I read, which do a good job talking about the ideas.
Mutual Recursion with Side Effects
In Solving Every Sudoku Puzzle, Peter Norvig builds a Python program for the title task. About 40% of the way in, he says:
If you have two mutually-recursive functions that both alter the state of an object, try to move almost all the functionality into just one of the functions. Otherwise you will probably end up duplicating code.
Duplicate code is usually a bad thing, because it creates a problem for programmers keeping the copies in sync. Duplicate code that modifies state is especially bad, because it brings in the extra complication of making another procedure harder to understand.
Side Effects and Python's Default Parameters
Coincidentally, the second pattern involves Python, too. This time the pattern is Python-specific, addressing an issue with a language feature.
Context: You are writing a procedure with a default parameter.
Problem: The procedure needs to modify the default parameter. Python evaluates a default parameter's initial value only on the first call to the procedure.
Solution: Whenever the parameter is missing, initialize it within the method.
Tim Ottinger gives his implementation technique in Python's Mutable Default Problem:
def function( item, stuff=None ): stuff = stuff or [] # ... modify stuff, etc.
This pattern allows the user to take advantage of the default parameter when he has no collection to send. It does so by following a principle laid out in the article Ottinger references: In Python, "don't use mutable objects as function defaults."
This pattern may not be Python-specific, because there may be other languages with this initialize-once behavior. It seems like a bug to me, not a feature, but I'm not a Python programmer and so can't speak authoritatively. But I am glad that Ruby doesn't do this.
~~~~~
Postscript: While preparing this article, I learned something new about Ruby, unrelated to this issue. It is possible to extract from a class a new class that has a subset of the original class's protocol. Very nice indeed!)
I have another million to my credit, and it was a marvelous little surprise.
Popular culture is full of all sorts of literary references with which you and I are supposed to be familiar. Every year brings another one or two. The Paradox of Choice. The Tipping Point. The Wisdom of Crowds. Well-read people are expected, well, to have read the books, too. How else can we expect to keep up with our friends when they discuss these books, or to use the central wisdoms they contain in knowing ways?
I have a confession. I have read only two or three chapters of The Wisdom of Crowds. I have read only an excerpt from The Tipping Point that appeared in the New Yorker or some other literary magazine. And while I've seen a Google talk by Barry Schwartz on-line, I may not have read anything more than a short precis of the work. Of course, I have learned a lot about them from my friends, and by reading about them in various other contexts. But, strictly speaking, I have not read any of them.
To be honest, I feel no shame about this state of affairs. There are so, so many books to read, and these just have not seemed important enough to displace others from my list. And in the case of The Wisdom of Crowds, I found that one or two chapters told me pretty much all I needed to understand the Big Idea it contained. Much as Seth Godin has said about many popular business books, many books in the popular canon can be boiled down to much shorter works in their essence, with the rest being there for elaboration or academic gravitas.
For airplane reading on my trip to the workshop at Google, I took Pierre Bayard's How to Talk About Books You Haven't Read. Bayard's thesis is that neither I nor anyone else should feel shame about not having read any given book, even if we feel a strong compulsion to comment, speak, or write about it. In not reading and talking anyway, we are part of a grand intellectual tradition and are, in fact, acting out of necessity. There are simply too many books to read.
This problem arises even in the most narrow technical situation. When I wrote my doctoral dissertation, I surely cited works with which I was familiar but which I had not "read", or, having read them, had only skimmed them for specific details. I recall feeling a little bit uneasy; what if some party of the book or dissertation that I had not studied deeply said something surprising or wrong? But I knew a lot about these works in context: from other people's analyses, from other works by the same author, and even from having discussed the work with the author him- or herself. But in an important way, I was talking about a work I "had not read".
How I could cite the work anyway and still feel I was being intellectually honest gets to one of the central themes of Bayard's book: the relationships between ideas are often more important than the ideas themselves. To understand a work in the context of the universal library means more than just to know the details of the work, and the details themselves are often so affected by conditions outside of the text that they are less reliable than the bigger picture anyway.
First, let me assure you. Bayard wrote this book with a wink in his eye. At times, he speaks with a cavalier sarcasm. He also repeats himself in places; occasional paragraphs sound as if they have been lifted verbatim from previous chapters.
Second, this book fits Seth Godin's analysis of popular business books pretty well. Two or three chapters were sufficient to express the basic idea of this book. But such a slim product would have missed something important. How to Talk About Books You Haven't Read started as a joke, perhaps over small talk at a cocktail party, but as Bayard expanded on the idea he ended up with an irreverent take on reading, thinking, and understanding that carries a lot more truth than I might first have imagined. Readers of this blog who are software patterns aficionados might think of Big Ball of Mud in order to understand just what I mean: antipattern as pattern, when looked at from a different angle.
This book covers a variety of books that deal in some way with not reading books but talking about them. Along the way, Bayard explores an even wider variety of ideas. Many of these sound silly, even wrong, at first, and he uses this to weave a lit-crit tale that is perfect parody. But as I read, I kept saying, "Yeah, but..." in a way, this really is true.
For example, Bayard posits that reading too much can cause someone to lose perspective in the world of ideas and to lose one's originality. In a certain way, the reader subordinates himself to the writer, and so reading too much means always subordinating to another rather than creating ideas oneself. We could read this as encouragement not to read (much), which would miss his joke. But there is another level at which he is dead-on right. I knew quite a few graduate students who learned this firsthand when they got into a master's program and found that they preferred to immerse themselves in the research of others than to do creative research of their own. And there many blogs which do a fine job reporting on other people's work but which never seem to offer much new. (I struggle with that danger each time I write in this blog.)
Not reading does not mean that we cannot have an opinion. My friends and I are examples of this. Students are notorious for this, and Bayard, a lit professor, discusses the case of students in class at some length. But I was most taken by his discussion of Laura Bohannan's experience telling the story of Hamlet to the Tiv people of West Africa. As she told the story, the Tiv elders interpreted the story for her, correcting her -- and Western culture, and Shakespeare -- along the way. One of the interpretations was a heterodoxy that has a small but significant following among Shakespeare scholars. The chief even ventured to predict how the story ended, and did a pretty good job. Bayard used this as evidence that not reading a book may actually leave our eyes open to new possibilities. Bohannan's story is available on-line, and you really should read it -- it is delightful.
Bayard talks about so many different angles on our relationship with books and stories about them, including
One chapter focuses on our encounters with writers, and the ticklish situations they create for the non-reader and for the writer. In another, Bayard deals with the relationship among professors, students, and books. It made me think about how students interpret the things I say in class, whether about our readings or the technical material we are learning. Both of these chapters figure in a second entry I'm writing about this book, as well as chapters on the works of Montaigne and Wilde.
One chapter uses as his evidence the campus novels of David Lodge, of whom I am a big fan. I've never blogged about them, but I did use the cover of one of his books to illustrate a blog entry. Yet another draws on Bill Murray's classic Groundhog Day, an odd twist in which actually reading books enters into Bayard's story and supports his thesis. I have recommended this film before and gladly do so again.
As in so many situations, our fear of appearing weak or unknowledgable is what prevents us from talking freely about a book we haven't read, or even to admit that we have not read it. But this same fear is also responsible for discouraging us from talking about books we have read and about ideas we have considered. This is ultimately the culprit that Bayard hopes to undermine:
But our anxiety in the face of the Other's knowledge is an obstacle to all genuine creativity about books. The idea that the Other has read everything, and thus is better informed than us, reduces creativity to a mere stopgap that non-readers might resort to in a pinch. In truth, readers and non-readers alike are caught up in an endless process of inventing books, whether we like it or not, and the real question is not how to escape that process, but how to increase its dynamism and its range.
Bayard's book is worth reading just for his excerpts of other books and for his pointer to the story about the Tiv. I should probably feel guilt at not having read this book yet when so many others have, but I'm just happy to have read it now.
While reading on the plane coming home, I glanced across the aisle and noticed another passenger reading Larry Niven's Ringworld. I smiled and thought "FB++", in Bayard's rating system. I could talk about it nonetheless.
In his recent bestseller The Black Swan: The Impact of the Highly Improbable, Nassim Nicholas Taleb uses the term narrative fallacy to describe man's penchant for creating a story after the fact, perhaps subconsciously, in order to explain why something happened -- to impute a cause for an event we did not expect. This fallacy derives from our habit of imposing patterns on data. Many view this as a weakness, but I think it is a strength as well. It is good when we use it to communicate ideas and to push us into backing up our stories with empirical investigation. It is bad when we let our stories become unexamined truth and when we use the stories to take actions that are not warranted or well-founded.
Of late, I've been thinking of the narrative fallacy in its broadest sense, telling ourselves stories that justify what we see or want to see. My entry on a response to the Onward! submission by my ChiliPLoP group was one trigger. Those of us who believe strongly that we could and perhaps should be doing something different in computer science education construct stories about what is wrong and what could be better; we're like anyone else. That one OOPSLA reviewer shed a critical light on our story, questioning its foundation. That is good! It forces us to re-examine our story, to consider to what extent it is narrative fallacy and to what extent it matches reality. In the best case, we now know more about how to tell the story better and what evidence might be useful in persuading others. In the worst, we may learn that our story is a crock. But that's a pretty good worst case, because it gets us back on the path to truth, if indeed we have fallen off.
A second trigger was finding a reference in Mark Guzdial's blog to a short piece on universal programming literacy at Ken Perlin's blog. "Universal programming literacy" is Perlin's term for something I've discussed here occasionally over the last year, the idea that all people might want or need to write computer programs. Perlin agrees but uses this article to consider whether it's a good idea to pursue the possibility that all children learn to program. It's wise to consider the soundness of your own ideas every once in a while. While Perlin may not be able to construct as challenging a counterargument as our OOPSLA reviewer did, he at least is able to begin exploring the truth of his axioms and the soundness of his own arguments. And the beauty of blogging is that readers can comment, which opens the door to other thinkers who might not be entirely sympathetic to the arguments. (I know...)
It is essential to expose our ideas to the light of scrutiny. It is perhaps even more important to expose the stories we construct subconsciously to explain the world around us, because they are most prone to being self-serving or simply convenient screens to protect our psyches. Once we have exposed the story, we must adopt a stance of skepticism and really listen to what we hear. This is the mindset of the scientist, but it can be hard to take on when our cherished beliefs are on the line.
I've tried to explain the idea of software patterns in a lot of different ways, to a lot of different kinds of people. Reading James Tauber's Grammar Rules reminds me of one of my favorites: a pattern language is a descriptive grammar. Patterns describe how (good) programmers "really speak" when they are working in the trenches.
Talking about patterns as grammar creates the potential for the sort of misunderstanding that Tauber discusses in his entry. Many people, including many linguists, think of grammar rules as, well, rules. I was taught to "follow the rules" in school and came to think of the rules as beyond human control. Linguists know that the rules of grammar are man-made, yet some still seem to view them as prescriptive:
It is as if these people are viewing rules of grammar like they would road rules--human inventions that one may disagree with, but which are still, in some sense, what is "correct"...
Software patterns are rarely prescriptive in this sense. They describe a construct that programmers use in a particular context to balance the forces at play in the problem. Over time, they have been found useful and so recur in similar contexts. But if a programmer decides not to use a pattern in a situation where it seems to apply, the programmer isn't "wrong" in any absolute sense. But he'll have to resolve the competing forces in some other way.
While the programmer isn't wrong, other programmers might look at him (or, more accurately, his program) funny. They will probably ask "why did you do it that way?", hoping to learn something knew, or at least confirm that the programmer has done something oddly.
This is similar to how human grammar works. If I say, "Me wrote this blog", you would be justified in looking at me funny. You'd probably think that what I speaking incorrectly.
Tauber points out that, while I might be violating the accepted rules of grammar, I'm not wrong in any absolute sense:
... most linguists focus on modeling the tacit intuitions native speakers have about their language, which are very often at odds with the "rules of grammar" learnt at school.
He gives a couple of examples of rules that we hear broken all of the time. For example, native speakers of English almost always say "It's me", not "It's I", though that violates the rules of nominative and accusative case. Are we all wrong? In Sr. Jeanne's 7th-grade English class, perhaps. But English grammar didn't fall from the heavens as incontrovertible rules; it was created by humans as a description of accepted forms of speech.
When a programmer chooses not to use a pattern, other programmers are justified in taking a second look at the program and asking "why?", but they can't really say he's guilty of anything more than doing things differently.
Like grammar rules, some patterns are more "right" than others, in the sense that it's less acceptable to break some than others. I can get away with "It's me", even in more formal settings, but I cannot get away with "Me wrote this blog", even in the most informal settings. An OO programmer might be able get away with not using the Chain of Responsibility pattern in a context where it applies, but not using Strategy or Composite in appropriate contexts just makes him look uninformed, or uneducated.
A few more thoughts:
So, patterns are not like a grammar for programming language, which is prescriptive. To speak Java at all, you have to follow the rules. They are like the grammar of a human language, which model observations about how people speak in the wild.
As a tool for teaching and learning, patterns are so useful precisely because they give us a way to learn accepted usages that go beyond the surface syntactic rules of a language. Even better, the pattern form emphasizes documenting when a construct works and why. Patterns are better than English grammar in this regard, at least better than the way English grammar is typically taught to us as schoolchildren.
There are certainly programmers, software engineers, and programming language theorists who want to tell us how to program, to define prescriptive rules. There can be value in this approach. We can often learn something from a model that has been designed based on theory and experience. But to me prescriptive models for programming are most useful when we don't feel like we have to follow them to the letter! I want to be able to learn something new and then figure out how I can use it to become a better programmer, not a programmer of the model's kind.
But there is also a huge, untapped resource in writing the descriptive grammar of how software is built in practice. It is awfully useful to know what real people do -- smart, creative people; programmers solving real problems under real constraints. We don't understand programming or software development well enough yet not to seek out the lessons learned by folks working in the trenches.
This brings to mind a colorful image, of software linguists venturing into the thick rain forest of a programming ecosystem, uncovering heretofore unexplored grammars and cultures. This may not seem as exotic as studying the Pirahã, but we never know when some remote programming tribe might upend our understanding of programming...
The Small Doses pattern I wrote up in my previous entry was triggered almost exclusively by the story I heard from Carl Page. The trigger lives on in the text that runs from "Often times, the value of Small Doses..." to the end, and in the paragraph beginning "There is value in distributing...". The story was light and humorous, just the sort of story that will stick with a person for twenty or more years.
As I finally wrote the pattern, it grew. That happens all the time when I write. It grew both in size and in seriousness. At first I resisted getting too serious, but increasingly I realized that the more serious kernel of truth needed telling. So I gave it a shot.
The result of this change in tone and scope means that the pattern you read is not yet ready for prime time. Rather than wait until it was ready, though, I decided to let the pattern be a self-illustration. I have put it out now, in its rough form. It is rough both in completeness and in quality. Perhaps my readers will help me improve. Perhaps I will have time and inspiration soon to tackle the next version.
In my fantasies, I have time to write more patterns in a Graduate Student pattern language (code name: Chrysalis), even a complete language, and cross-reference it with other pattern languages such as XP. Fantasies are what they are.
Update: Added a known use contributed by Joe Bergin. -- 05/19/08.
Also Known As Frequent Releases
From Pattern Language Graduate Student
You are writing a large document that one or more other people must read and provide feedback on before it is distributed externally.
The archetype is a master's thesis or doctoral dissertation, which must be approved by a faculty committee.
You want to give your reviewers the best document possible. You want them to give you feedback and feel good about approving the document for external distribution.
You want to publish high-quality work. You would also like to have your reviewers see only your best work, for a variety of reasons. First, you respect them and are thankful for their willingness to help you. Second, they often have the ability to help you further in the future, in the form of jobs or recommendations. Good work will impress your reviewers more than weaker work. A more complete and mature document is more likely to resemble the final result than a rougher, earlier version.
In order to produce a document of the highest quality, you need time -- time to research, write, and revise. This delays your opportunity to distribute it to your reviewers. It also delays their opportunity to give you feedback.
Your reviewers are busy. They have their own work to do and are often volunteering their time to serve you.
Big tasks require a lot of time. If a task takes a lot of time to do, some people face a psychological barrier to starting the task.
A big task decomposed into several smaller tasks may take as much time as the big task, or more, but each task takes a smaller time. It is often easier to find time in small chunks, which makes it easier to start on the task in the first place.
The sooner your reviewers are able to read parts of the document, the sooner they will be able to give you feedback. This feedback helps you to improve the document, both in the large (topic, organization) and the small (examples, voice, illustrations, and so on).
Therefore:
Distribute your document to reviewers periodically over a relatively drawn out period. Early versions can be complete parts of the document, or rough versions of the entire document.
In the archetypal thesis, you might give the reviewers one chapter per week, or you might give a whole thesis in which each part is in various stages of completeness.
There is certainly a trade-off between the quality of a document and the timeliness of delivery. Don't worry; this is just a draft. You are always free to improve and extend your work. Keep in mind that there is also a trade-off between the quality of a document and the amount of useful feedback you are able to incorporate.
There is value in distributing even a very rough or incomplete document at regular intervals. If reviewers read the relatively weak version and make suggestions, they will feel valuable. If they don't read it, they won't know or mind that you have made changes in later versions. Furthermore, they may feel wise for not having wasted their time on the earlier draft!
With the widespread availability of networks, we can give our reviewers real-time access to an evolving document in the form of an-line repository. In such a case, Small Doses may take the form of comments recorded as each change is committed to the repository. It is often better for your reviewers if you give them periodic descriptions of changes made to the document, so that they don't have to wade through minutiae and can focus on discrete meaningful jumps in the document.
I have seen Small Doses work effectively in a variety of academic contexts, from graduate students writing theses to instructors writing lecture notes and textbooks for students. I've seen it work for master's students and doctoral students, for text and code and web sites. Joe Bergin says that this pattern is an essential feature of the Doctor of Professional Studies program at Pace University. Joe has documented patterns for succeeding in the DPS program. (If you know of particularly salient examples of Small Doses, I'd love to hear them.)
Often times, the value of Small Doses is demonstrated best in the results of its applying its antipattern, Avalanche. Suppose you are nearing a landmark date, say, the deadline for graduation or the end of spring semester, when faculty will scatter for the summer. You dump a 50-, 70-, or 100+-page document on your thesis committee. In the busy time before the magic date, committee members have a difficulty finding time to read the whole document. Naturally, they put off reading it. Soon they fall way behind and have a hard time meeting the deadline -- and they blame you for the predicament, for unrealistic expectations. Of course, had you given the committee a chapter at a time for 5 or 6 weeks leading up to the magic date, some committee members would still have fallen behind reading it, because they are distracted with their own work. But then you could blame the them for not getting done!
Related Patterns Extreme Programming and other agile software methodologies encourage Short Iterations and Frequent Releases. Such frequent releases are the Small Doses of the software development world. They enable more continuous feedback and allow the programmers to improve the quality of the code based on what they learn. The Avalanche antipattern is the source of many woes in the world of software, in large part due to the lack of feedback they afford from users and clients.
I learned the Small Doses pattern from Carl Page, my Artificial Intelligence II professor at Michigan State University in 1987. Professor Page was a bright guy and very good computer scientist, with an often dark sense of humor. He mentored his students and advisees with sardonic stories like Small Doses. He was also father to another famous Page, Larry.
This week I ran across Jonathan Edwards's review of Gregor Kiczales's OOPSLA 2007 keynote address, "Context, Perspective and Programs" (which is available from the conference podcast page). Having recently commented on Peter Turchi's keynote, I figured this was a good time to go back and listen to all of Gregor's again. I first listened to part of it back when I posted it to the page, but I have to admit that I didn't understand it all that well then. So a second listen was warranted. This time I had access to his slides, which made a huge difference.
In his talk, Kiczales tries to bring together ideas from philosophy, language, and our collective experience writing software to tackle a problem that he has been working around his whole career: programs are abstractions, and any abstraction represents a particular point of view. Over time, the point of view changes, which means that the program is out of sync. Software folks have been thinking about ways to make programs capable of responding naturally as their contexts evolve. Biological systems have offered some inspiration in the last decade or so. Kiczales suggests that computer science's focus on formality gets in the way of us finding a good answer to our problem.
Some folks took this suggestion as meaning that we would surrender all formalism and take up models of social negotiation as our foundation. Roly Perera wrote a detailed and pointed review of this sort. While I think Perera does a nice job of placing Kiczales's issues in their philosophical context, I do not think Kiczales was saying that we should go from being formal to being informal. He was suggesting that we shouldn't have to go from being completely formal to being completely informal; there should be a middle ground.
Our way of thinking about formality is binary -- is that any surprise? -- but perhaps we can define a continuum between the two. If so, we could write our program at an equilibrium point for the particular context it is in and then, as the context shifts, allow the program to move along the continuum in response.
Now that I understand a little better what Kiczales is saying, his message resonates well with me. It sounds a lot like the way a pattern balances the forces that affect a system. As the forces change, a new structure may need to emerge to keep the code in balance. We programmers refactor our code in response to such changes. What would it be like for the system to recognize changes in context and evolve? That's how natural systems work.
As usual, Kiczales is thinking thoughts worth thinking.
While catching up on some work at the office yesterday -- a rare Saturday indeed -- I listened to Peter Turchi's OOPSLA 2007 keynote address, available from the conference podcast page. Turchi is a writer with whom conference chair Richard Gabriel studied while pursuing his MFA at Warren Wilson College. I would not put this talk in the same class as Robert Hass's OOPSLA 2005 keynote, but perhaps that has more to do with my listening to an audio recording of it and not being there in the moment. Still, I found it to be worth listening as Turchi encouraged us to "get lost" when we want to create. We usually think of getting lost as something that happens to us when we are trying to get somewhere else. That makes getting lost something we wish wouldn't happen at all. But when we get lost in a new land inside our minds, we discover something new that we could not have seen before, at least not in the same way.
As I listened, I heard three ideas that captured much of the essence of Turchi's keynote. First was that we should strive to avoid preconception. This can be tough to do, because ultimately it means that we must work without knowing what is good or bad! The notions of good and bad are themselves preconceptions. They are valuable to scientists and engineers as they polish up a solution, but they often are impediments to discovering or creating a solution in the first place.
Second was the warning that a failure to get lost is a failure of imagination. Often, when we work deeply in an area for a while, we sometimes feel as if we can't see anything new and creative because we know and understand the landscape so well. We have become "experts", which isn't always as dandy a status as it may seem. It limits what we see. In such times, we need to step off the easy path and exercise our imaginations in a new way. What must I do in order to see something new?
This leads to the third theme I pulled from Turchi's talk: getting lost takes work and preparation. When we get stuck, we have to work to imagine our way out of the rut. For the creative person, though, it's about more about getting out of a rut. The creative person needs to get lost in a new place all the time, in order to see something new. For many of us, getting lost may seem like as something that just happens, but the person who wants to be lost has to prepare to start.
Turchi mentioned Robert Louis Stevenson as someone with a particular appreciation for "the happy accident that planning can produce". But artists are not the only folks who benefit from these happy accidents or who should work to produce the conditions in which they can occur. Scientific research operates on a similar plane. I am reminded again of Robert Root-Bernstein's ideas for actively engaging the unexpected. Writers can't leave getting lost to chance, and neither can scientists.
Turchi comes from the world of writing, not the world of science. Do his ideas apply to the computer scientist's form of writing, programming? I think so. A couple of years ago, I described a structured form of getting lost called air-drop programming, which adventurous programmers use to learn a legacy code base. One can use the same idea to learn a new framework or API, or even to learn a new programming language. Cut all ties to the familiar, jump right in, and see what you learn!
What about teaching? Yes. A colleague stopped by my office late last week to describe a great day of class in which he had covered almost none of what he had planned. A student had asked a question whose answer led to another, and then another, and pretty soon the class was deep in a discussion that was as valuable, or more, than the planned activities. My colleague couldn't have planned this unexpectedly good discussion, but his and the class's work put them in a position where it could happen. Of course, unexpected exploration takes time... When will they cover all the material of the course? I suspect the students will be just fine as they make adjustments downstream this semester.
What about running? Well, of course. The topic of air-drop programming came up during a conversation about a general tourist pattern for learning a new town. Running in a new town is a great way to learn the lay of the land. Sometimes I have to work not to remember landmarks along the way, so that I can see new things on my way back to the hotel. As I wrote after a glorious morning run at ChiliPLoP three years ago, sometimes you run to get from Point A to Point B; sometimes, you should just run. That applies to your hometown, too. I once read about an elite women's runner who recommended being dropped off far from your usual running routes and working your way back home through unfamiliar streets and terrain. I've done something like this myself, though not often enough, and it is a great way to revitalize my running whenever the trails start look like the same old same old.
It seems that getting lost is a universal pattern, which made it a perfect topic for an OOPSLA keynote talk.
I can talk about something other than science. As I write this, I am at a talk called "What is a Tree?", by computational artist Ira Greenberg. In it, Greenberg is telling his story of going from art to math -- and computation -- and back.
Greenberg started as a traditional artist, based in drawing and focused in painting. He earned his degrees in Visual Art, from Cornell and Penn. His training was traditional, too -- no computation, no math. He was going to paint.
In his earliest work, Greenberg was caught up in perception. He found that he could experiment only with the motif in front of him. Over time he evolved from more realistic natural images to images that were more "synthetic", more plastic. His work came to be about shape and color. And pattern.
Alas, he wasn't selling anything. Like all of us, he needed to make some money. His uncle told him to "look into computers -- they are the future". (This is 1993 or so...) Greenberg could not have been less interested. Working with computers seemed like a waste of time. But he got a computer, some software, and some books, and he played. In spite of himself, he loved it. He was fascinated.
Soon he got paying gigs at places like Conde Nast. He was making good money doing computer graphics for marketing and publishing folks. At the time, he said, people doing computer graphics were like mad scientists, conjuring works with mystical incantations. He and his buddies found work as a hired guns for older graphic artists who had no computer skills. They would stand over his should, point at the screen, and say in rapid-fire style, "Do this, do this, do this." "We did, and then they paid us."
All the while, Greenberg was still doing his "serious work" -- painting -- on side.
But he got good at this computer stuff. He liked it. And yet he felt guilty. His artist friends were "pure", and he felt like a sell-out. Even still, he felt an urge to "put it all together", to understand what this computer stuff really meant to his art. He decided to sell out all the way: to go to NYC and sell these marketable skills for big money. The time was right, and the money was good.
It didn't work. Going to an office to produce commercial art for hire changed him, and his wife notice. Greenberg sees nothing wrong with this kind of work; it just wasn't for him. Still, he liked at least one thing about doing art in the corporate style: collaboration. He was able to work with designers, writers, marketing folks. Serious painters don't collaborate, because they are doing their own art.
The more he work with computers in the creative process, the more he began to feel as if using tools like Photoshop and LightWave was cheating. They provide an experience that is too "mediated". With any activity, as you get better you "let the chaos guide you", but these tools -- their smoothness, their engineered perfection, their Undo buttons -- were too neat. Artists need fuzziness. He wanted to get his hands dirty. Like painting.
So Greenberg decided to get under the hood of Photoshop. He started going deeper. His artist friends thought he was doing the devil's work. But he was doing cool stuff. Oftentimes, he felt that the odd things generated by his computer programs were more interesting than his painting!
He went deeper with the mathematics, playing with formulas, simulating physics. He began to substitute formulas inside formulas inside formulas. He -- his programs -- produced "sketches".
At some point, he came across Processing, "an open source programming language and environment for people who want to program images, animation, and interactions". This is a domain-specific language for artists, implemented as an IDE for Java. It grew out of work done by John Maeda's group at the MIT Media Lab. These days he programs in ActionScript, Java, Flash, and Processing, and promotes Processing as perhaps the best way for computer-wary artists to get started computationally.
With his biographical sketch done, he moved on to the art that inspired his talk's title. He showed a series of programs that demonstrated his algorithmic approach to creativity. His example was a tree, which was a double entendré for his past as a painter of natural scenes and also for his embrace of computer science.
He started with the concept of a tree in a simple line drawing. Then he added variation: different angles, different branching factors. These created asymmetry in the image. Then he added more variation: different scales, different densities. Then he added more variation: different line thickness, "foliage" at the end of the smallest branches. With randomness elements in the program, he gets different outputs each time he runs the code. He added still more variation: color, space, dimension, .... He can keep going along as many different conceptual dimensions as he likes to create art. He can strive for verisimilitude, representation, abstraction, ... any artistic goal he might seek with a brush and oils.
Greenberg's artistic medium is code. He writes some code. He runs it. He change some things, and runs it again. This process is interactive with the medium. He evolves not a specific work of art, but an algorithm that can generate an infinite number of works.
I would claim that in a very important sense his work is the algorithm. For most artists, the art is in the physical work they produce. For Greenberg, there is a level of indirection -- which is, interestingly, one of the most fundamental concepts of computer science. For me, perhaps the algorithm is the artistic work! Greenberg's program is functional, not representational, and what people want to see is the art his programs produce. But code can be beautiful, too.
Context You are in an Interactive Performance, perhaps a play, using Scripted Dialogue.
Problem The performer speaking before you delivers a line incorrectly. The new line does not change the substance of the play, but it interrupts the linguistic flow.
Example Instead of saying "until the first of the year", the performer says as "for the rest of the year".
Forces You know your lines and want to deliver them correctly.
The author wrote the dialogue with a purpose in mind.
Delivering the line as you memorized it is the safest way for you to proceed, and also the safest route back on track.
BUT... Delivering the scripted line will call attention to the error. This may disconcert your partner. It will also break the mood for the audience.
So: Adapt your line to the set up. Respond in a way that is seamless to the audience, retains the key message of the story, and gets the dialogue back on track.
That is, catch what you are thrown.
Example Change your line to say "for the rest of the year?" instead of "until the first of the year?"
Related Patterns If the performer speaking before you misses a line entirely, or gets off the track of the Scripted Dialogue, deliver a Redirecting Line.
----
Postscript: This category of my blog is intended for software patterns and discussion thereof, but this is a pattern I just learned and felt a strong desire to right. I may well try to write Redirecting Line and maybe even the higher-level Scripted Dialogue and Interactive Performance patterns, if the mood strikes me and the time is available. I never thought of pattern language of performance when I signed on for this gig... And just so you know, I was the performer who mis-delivered his line in the example given above, where I first encountered Catch What You're Thrown.
Last Sunday was my fourth rehearsal as a newly-minted actor. It was our first run-through of the entire play on stage, and the first time the director had a chance to give notes to the entire cast. The whole afternoon, my mind was making connections between plays and programs -- and I don't mean the playbill. These thoughts follow from my experiences in the play and my experiences as a software developer. Here are a few.
Scenes and Modularity Each scene is a like a module that the director debugs. In some plays, the boundaries between scenes are quite clean, with a nice discrete break from one to the next. With a play on stage, the boundaries between scenes may be less clear.
In our play, scenes often blend together. Lights go down on one part of the stage and up on another, shifting the audience's attention, while the actors in the first scene remain in place. Soon, the lights shift back to the first and the action picks up where it left off. Plays work this way in part because they operate in limited real estate, and changing scenery can be time-consuming. But this also
It is easier to debug scenes that are discrete modules. Debugging scenes that run together is difficult for the same reasons that debugging code modules with leaky boundaries is. A change in one scene (say, repositioning the players) can affect the linked scene (say, by changing when and where a player enters).
Actors and Code If the director debugs a scene, then maybe the actors, their lines, and the props are like the code. That just occurred to me while typing the last section!
Time Constraints It is hard to get things right in a rush. Based on what he sees as the play executes, the director makes changes to the content of the show. He also refactors: rearranging the location of a prop or a player, either to "clean things up" or to facilitate some change he has in mind. It takes time to clean things up, and we only know that something needs to be changed as we watch the play run, and think about it.
Mock Objects and Stand-Ins When rehearsing a scene, if you don't have the prop that you will use in the play itself, then use something to stand in its place. It helps you get used to the the fact that something will be there when you perform, and it helps the director remember to take into account its use and placement. You have to remember to have it at the right time in the right place, and return it to the prop table when it's not in use.
A good mock prop just needs to have some of the essential features of the actual prop. When rehearsing a scene in which I will be carrying some groceries to the car, I grabbed a box full of odds and ends out of some office. It was big enough and unwieldy enough to help me "get into the part".
Conclusion for Now The relationship between software and plays or movies is not a new idea, of course. I seem to recall a blog entry a couple of years ago that explored the analogy of developing software as producing a film, though I can't find a link to it just now. Googling for it led me to a page on Ward's wiki and to two entries on the Confused of Calcutta blog. More reading for me... And of course there is Brenda Laurel's seminal Computers As Theatre. You really should read that!
Reading is a great way to learn, but there is nothing quite like living a metaphor to bring it home.
The value that comes from making analogies and metaphors comes in what we can we learn from them. I'm looking forward to a couple of weeks more of learning from this one -- in both directions.
Gerald Weinberg's recent blog Setting (and Character): A Goldilocks Exercise describes a writing exercise that I've mentioned here as a programming exercise, a pedagogical pattern many call Three Bears. This is yet another occurrence of a remarkably diverse pattern.
Weinberg often describes exercises that writers can use to free their minds and words. It doesn't surprise me that "freeing" exercises are built on constraints. In one post, Weinberg describes The Missing Letter, in which the writer writes (or rewrites) a passage without using a randomly chosen letter. The most famous example of this form, known as a lipogram, is La disparition, a novel written by Georges Perec without an using the letter 'e' -- except to spell the author's name on the cover.
When I read that post months ago, I immediately thought of creating programming exercises of a similar sort. As I quoted someone in a post on a book about Open Court Publishing, "Teaching any skill requires repetition, and few great works of literature concentrate on long 'e'." We can design a set of exercises in which the programmer surrenders one of her standard tools. For instance, we could ask her to write a program to solve a given problem, but...
Recursion is the usual path for students to learn how to repeat actions without a for statement, but I don't think most students get recursion the way most folks teach it. Learning it with a rich data type makes a lot more sense.
Of course, one can start with an language that provides only the most meager set of base types, thus forcing one to build up nearly all the abstractions demanded by a problem. Scheme feels like that to most students, but only a few of mine seem to grok how much they can learn about programming by working in such a language. (And it's of no comfort to them that Church built everything out of functions in his lambda calculus!)
This list operates at the level of programming construct. It is just the beginning of the possibilities. Another approach would be to forbid the use of a data structure, a particularly useful class, or an individual base type. One could even turn this idea into a naming challenge by hewing close to Weinberg's exercise and forbidding the use of a selected letter in identifiers. As an instructor, I can design an exercise targeted at the needs of a particular student or class. As a programmer, I can design an exercise targeted at my own blocks and weaknesses. Sometimes, it's worth dusting off an old challenge and doing it for it's own sake, just to stay sharp or shake off some rust.
Back in 2002, I had a sabbatical (which are, for political reasons, called "professional development assignments" here) to document some of the patterns of programs that are written in a functional style. So much good work had been done by then on object-oriented patterns, and a few people had begun to describe OO patterns that were motivated by functional programming techniques. But few people had begun to document functional programming patterns. I had a feeling that something useful could come from moving into that space. In the end I didn't make the sort of progress I had hoped, but I did learn a lot that has continued to influence my teaching and research.
One of the areas explored as a part of that project was refactoring functional programs. I believe that refactorings are the behavioral side of patterns, and by looking at work on existing work on functional refactorings I hoped to speed my search for patterns. There hadn't been much work done on refactoring functional programs at that time, but I wasn't alone in thinking of it -- Simon Thompson and Claus Reinke were just beginning their quite productive research program on the same topic. That work, like most of the work that deals with patterns and refactoring in functional programming, focused more on the language-processing side of the topic, considering refactorings as program transformation in the vein of compiler optimizations. What seems to me still to be missing is a look at refactoring of functional programs from the software developer's perspective: I have a piece of code that I want to restructure in order to improve its design, or in order to facilitate the next addition to the program. What do I do next?
Noel Welsh recently wrote a blog entry called Refactoring Functional Programs that makes a very nice contribution in the space. Noel is just the sort of person who should be documenting FP patterns and refactorings, a practicing developer who works primarily in a functional or mostly functional language and who is growing a significant software system over time. His essay talks about a sequence of refactorings that he applied to his system, a web application framework, which moved one of its modules from a long-ish, overly complex, repetitive piece of code to something shorter, simpler, and well-factored. You will enjoy reading the article even if you don't program in Scheme, because Noel explains the thinking he did as he attacked this piece of code, including an alternative step that he considered but rejected due to the forces that affect his system.
For the record, his three refactorings were these. The names are part mine, part his:
One of the things I like about this story is how it makes a "round-trip" to CPS and back, using the trip to change the program's design in a way that might have been inaccessible otherwise. I teach my students a similar "refactoring pattern" when teaching them functional programming, make a round-trip through Mutual Recursion, by way of Program Derivation, to convert a messy one-procedure solution into a clean and one-elegant one-procedure solution.
I hope that other developers using functional programming in the trenches will follow Noel's lead and document some of the patterns and refactorings that they discover in their code and process. More than anything else, this sort of pragmatic, nuts-and-bolts writing will help to increase the accessibility of functional programming style to a wider audience of programmers.
Browsing through several overflowing mailboxes from various agile software development lists -- thank the mail gods for procmail -- I ran across three messages that made save them for later reflection or use in a class.
On the extreme programming mailing list, Laurent Bossavit wrote:
> How do you deal with [a change breaks lots of tests]?See it as an opportunity. A single change breaking a lot of tests means that your design has excessive coupling, *or* that your unit tests are written at too low a level of abstraction. This is an important and valuable thing to have learned about your code and tests.
Most of the time, we treat unexpected obstacles as problems -- either problems to solve, or problems to avoid. When we operate under time pressure, we usually try to avoid such obstacles. Students often find themselves in a bind for time and so seek to avoid problems, rather using them as an opportunity to make their programs better. This is another reason to start assignments early: to have time to be opportunistic on unexpected chances to learn!
Time pressure isn't the only reason students avoid problems. Some are driven by grades, in order to impress potential employers. (Maybe this will change in a world not driven by "getting a job".) Others are simply afraid to take the risk. Our school system does a pretty good job of beating risk-taking behavior out of students by the time they reach the university, and universities often don't do enough to undo this before they graduate into professional careers.
On the agile content of Laurent's comment, he is spot-on, of course. All those broken tests are excellent feedback on a weakness in either the system or the tests. Listen to the code.
On the Crystal Clear mailing list (dedicated to discussing "the ultralight Crystal Clear software development methodology"), methodology creator Alistair Cockburn wrote:
"Deciding what to build" is my new replacement phrase for "requirements". The word "requirements" tends to imply
- that [someone can] correctly locate and pluck them,
- that they are "true"
None of those are correct. They don't already pre-exist so they can't be "plucked", [no one is] in that unique position [to locate and pluck them], and they aren't "true".
which is why I nowadays prefer to talk about "deciding what to build".
I wish that more people -- software engineers, programmers, and consumers of software and software development services alike -- would think like this! "Gathering requirements" is a metaphor that goes wrong both on 'gathering' and on 'requirements'. "Deciding what to build" creates a completely different mental image and sets up a completely different set of performance expectations. As Alistair tells it, "deciding what to build" is a negotiation, not an exam question about Platonic ideals that has a correct answer. A negotiation can take into account many factors, including benefits, costs, preferences, and time.
The developer and customer can then record the results of their negotiation in a "contract" of whatever form they prefer. If they are so inclined, the contract can take the form of a set of tests that captures what the developer will deliver, say, FIT tests. When one side needs to "break the contract", the negotiation should have specified relative preferences that enable the parties to arrive at a new understanding of the system to be built. More negotiation -- not "adding a requirement" or "not delivering a requirement".
Finally, for a touch of humor, I give you this passage from a message to the refactoring mailing list from Buddha Buck:
Our team is considering implementing the refactoring "Replace C++ With Language With Good Refactoring Tool Support". It's a major refactoring, and full implementation might take months, if we decide to do it.
There have to be some pleasant steps to execute in this refactoring, and some even more pleasant milestones. That last rm *.cpp has to feel good.
More seriously, I think there is a great opportunity to write refactorings that aren't about software architecture and code. The patterns of Christopher Alexander begat the software patterns world, which caused many of us to write the patterns of other kinds of systems, including music, management, and pedagogy. In many software circles, refactorings are behavior-preserving modifications that target the patterns of the domain. If we write patterns of management or pedagogy, then why not write refactorings that help people prepare their environments for disruptive change? An interesting question comes to mind: what does it mean for a change to a management system to "preserve behavior"? This seems like a very cool avenue for some thought, even if it hits a dead end.
This week I ran across a link to an old essay called Good Writing, by Marc Raibert. By now I shouldn't be so happy to be reminded how much good programming practice is similar to good writing in general. But I usually am. The details of Raibert's essay are less important to me than some of his big themes.
Raibert starts with something that people often forget. To write well, one must ordinarily want to write well and believe that one can. Anyone who teaches introductory computer science knows how critical motivation and confidence are. Many innovations in CS1 instruction over the years have been aimed at helping students to develop confidence in the face of what appear to be daunting obstacles, such as syntax, rigor, and formalism. Much wailing and gnashing of teeth has followed the slowly dawning realization that students these days are much less motivated to write computer programs than they have been over most of the last thirty years. Again, many innovations and proposals in recent years have aimed at motivating students -- more engaging problems, media computation, context in the sciences and social sciences, and so on. These efforts to increase motivation and confidence are corporate efforts, but Raibert reminds us that, ultimately, the individual who would be a writer must hold these beliefs.
After stating these preconditions, Raibert offers several pieces of advice that apply directly to computing. Not surprisingly, my favorite was his first: Good writing is bad writing that was rewritten. This fits nicely in the agile developer's playbook. I think that few developers or CS teachers are willing to say that it's okay to write bad code and then rewrite. Usually, when folks speak in terms of do the simplest thing that will work and refactor mercilessly, they do not usually mean to imply that the initial code was bad, only that it doesn't worry inordinately about the future. But one of the primary triggers for refactoring is the sort of duplication that occurs when we do the simplest thing that will work without regard for the big picture of the program. Most will agree that most such duplication is a bad thing. In these cases, refactoring takes a worse program and creates a better one.
Allowing ourselves to write bad code empowers us, just as it empowers writers of text. We need not worry about writing the perfect program, which frees us to write code that just works. Then, after it does, we can worry about making the program better, both structurally and stylistically. But we can do so with the confidence that comes from knowing that the substance of our program is on track.
Of course, starting out with the freedom to write bad code obligates us to re-write, to refactor, just as it obligates writers of text to re-write. Take the time! That's how we produce good code reliably: write and re-write.
I wish more of my freshmen would heed this advice:
The first implication is that when you start a new [program], there is nothing wrong with using bad writing. Your goal when you start is to get your ideas down on paper in any form you can.
For the novice programmer, I do not recommend writing ungrammatical or "stream of consciousness" code, but I do encourage them to take the ideas they have after having thought about the problem and expressing them in code. The best way to find out if an idea is a good one is to see it run in code.
Raibert's other advice also applies. When I read Spill the beans fast, I think of making my code transparent. Don't hide its essence in subterfuge that makes me seem clever; push its essence out where all can see it and understand the code. Many of the programmers whose code I respect most, such as Ward Cunningham, write code that is clear, concise, and not at all clever. That's part of what makes it beautiful.
Don't get attached to your prose is essential when writing prose, and I think it applies to code as well. Just because you wrote a great little method or class yesterday doesn't mean that it should survive in your program of tomorrow. While programming, you discover more about your problem and solution than you knew yesterday. I love Raibert's idea of a PRIZE_WINNING_STUFF.TXT file. I have a directory labeled playground/ where I place all the little snippets I've built as simple learning experiments, and now I think I need to create a winners/ directory right next to it!
Raibert closes with the advice to get feedback and then to trust your readers. A couple of months back I had a couple of entries on learning from critics, with different perspectives from Jerry Weinberg and Alistair Cockburn. That discussion was about text, not code (at least on the surface). But one thing that we in computer science need to do is to develop a stronger culture of peer review of our code. The lack of one is one of the things that most differentiates us from other engineering disciplines, to which many in computing look for inspiration. I myself look more to the creative disciplines for inspiration than to engineering, but on this the creative and engineering disciplines agree: getting feedback, using it to improve the work, and using it to improve the person who made the work are essential. I think that finding ways to work the evaluation of code into computer science courses, from intro courses to senior project courses, is a path to improving CS education.
This last bit of advice from Raibert is also timely, if bittersweet for me... In just a few days, PLoP 2007 begins. I am quite bummed that I won't be able to attend this year, due to work and teaching obligations. PLoP is still one of my favorite conferences. Patterns and writers' workshops are both close to my heart and my professional mind. If you have never written a software pattern or been to PLoP, you really should try both. You'll grow, if only when you learn how a culture of review and trust can change how you think about writing.
The good news for me is that OOPSLA 2007, which I will be attending, features a Mini-PLoP. This track consists of a pattern writing bootcamp, a writers' workshop for papers in software development, and a follow-up poster. I hope to attend the writers' workshop session on Monday, even if only as a non-author who pledges to read papers and provide feedback to authors. It's no substitute for PLoP, but it's one way to get the feeling.
No, I've not become homicidal. That is the title of a recent book about the Open Court Publishing Company, which according to its subtitle "fought the culture of American education" by trying to change how our schools teach reading and mathematics. Blouke Carus, the creator of Open Court's reading program, sought to achieve an enviable goal -- engagement with and success in the world of great ideas for all students -- in a way that was beholden to neither the traditionalist "back to basics" agenda nor the progressivist "child-centered" agenda. Since then, the reading series has been sold to McGraw-Hill.
Thanks to the creator of the TeachScheme! project, Matthias Felleisen, I can add this book to my list of millions. He calls Let's Kill and Dick and Jane "TeachScheme! writ large". Certainly there are differences between the K-8 education culture and the university computer science culture, but they share enough commonalities to make reform efforts similarly difficult to execute. As I have noted before, universities and their faculty are a remarkably conservative lot. TeachScheme! is a principled, comprehensive redefinition of introductory programming education. In arguing for his formulation, Felleisen goes so far as to explain why Structure and Interpretation of Computer Programs -- touted by many, including me, as the best CS book ever written -- is not suitable for CS 1. (As much as I like SICP, Matthias is right.)
But TeachScheme! has not succeeded in the grand way its implementors might have hoped, for many of the reasons that Open Court's efforts have come up short of its founders' goals. Some of the reasons are cultural, some are historical, and some are probably strategic.
The story of Open Court is of immediate interest to me for our state's interest in changing K-12 math and science education in a fundamental way, a reform effort that my university has a leading role in, and which my department and I have a direct interest in. We believe in the need for more and better computer scientists and software developers, but university CS enrollments remain sluggish. Students who are turned off to science, math, and intellectual ideas in grade school aren't likely to select CS as a major in college... Besides, like Carus, I have a great interest in raising the level of science and math understanding across the whole population.
This book taught me a lot about what I had understood only incompletely as an observer of our education system. And I appreciated that it avoided the typical -- and wrong -- conservative/liberal dichotomy between the traditional and progressive approaches. America's education culture is a beast all its own, essentially anti-intellectual and exhibiting an inertia borne out of expectations, habit, and a lack of will and time to change. Changing the system will take a much more sophisticated and patient approach than most people usually contemplate.
Though I have never developed a complete curriculum for CS 1 as Felleisen has, I have long aspired to teaching intro CS in more holistic way, integrating the learning of essential tools with higher-level design skills, built on the concept of a pattern language. So Open Court's goals, methods, and results all intrigue me. Here are some of the ideas that caught my attention as I read the story of Open Court:
A teacher must dare to be different! She must pull away from monotonous repetition of, 'Today we are going to write a story.' Most children view that announcement with exactly what it deserves, and there are few teachers who are not aware of what the reactions are.
s/story/program/* and s/children/students/* to get a truth for CS instructors such as me.
The quality of the Open Court program was a substantive strength and a marketing weakness. It required teachers to be conversant with a variety of methods. And the program worked best when used as a system... Teachers accustomed to trying a little of this and a little of that were likely to be put off by an approach that did not lend itself to tinkering.
I guess I'm not the only person who has trouble sticking to the textbook. To be fair to us tinkerers, systematic integrated instructional design is so rare as to make tinkering a reasonable default stance.
Thus Open Court's usual problem was not that it contradicted teachers' ideology, but that it violated their routine.
Old habits dies hard, if at all.
... the learned professions ...[in which] adaptation to the expectations of one's peers requires continual growth in knowledge and competence.
In the professions, we focus not only on level of knowledge but also on the process of continuously getting better.
There are circumstances in which it is best to package our revolutionary aspirations in harmless-looking exteriors.... We should swallow our pride and let [teachers] think we are bland and acceptable. We should fool them and play to their complacent pieties, But we should never for a moment fool ourselves.
Be careful what you pretend to be.
Then we obsess about covering all the material, made harder by the inclusion of all this new material.
I've seen this happen to software patterns in CS classrooms. It was sad. I usually think that we elementary patterns folks haven't done a good enough job yet, but Open Court's and TeachScheme!'s experiences do not encourage me to think that it will be easy to do well enough.
In an academic metaphor, [Open Court's] people had been the liberal-arts students looking down on the business school. But now that they needed business-school expertise, they were unable to judge it critically.
Maybe a so-called "liberal education" isn't broad enough if it leaves the recipient unable to think deeply in an essential new context. In today's world, both business and technology are essential components of a broadly applicable education.
Teaching any skill requires repetition, and few great works of literature concentrate on long "e".
My greatest inspiration in this vein is the Suzuki literature developed for teaching violin and later piano and other instruments. I've experienced the piano literature first hand and watched my daughters work deep into the violin literature. At the outset, both use existing works (folk tunes, primarily) whenever appropriate, but they also include carefully designed pieces that echo the great literature we aspire to for our students. As the student develops technical skill, the teaching literature moves wholly into the realm of works with connection to the broader culture. My daughters now play real violin compositions from real composers.
TeachScheme! is a worthy model in this regard. It has worked hard to train teachers in its approach, to provide tangible support, and to build a community of teachers.
In my own work on elementary patterns, my colleague and friend Robert Duvall continually reminds us all of the need for providing practical support to instructors who might adopt our ideas -- if only they had the instructional tools to make a big change in how they teach introductory programming.
In an odd way, this reminds me of Drawing on the Right Side of the Brain, with its precept that we should draw what we see, not what we think we see.
Let's Kill Dick and Jane is a slim volume, a mere 155 pages, and easy to read. It's not perfect, neither in its writing nor in its analysis, but it tells an important story well. I recommend it to anyone with aspirations of changing how we teach computer science to students in the university or high school. I also recommend it to anyone who is at all interested in the quality of our educational system. In a democracy such as the U.S., that should be everyone.
David Altenburg gives a nice example of a pattern you'll find in functional programs, which he calls Enumerate, Map, Filter, Accumulate. This isn't the typical snappy pattern name, but it does show deference to Chapter 2 of Structure and Interpretation of Computer Programs, which discusses in some detail this way of processing a stream of data. I like this blog entry because it illustrates nicely a difference between how functional programmers think and how OO programmers think. He even gives idiomatic Java and Ruby code to demonstrate the difference more clearly. Seeing the Ruby example also makes clear just how powerful first-order blocks can be in an OO language.
SICP is a veritable catalog of the patterns of programs written in a functional style. They aren't written in Alexander's pattern style, but they are patterns nonetheless.
(In a snarkier mood, I might say: I am sure some Lisp aficionado will explain that Enumerate, Map, Filter, Accumulate isn't a pattern because we could define a enumapfilacc macro and make it go away...)
I haven't written much in anticipation of OOPSLA 2007, but not because I haven't been thinking about it. In years when I have had a role in content, such as the 2004 and 2005 Educators' Symposia or even the 2006 tutorials track, I have been excited to be deep in the ideas of a particular part of OOPSLA. This year I have blogged just once, about the December planning meeting. (I did write once from the spring planning meeting, but about a movies.) My work this year for the conference has been in an administrative role, as communications chair, which has focused on sessions and schedules and some web content. Too be honest, I haven't done a very good job so far, but that is a subject for another post. For now, let's just say that I have not been a very good Mediator nor a good Facade.
I am excited about some of the new things we are doing this year to get the word out about the conference. At the top of this list is a podcast. Now, podcasts have been around for a while now, but they are just now becoming a part of the promotional engine for many organizations. We figured that hearing about some of the cool stuff that will happen at OOPSLA this year would complement what you can read on the web. So we arranged to have two outfits, Software Engineering Radio and DimSumThinking, co-produce a series of episodes on some of the hot topics covered at this year's conference.
Our first episode, on a workshop titled No Silver Bullet: A Retrospective on the Essence and Accidents of Software Engineering, organized by Dennis Mancl, Steven Fraser, and Bill Opdyke, is on-line at the OOPSLA 2007 Podcast page. Stop by, give it a listen, and subscribe to the podcast's feed so that you don't miss any of the upcoming episodes. (We are available in iTunes, too.) We plan to role new interviews out every 7-10 for the next few months. Next up is a discussion of a Scala tutorial with Martin Odersky, due out on July 16.
If you would like to read a bit more about the conference, check out conference chair Richard Gabriel's The (Unofficial) How To Get Around OOPSLA Guide, and especially his ooPSLA Impressions. As I've written a few times, there really isn't another conference like OOPSLA. Richard's impressions page does a good job communicating just how, mostly in the words of people who've been to OOPSLA and seen it.
While putting together some of his podcast episodes, Daniel Steinberg of DimSumThinking ran into something different than usual: fun.
I've done three interviews for the oopsla podcast -- every interviewee has used the same word to describe OOPSLA: fun. I just thought that was notable -- I do a lot of this sort of thing and that's not generally a word that comes up to describe conferences.
And that fun comes on top of the ideas and the people you will encounter, that will stretch you. We can't offer a Turing Award winner every year, but you may not notice with all the intellectual foment. (And this year, we can offer John McCarthy as an invited speaker...)
I remember back in my early years teaching (*) I had a student who came in to ask a question about a particular error message she had received from our Pascal compiler. She had some idea of what caused it, and she wanted to know what it meant. It was a pretty advanced error, one we hadn't reached the point of making in class, so I asked her how she had managed to bump into it. Easy, she said; she was intentionally creating errors in her program so that she could figure out what sort of error messages would result.
If you teach programming for very long, you are bound to encounter such a student. She was doing fine in class but was afraid that she was having life too easy and so decided to use her time productively -- creating errors that she could learn to debug and maybe even anticipate.
I've written many times before about practice, including practice for practice's sake. That entry was about the idea of creating "valueless software", which one writes as a learning exercise, not for anyone's consumption. But my forward-thinking student was working more in the vein of fixin' what's broke, in which one practices in an area of known weakness, with the express purpose of making that part of one's game strong. My student didn't know many of the compiler error messages that she was going to face in the coming weeks, so she set out to learn them.
I think that she was actually practicing a simple form of an even more specific learning pattern: consciously seeking out, even creating, challenges to conquer. Make a Mess, Clean it Up!, is a neat story about an example of this pattern in the history of the Macintosh team. There, Donn Denman talks about Burrell Smith's surprising way of getting better at Defender, a video game played by the Mac programmers as a way to relax or pump themselves up:
Instead of avoiding the tough situations, he'd immediately create them, and immediately start learning how to handle the worst situation imaginable. Pretty soon he would routinely handle anything the machine could throw at him.
He'd lose a few games along the way, but soon he was strong in areas of the game that his competitors may not have even encountered before -- because they had spent time avoiding difficult times! Denman saw in this game-playing behavior something he recognized in Smith's life as a programmer: he
... likes challenges so much that he actually seeks them out and consciously creates them. In the long run, this approach makes sense. He seems to aggressively set up challenging situations throughout his life. Then, when life throws him a curve ball, he'll swing hard, and knock it out of the park.
The article uses two metaphors for this pattern: make a mess so that you can clean it up, and choose to face tough situations so that you are ready for the curve balls life throws you. (I guess those tough situations must be akin the nasty breaking stuff of a major-league pitcher.) My title for this entry offers a third metaphor: digging yourself into a hole so that you can how to get out of a hole. As much a baseball fan as I was growing up, the metaphor of digging oneself into a hole was the more common. Whatever it's name, the idea is the same.
I find that I'm more likely to apply this pattern in some parts of my life than others. In programming, I can recover from bad situations by re-compiling, re-booting, or at worst reinstalling. In running, I can lose all the races I want, or come up short in a training session all I want -- so long as I don't put my body at undue risk. The rest of life, the parts that deal with other people, require some care. It's hard to create an environment in which I can screw up my interpersonal relationships just so that I can learn how to get out of the mess. There's a different metaphor for such behavior -- burning bridges -- that connotes its irreversibility. Besides, it's not right to treat people as props in my game. I suppose that this is a place in which role play can help, though artificial situations can go only so far.
Where games, machines, and tools are concerned, though, digging a deep hole just for the challenge of getting out of it can be a powerful way to learn. Pretty soon, you can find yourself as master of the machine.
----
(*) Yikes, how old does that make me sound?
An idea is a feat of association.
-- Robert Frost
Yesterday I went to a talk by Roy Behrens, an earlier talk of whose I enjoyed very much and blogged about. That time he talked about teaching as a "subversive inactivity", and this time he spoke more on the topic of his scholarly interest, in creativity and design, ideas and metaphors, similarities and differences, even camouflage! Given that these are his scholarly interests, I wasn't surprised that this talk touched on some of the same concepts as his teaching talk. There are natural connection between how ideas are formed at the nexus os similarity and difference and how one can best help people to learn. I found this talk energizing and challenging in a different sort of way.
In the preceding paragraph, I first wrote that Roy "spoke directly on the topic of his scholarly interest", but there was little direct about this talk. Instead, Roy gave us parallel streams of written passages and images from a variety of sources. This talk felt much like an issue of his commonplace book/journal Ballast Quarterly Review, which I have blogged about before. The effect was mesmerizing, and it had its intended effect in illustrating his point: that the human mind is a connection-making machine, an almost unwilling creator of ideas that grow out of the stimuli it encounters. We all left the talk with new ideas forming.
I don't have a coherent, focused essay on this talk yet, but I do have a collection of thoughts that are in various stages of forming. I'll share what I have now, as much for my own benefit as for what value that may have to you.
Similarity and difference, the keys to metaphor, matter in the creation of software. James Coplien has written an important book that explicates the roles of commonality analysis and variability analysis in the design of software that can separate domain concerns into appropriate modules and evolve gracefully as domain requirements change. Commonality and variability; similarity and difference. As one of Roy's texts pointed out, the ability to recognize similarity and difference is common to all practical arts -- and to scientists, creators, and inventors.
The idea of metaphor in software isn't entirely metaphorical. See this paper by Noble, Biddle, and Tempero that considers how metaphor and metonymy relate to object-oriented design patterns. These creative fellows have explored the application of several ideas from the creative arts to computing, including deconstruction and postmodernism.
To close, Roy showed us the 1952 short film Blacktop: A Story of the Washing of a School Play Yard. And that's the story it told, "with beautiful slow camera strides, the washing of a blacktop with water and soap as it moves across the asphalt's painted lines". This film is an example of how to make something fabulous out of... nothing. I think the more usual term he used was "making the familiar strange". Earlier in his talk he had read the last sentence of this passage from Maslow (emphasis added):
For instance, one woman, uneducated, poor, a full-time housewife and mother, did none of these conventionally creative things and yet was a marvelous cook, mother, wife, and home-maker. With little money, her home was somehow always beautiful. She was a perfect hostess. Her meals were banquets, her taste in linens, silver, glass crockery and furniture was impeccable. She was in all these areas original, novel, ingenious, unexpected, inventive. I learned from her and others like her that a first-rate soup is more creative than a second-rate painting, and that, generally, (un)cooking or parenthood or making a home could be creative while poetry need not be; it cold be uncreative.
Humble acts and humble materials can give birth to unimagined creativity. This is something of a theme for me in the design patterns world, where I tell people that even novices engage in creative design when they write the simplest of programs and where so-called elementary patterns are just as likely to give rise to creative programs as Factory or Decorator.
Behrens's talk touched on two other themes that run through my daily thoughts about software, design, and teaching. One dealt with tool-making, and the other with craft and limitations.
At one point during the Q-n-A after the talk, he reminisced about Let's Pretend, a radio show from his youth which told stories. The value to him as a young listener lay in forcing -- no, allowing -- him to create the world of the story in his own mind. Most of us these days are conditioned to approach an entertainment venue looking for something that has already been assembled for us, for the express purpose of entertaining ourselves. Creativity is lost when our minds never have the opportunity to create, and when our minds' ability to create atrophies from disuse. One of Roy's goals in teaching graphic design students is to help students see that they have the tools they need to create, to entertain.
This is true for artists, but in a very important sense it is true for computer science students, too. We can create. We can build our own tools--our own compilers, our own IDEs, our own applications, our own languages... anything we need! That is one of the great powers of learning computer science. We are in a new and powerful way masters of our own universe. That's one of the reasons I so enjoy teaching Programming Languages and compilers: because they confront CS students directly with the notion that their tools are programs just like any other. You never have to settle for less.
Finally, may favorite passage from Roy's talk plays right into my weakness for the relationship between writing and programming, and for the indispensable role of limitation in creativity and in learning how to create. From Anthony Burgess:
Art begins with craft, and there is no art until craft has been mastered. You can't create until you're willing to subordinate the creative impulses to the constriction of a form. But the learning of craft takes a long time, and we all think we're entitled to shortcuts.... Art is rare and sacred and hard work, and there ought to be a wall of fire around it.
One of my favorite of my blog posts is from March 2005, when I wrote a piece called Patterns as a Source of Freedom. Only in looking back now do I realize that I quoted Burgess there, too -- but only the sentence about willing subordination! I'm glad that Roy gave the context around that sentence yesterday, because it takes the quote beyond constriction of form to the notion of art growing out of craft. It then closes with that soaring allusion. Anyone who has felt even the slightest sense of creating something knows what Burgess means. We computer scientists may not like to admit that what we do is sometimes art, and that said art is rare and sacred, but that doesn't change reality.
Good talk -- worth much more in associations and ideas than the lunch hour it cost. My university is lucky to have Roy Behrens, and other thinkers like him, on our faculty.
At SIGCSE a couple of weeks ago, I attended an interesting pair of complementary panel sessions. I wrote about one, Ten Things I Wish They Would Have Told Me..., in near-real time. Its complement was a panel called "It Seemed Like a Good Idea at the Time". Here, courageous instructors got up in front of a large room full of their peers to do what for many is unthinkable: tell everyone about an idea they had that failed in practice. When the currency of your profession is creating good ideas, telling everyone about one of your bad ideas is unusual. Telling everyone that you implemented your bad idea and watched it explode, well, that's where the courage comes in.
My favorite story from the panel was from the poor guy who turned his students loose writing Java mail agents -- on his small college's production network. He even showed a short video of one of the college's IT guys describing the effects of the experiment, in a perfect deadpan. Hilarious.
We all like good examples that we can imitate. That's why we are all drawn to panels such as "Ten Things..." -- for material to steal. But other than the macabre humor we see in watching someone else's train wreck, what's the value in a panel full of bad examples?
The most immediate answer is that we may have had the same idea, and we can learn from someone else's bad example. We may decide to pitch the idea entirely, or to tweak our idea based on the bad results of our panelist. This is useful, but the space of ideas -- good and bad -- is large. There are lots of ways to tweak a bad idea, and not all of them result in a good idea. And knowing that an idea is bad leaves us with the real question unanswered: Just what should we do?
(The risk that the cocky among us face is the attitude that says, "Well, I can make that work. Just watch." This is the source