With the exception of teaching our database course during the COVID year, I have been teaching a stable pair of courses for the last many semesters: Programming Languages in the spring and our compilers course, Translation of Programming Languages, in the fall. That will change this fall due to some issues with enrollments and course demands. I'll be teaching a course in client-side web development.
The official number and title of the course are "CS 1100 Web Development: Client-Side Coding". The catalog description for the course was written years ago by committee:
Client-side web development adhering to current Web standards. Includes by-hand web page development involving basic HTML, CSS, data acquisition using forms, and JavaScript for data validation and simple web-based tools.
As you might guess from the 1000-level number, this is an introductory course suitable for even first-year students. Learning to use HTML, CSS, and Javascript effectively is the focal point. It was designed as a service course for non-majors, with the primary audience these days being students in our Interactive Digital Studies program. Students in that program learn some HTML and CSS in another course, but that course is not a prerequisite to ours. A few students will come in with a little HTML5+CSS3 experience, but not all.
So that's where I am. As I mentioned, this is one of my first courses to design from scratch in a long time. Other than Database Systems, we have to go back to Software Engineering in 2009. Starting from scratch is fun but can be daunting, especially in a course outside my core competency of hard-core computer science.
The really big change, though, was mentioned two paragraphs ago: non-majors. I don't think I've taught non-majors since teaching my university's capstone 21 years ago -- so long ago that this blog did not yet exist. I haven't taught a non-majors' programming course in even longer, 25 years or more, when I last taught BASIC. That is so long ago that their was no "Visual" in the language name!
So: new area, new content, new target audience. I have a lot of work to do this summer.
I could use some advice from those of you who do web development for a living, who know someone who does, or who are more up to date on the field than I.
Generally, what should a course with this title and catalog description be teaching to beginners in 2023?
Specifically, can you point me toward...
For example, a former student and now friend mentioned that the w3schools website includes a JavaScript tutorial which allows students to type and test code within the web browser. That might simplify practice greatly for non-CS students while they are learning other development tools.
I have so many questions to answer about tools in particular right now: Should we use an IDE or a simple text editor? Which one? Should we learn raw JavaScript or a simple framework? If a framework, which one?
This isn't a job-training course, but to the extent that's reasonable I would like for students to see a reasonable facsimile of what they might encounter out in industry.
Thinking back, I guess I'm glad now that I decided to play some around with JavaScript in 2022... At least I now have more context for evaluatins the options available for this course.
If you have any thoughts or suggestions, please do send them along. Sending email or replying on Mastodon or Twitter all work. I'll keep you posted on what I learn.
I just started reading Joshua Kendall's The Man Who Made Lists, a story about Peter Mark Roget. Long before compiling his namesake thesaurus, Roget was a medical doctor with a local practice. After a family tragedy, though, he returned to teaching and became a science writer:
In the 1820s and 1830s, Roget would publish three hundred thousand words in the Encyclopaedia Brittanica and also several lengthy review articles for the Society for the Diffusion of Useful Knowledge, the organization affiliated with the new University of London, which sought to enable the British working class to educate itself.
What a noble goal, enabling the working class to educate itself. And what a cool name: The Society for the Diffusion of Useful Knowledge!
For many years, my university has provided a series of talks for retirees, on topics from various departments on campus. This is a fine public service, though without the grand vision -- or the wonderful name -- of the Society for the Diffusion of Useful Knowledge. I suspect that most universities depend too much on tuition and lower costs these days to mount an ambitious effort to enable the working class to educate itself.
Mental illness ran in Roget's family. Kendall wonders if Roget's "lifelong desire to bring order to the world" -- through his lecturing, his writing, and ultimately his thesaurus, which attempted to classify every word and concept -- may have "insulated him from his turbulent emotions" and helped him stave off the depression that afflicted several of his family members.
Academics often live an obsessive connection with the disciplines they practice and study. Certainly that sort of focus can can be bad for a person when taken too far. (Is it possible for an obsession not to go too far?) For me, though, the focus of studying something deeply, organizing its parts, and trying to communicate it to others through my courses and writing has always felt like a gift. The activity has healing properties all its own.
In any case, the name "The Society for the Diffusion of Useful Knowledge" made me smile. Reading has the power to heal, too.
Another way that attending a virtual conference is like the in-person experience: you can oversleep, or lose track of time. After a long morning of activity before the time-shifted start of Day 2, I took a nap before the 11:45 talk, and...
Talk 1: Python's Syntactic Sugar
Grrr. I missed the first two-thirds of this talk, which I greatly anticipated, but I slept longer than I planned. My body must have needed more than I was giving it.
I saw enough of the talk, though, to know I want to watch the video on YouTube when it shows up. This topic is one of my favorite topics in programming languages: What is the smallest set of features we need to implement the rest of the language? The speaker spent a couple of years implementing various Python features in terms of others, and arrived at a list of only ten that he could not translate away. The rest are sugar. I missed the list at the beginning of the talk, but I gathered a few of its members in the ten minutes I watched: while, raise, and try/except.
I love this kind of exercise: "How can you translate the statement if X: Y into one that uses only core features?" Here's one attempt the speaker gave:
try: while X: Y raise _DONE except _DONE: None
I was today days old when I learned that Python's bool subclasses int, that True == 1, and that False == 0. That bit of knowledge was worth interrupting my nap to catch the end of the talk. Even better, this talk was based on a series of blog posts. Video is okay, but I love to read and play with ideas in written form. This series vaults to the top of my reading list for the coming days.
Talk 2: Subclassing, Composition, Python, and You
Okay, so this guy doesn't like subclasses much. Fair enough, but... some of his concerns seem to be more about the way Python classes work (open borders with their super- and subclasses) than with the idea itself. He showed a lot of ways one can go wrong with arcane uses of Python subclassing, things I've never thought to do with a subclass in my many years doing OO programming. There are plenty of simpler uses of inheritance that are useful and understandable.
Still, I liked this talk, and the speaker. He was honest about his biases, and he clearly cares about programs and programmers. His excitement gave the talk energy. The second half of the talk included a few good design examples, using subclassing and composition together to achieve various ends. It also recommended the book Architecture Patterns with Python. I haven't read a new software patterns book in a while, so I'll give this one a look.
Toward the end, the speaker referenced the line "Software engineering is programming integrated over time." Apparently, this is a Google thing, but it was new to me. Clever. I'll think on it.
Talk 3: How We Are Making CPython Faster -- Past, Present and Future
I did not realize that, until Python 3.11, efforts to make the interpreter had been rather limited. The speaker mentioned one improvement made in 3.7 to optimize the typical method invocation, obj.meth(arg), and one in 3.8 that sped up global variable access by using a cache. There are others, but nothing systematic.
At this point, the talk became mutually recursive with the Friday talk "Inside CPython 3.11's New Specializing, Adaptive Interpreter". The speaker asked us to go watch that talk and return. If I were more ambitious, I'd add a link to that talk now, but I'll let you any of you are interested to visit yesterday's post and scroll down two paragraphs.
He then continued with improvements currently in the works, including:
He also mentions possible improvements related to C extension code, but I didn't catch the substance of this one. The speaker offered the audience a pithy takeaway from his talk: Python is always getting faster. Do the planet a favor and upgrade to the latest version as soon as you can. That's a nice hook.
There was lots of good stuff here. Whenever I hear compiler talks like this, I almost immediately start thinking about how I might work some of the ideas into my compiler course. To do more with optimization, we would have to move faster through construction of a baseline compiler, skipping some or all of the classic material. That's a trade-off I've been reluctant to make, given the course's role in our curriculum as a compilers-for-everyone experience. I remain tempted, though, and open to a different treatment.
Talk 4: The Lost Art of Diagrams: Making Complex Ideas Easy to See with Python
Early on, this talk contained a line that programmers sometimes need to remember: Good documentation shows respect for users. Good diagrams, said the speaker, can improve users' lives. The talk was a nice general introduction to some of the design choices available to us as we create diagrams, including the use of color, shading, and shapes (Venn diagrams, concentric circles, etc.). It then discussed a few tools one can use to generate better diagrams. The one that appealed most to me was Mermaid.js, which uses a Markdown-like syntax that reminded me of GraphViz's Dot language. My students and use GraphViz, so picking up Mermaid might be a comfortable task.
~~~~~
My second day at virtual PyCon confirmed that attending was a good choice. I've seen enough language-specific material to get me thinking new thoughts about my courses, plus a few other topics to broaden the experience. A nice break from the semester's grind.
There are several revised approaches to "what's the deal with the ring?" presented in "The History of The Lord of the Rings", and, as you read through the drafts, the material just ... slowly gets better! Bit by bit, the familiar angles emerge. There seems not to have been any magic moment: no electric thought in the bathtub, circa 1931, that sent Tolkien rushing to find a pen.
It was just revision.
Then:
... if Tolkien can find his way to the One Ring in the middle of the fifth draft, so can I, and so can you.
-- Robin Sloan, How The Ring Got Good
On Friday, I wrote a note to myself about updating an upcoming class session:
Clean out the old cruft. Simplify, simplify, simplify! I want students to grok the idea and the implementation. All that Lisp and Scheme history is fun for me, but it gets in the students' way.
This is part of an ongoing battle for me. Intellectually, I know to design class sessions and activities focused on where students are and what they need to do in order to learn. Yet it continually happens that I strike upon a good approach for a session, and then over the years I stick a little extra in here and there; within a few iterations I have a big ball of mud. Or I fool myself that some bit of history that I find fascinating is somehow essential for students to learn about, too, so I keep it in a session. Over the years, the history grows more and more distant; the needs of the session evolve, but I keep the old trivia in there, filling up time and distracting my students.
It's hard.
The specific case in question here is a session in my programming languages course called Creating New Syntax. The session has the modest goal of introducing students to the idea of using macros and other tools to define new syntactic constructs in a language. My students come into the course with no Racket or Lisp experience, and only a few have enough experience with C/C++ that they may have seen its textual macros. My plan for this session is to expose them to a few ideas and then to demonstrate one of Racket's wonderful facilities for creating new syntax. Given the other demands of the course, we don't have time to go deep, only to get a taste.
[In my dreams, I sometimes imagine reorienting this part of my course around something like Matthew Butterick's Beautiful Racket... Maybe someday.]
Looking at my notes for this session on Friday, I remembered just how overloaded and distracting the session has become. Over the years, I've pared away the extraneous material on macros in Lisp and C, but now it has evolved to include too many ideas and incomplete examples of macros in Racket. Each by itself might make for a useful part of the story. Together, they pull attention here and there without ever closing the deal.
I feel like the story I've been telling is getting in the way of the one or two key ideas about this topic I want students to walk away from the course with. It's time to clean the session up -- to make some major changes -- and tell a more effective story.
The specific idea I seized upon on Friday is an old idea I've had in mind for a while but never tried: adding a Python-like for-loop:
(for i in lst: (sqrt i))
[Yes, I know that Racket already has a fine set of for-loops! This is just a simple form that lets my students connect their fondness for Python with the topic at hand.]
This functional loop is equivalent to a Racket map expression:
(map (lambda (i) (sqrt i)) lst)
We can write a simple list-to-list translator that converts the loop to an equivalent map:
(define for-to-map (lambda (for-exp) (let ((var (second for-exp)) (lst (fourth for-exp)) (exp (sixth for-exp))) (list 'map (list 'lambda (list var) exp) lst))))
This code handles only the surface syntax of the new form. To add it to the language, we'd have to recursively translate the form. But this simple function alone demonstrates the idea of translational semantics, and shows just how easy it can be to convert a simple syntactic abstraction into an equivalent core form.
Racket, of course, gives us better options! Here is the same transformer using the syntax-rules operator:
(define-syntax for-p (syntax-rules (in :) ( (for-p var in lst : exp) (map (lambda (var) exp) lst) ) ))
So easy. So powerful. So clear. And this does more than translate surface syntax in the form of a Racket list; it enables the Racket language processor to expand the expression in place and execute the result:
> (for-p i in (range 0 10): (sqrt i)) '(0 1 1.4142135623730951 ... 2.8284271247461903 3)
This small example demonstrates the key idea about macros and syntax transformers that I want students to take from this session. I plan to open the session with for-p, and then move on to range-case, a more complex operator that demonstrates more of syntax-rules's range and power.
This sets me up for a fun session in a little over a week. I'm excited to see how it plays with students. Renewed simplicity and focus should help.
Robin Sloan speculates that language-learning models like ChatGPT have gone through a phase change in what they can accomplish.
AI at this moment feels like a mash-up of programming and biology. The programming part is obvious; the biology part becomes apparent when you see AI engineers probing their own creations the way scientists might probe a mouse in a lab.
Like so many people, I find my social media and blog feeds filled with ChatGPT and LLMs and DALL-E and ... speculation about what these tools mean for (1) the production of text and code, and (2) learning to write and program. A lot of that speculation is tinged with fear.
I admire Sloan's effort to be constructive in his approach to the uncertainty:
I've found it helpful, these past few years, to frame my anxieties and dissatisfactions as questions. For example, fed up with the state of social media, I asked: what do I want from the internet, anyway?
It turns out I had an answer to that question.
Where the GPT-alikes are concerned, a question that's emerging for me is:
What could I do with a universal function — a tool for turning just about any X into just about any Y with plain language instructions?
I admit that I am reacting to these developments slowly compared to many people. That's my style in most things: I am more likely to under-react to a change than to over-react, especially at the onset of the change. In this case, there is no chance of immediate peril, so waiting to see what happens as people use these tools seems like a reasonable reasonable. I haven't made any effort to use these tools actively (or even been moved to), so any speculating I do would be uninformed by personal experience.
Instead, I read as people whose work I respect experiment with these tools and try to make sense of them. Occasionally, I draw a small, tentative conclusion, such as that prompting these generators is a lot like prompting students. After a few months of reading and a little reflection, I still think the biggest risk we face is probably that we tend to change the world around us to accommodate our technologies. If we put these tools to work for us in ways that enhance what we do, then the accommodation will pay off. If not, then we may, as Daniel Steinberg wrote in one of his newsletters, stop asking the questions we want to ask and start asking only the questions these tools can answer.
Professionally, I think most about the effect that ChatGPT and its ilk will have on programming and CS education. In these regards, I've been paying special attention to reports from David Humphrey, such as this blog post on his university's attempt to grapple the widespread availability of these tools. David has approached OpenAI with an open mind and written thoughtfully about the promise and the risk. For example, he has written a lot of code with an LLM assistant and found it improving his ability both to write code and to think about problems. Advanced CS students can benefit from this kind of assistance, too, but David wonders how such tools might interfere with students first learning to program.
What do we educators want from generative programming tools anyway? What do I as a programmer and educator want from them?
So, at this point, my personal interaction with the phase change that Sloan describes has been mostly passive: I read about what others are doing and think about the results of their exploration. Perhaps this post is a guilty conscience asserting that I should be doing more. Really, though, I think of it more as an active form of inaction: an effort to collect some of my thinking as I slowly respond to the changes that are coming. Perhaps some day soon I will feel moved to use of these tools as I write code of my own. For now, though, I am content to watch from the sidelines. You can learn a lot just by watching.
In this episode of Conversations With Tyler, Cowen asks economist Jeffrey Sachs if he agrees with several other economists' bearish views on a particular issue. Sachs says they "have been wrong ... for 20 years", laughs, and goes on to say:
They just got it wrong time and again. They had failed to understand, and the same with [another economist]. It's the same story. It doesn't fit our model exactly, so it can't happen. It's got to collapse. That's not right. It's happening. That's the story of our time. It's happening.
"It doesn't fit our model, so it can't happen." But it is happening.
When your model keeps saying that something can't happen, but it keeps happening anyway, you may want to reconsider your model. Otherwise, you may miss the dominant story of the era -- not to mention being continually wrong.
Sachs spends much of his time with Cowen emphasizing the importance of context in determining which model to use and which actions to take. This is essential in economics because the world it studies is simply too complex for the models we have now, even the complex models.
I think Sachs's insight applies to any discipline that works with people, including education and software development.
The topic of education even comes up toward the end of the conversation, when Cowen asks Sachs how to "fix graduate education in economics". Sachs says that one of its problems is that they teach econ as if there were "four underlying, natural forces of the social universe" rather than studying the specific context of particular problems.
He goes on to highlight an approach that is affecting every discipline now touched by data analytics:
We have so much statistical machinery to ask the question, "What can you learn from this dataset?" That's the wrong question because the dataset is always a tiny, tiny fraction of what you can know about the problem that you're studying.
Every interesting problem is bigger than any dataset we build from it. The details of the problem matter. Again: context. Sachs suggests that we shouldn't teach econ like physics, with Maxwell's overarching equations, but like biology, with the seemingly arbitrary details of DNA.
In my mind, I immediately began thinking about my discipline. We shouldn't teach software development (or econ!) like pure math. We should teach it as a mix of principles and context, generalities and specific details.
There's almost always a tension in CS programs between timeless knowledge and the details of specific languages, libraries, and tools. Most of students don't go on to become theoretical computer scientists; they go out to work in the world of messy details, details that keep evolving and morphing into something new.
That makes our job harder than teaching math or some sciences because, like economics:
... we're not studying a stable environment. We're studying a changing environment. Whatever we study in depth will be out of date. We're looking at a moving target.
That dynamic environment creates a challenge for those of us teaching software development or any computing as practiced in the world. CS professors have to constantly be moving, so as not to fall our of date. But they also have to try to identify the enduring principles that their students can count on as they go on to work in the world for several decades.
To be honest, that's part of the fun for many of us CS profs. But it's also why so many CS profs can burn out after 15 or 20 years. A never-ending marathon can wear anyone out.
Anyway, I found Cowens' conversation with Jeffrey Sachs to be surprisingly stimulating, both for thinking about economics and for thinking about software.
(These days, I'm posting a bit more on Mastodon. It has a higher character limit than Twitter, so I sometimes write longer posts, including quoted passages. Those long posts start to blur in length and content with blog posts. In an effort to blog more, and to preserve writing that may have longer value than a social media post provides, I may start capturing threads there as blog posts here. This post originated as a Mastodon post last week.)
~~~~~
This post contains one of the reasons I tell prospective CS students to take their humanities and social science courses seriously:
In short, the key skill for making sense of the world of information is developing the ability to accurately and neutrally summarize some body of information in your own words.
The original poster responded that it wasn't until going back to pursue a master's degree in library and information science that this lesson hit home for him.
I always valued my humanities and social science courses, both because I enjoyed them and because they let me practice valuable skills that my CS and math courses didn't exercise. But this lesson hit home for me in a different way after I became a professor.
Over my years teaching, I've seen students succeed and struggle in a lot of different ways. The ability to read and synthesize information with facility is one of the main markers of success, one of the things that can differentiate between students who do well and those who don't. It's also hard to develop this skill after students get to college. Nearly every course and major depends on it, even technical courses, even courses and majors that don't foreground this kind of skill. Without it, students keep falling farther behind. It's hard to develop the skill quickly enough to not fall so far behind that success feels impossible.
So, kids and parents, when you ask how to prepare to succeed as a CS student while in high school, one of my answers will almost always be: take four years of courses in every core area, including English and social science. The skills you develop and practice there will pay off many-fold in college.
Today I received an email message similar to this:
I didn't do very well in my first semester, so I'm looking for ways to do better this time around. Do you have any ideas about study resources or tips for succeeding in CS courses?
As an advisor, I'm occasionally asked by students for advice of this sort. As department head, I receive even more queries, because early on I am the faculty member students know best, from campus visits and orientation advising.
When such students have already attempted a CS course or two, my first step is always to learn more about their situation. That way, I can offer suggestions suited to their specific needs.
Sometimes, though, the request comes from a high school student, or a high school student's parent: What is the best way to succeed as a CS student?
To be honest, most of the advice I give is not specific to a computer science major. At a first approximation, what it takes to succeed as a CS student is the same as what it takes to succeed as a student in any major: show up and do the work. But there are a few things a CS student does that are discipline-specific, most of which involve the tools we use.
I've decided to put together a list of suggestions that I can point our students to, and to which I can refer occasionally in order to refresh my mind. My advice usually includes one or all of these suggestions, with a focus on students at the beginning of our program:
That's what came to mind at the end of a Friday, at the end of a long week, when I sat down to give advice to one student. I'd love to hear your suggestions for improving the suggestions in my list, or other bits of advice that would help our students. Email me your ideas, and I'll make my list better for anyone who cares to read it.
Cory Doctorow, in a recent New Yorker interview reminisces about learning to program. The family had a teletype and modem.
My mom was a kindergarten teacher at the time, and she would bring home rolls of brown bathroom hand towels from the kid's bathroom at school, and we would feed a thousand feet of paper towel into the teletype and I would get a thousand feet of computing after school at the end of the day.
Two things:
The entire interview is a good read on the role of computing in modern society. The programmer in me also resonated with this quote from Doctorow's 2008 novel, Little Brother:
If you've never programmed a computer, there's nothing like it in the whole world. It's awesome in the truest sense: it can fill you with awe.
My older daughter recommended Little Brother to me when it first came out. I read many of her recommendations promptly, but for some reason this one sits on my shelf unread. (The PDF also sits in my to-read/ folder, unread.) I'll move it to the top of my list.
Ethan Mollick tells us how to generate prompts for programs like ChatGPT and DALL-E: give direct and detailed instructions.
Don't ask it to write an essay about how human error causes catastrophes. The AI will come up with a boring and straightforward piece that does the minimum possible to satisfy your simple demand. Instead, remember you are the expert and the AI is a tool to help you write. You should push it in the direction you want. For example, provide clear bullet points to your argument: write an essay with the following points: -Humans are prone to error -Most errors are not that important -In complex systems, some errors are catastrophic -Catastrophes cannot be avoided
But even the results from such a prompt are much less interesting than if we give a more explicit prompt. Fo instance, we might add:
use an academic tone. use at least one clear example. make it concise. write for a well-informed audience. use a style like the New Yorker. make it at least 7 paragraphs. vary the language in each one. end with an ominous note.
This reminds me of setting essay topics for students, either for long-form writing or for exams. If you give a bland uninteresting question, you will generally get a bland uninteresting answer. Such essays are hard to evaluate. A squooshy question allows the student to write almost anything in response. Students are usually unhappy in this scenario, too, because they don't know what you want them to write, or how they will be evaluated.
Asking a human a more specific question has downsides, though. It increases the cognitive load placed on them, because there are more things for them to be thinking about as they write. Is my tone right? Does this sound like the New Yorker? Did I produce the correct number of paragraphs? Is my essay factually accurate? (ChatGPT doesn't seem to worry about this one...) The tradeoff is clearer expectations. Many students prefer this trade, at least on longer form assignments when they have time to consider the specific requests. A good spec reduces uncertainty.
Maybe these AI programs are closer to human than we think after all. (Some people don't worry much about correctness either.)
~~~~
On a related note: As I wrote elsewhere, I refuse to call ChatGPT or any program "an AI". The phrase "artificial intelligence" is not a term for a specific agent; it is the name of an idea. Besides, none of these programs are I yet, only A.
This morning on the exercise bike, I read a big chunk of Daniel Gross and Tyler Talk Talent, from the Conversations with Tyler series. The focus of this conversation is how to identify talent, as prelude to the release of their book on that topic.
The bit I've read so far has been like most Conversations with Tyler: interesting ideas with Cowen occasionally offering an offbeat idea seemingly for the sake of being offbeat. For example, if the person he is interviewing has read Shakespeare, he might say,
"Well, my hypothesis is that in Romeo and Juliet, Romeo and Juliet don't actually love each other at all. Does the play still make sense?" Just see what they have to say. It's a way of testing their second-order understanding of situations, diversity of characters.
This is a bit much for my taste, but the motivating idea behind talking to people about drama or literature is sound:
It's not something they can prepare for. They can't really fake it. If they don't understand the topic, well, you can switch to something else. But if you can't find anything they can understand, you figure, well, maybe they don't have that much depth or understanding of other people's characters.
It seems to me that this style of interviewing runs a risk of not being equitable to all candidates, and at the very least places high demands on both the interviewee and the interviewer. That said, Gross summarizes the potential value of talking to people about movies, music, and other popular culture in interviews:
I think that works because you can learn a lot from what someone says -- they're not likely to make up a story -- but it's also fun, and it is a common thing many people share, even in this era of HBO and Netflix.
This exchange reminded me of perhaps my favorite interview of all time, one in which I occupied the hot seat.
I was a senior in high school, hoping to study architecture at Ball State University. (Actual architecture... the software thing would come later.) I was a pretty good student, so I applied for Ball State's Whitinger Scholarship, one of the university's top awards. My initial application resulted in me being invited to campus for a personal interview. First, I sat to write an essay over the course of an hour, or perhaps half an hour. To be honest, I don't remember many details from that part of the day, only sitting in a room by myself for a while with a blue book and writing away. I wrote a lot of essays in those days.
Then I met with Dr. Warren Vander Hill, the director of the Honors College, for an interview. I'd had a few experiences on college campuses in the previous couple of years, but I still felt a little out of my element. Though I came from a home that valued reading and learning, my family background was not academic.
On a shelf behind Dr. Vander Hill, I noticed a picture of him in a Hope College basketball jersey, dribbling during a college game. I casually asked him about it and learned that he had played Division III varsity ball as an undergrad. I just now searched online in hopes of confirming my memory and learned that he is still #8 on the list of Hope's all-time career scoring leaders. I don't recall him slipping that fact into our chat... (Back then, he would have been #2!)
Anyway, we started talking basketball. Somehow, the conversation turned to Oscar Robertson, one of the NBA's all-time great players. He starred at Indianapolis's all-black Crispus Attucks High School and led the school to a state championship in 1955. From there, we talked about a lot of things -- the integration of college athletics, the civil rights movement, the state of the world in 1982 -- but it all seemed to revolve around basketball.
The time flew. Suddenly, the interview period was over, and I headed home. I enjoyed the conversation quite a bit, but on the hour drive, I wondered if I'd squandered my chances at the scholarship by using my interview time to talk sports. A few weeks later, though, I received a letter saying that I had been selected as one of the recipients.
That was the beginning of four very good years for me. Maybe I can trace some of that fortune to a conversation about sports. I certainly owe a debt to the skill of the person who interviewed me.
I got to know Dr. Vander Hill better over the next four years and slowly realized that he had probably known exactly what he was doing in that interview. He had found a common interest we shared and used it to start a conversation that opened up into bigger ideas. I couldn't have prepared answers for this conversation. He could see that I wasn't making up a story, that I was genuinely interested in the issues we were discussing and was, perhaps, genuinely interesting. The interview was a lot of fun, for both of us, I think, and he learned a lot about me from just talking.
I learned a lot from Dr. Vander Hill over the years, though what I learned from him that day took a few years to sink in.
Someone on Mastodon posted a link to a 2021 survey of how the parsers for major languages are implemented. Are they written by hand, or automated by a parser generator? The answer was mixed: a few are generated by yacc-like tools (some of which were custom built), but many are written by hand, often for speed.
My two favorite notes:
Julia's parser is handwritten but not in Julia. It's in Scheme!
Good for the Julia team. Scheme is a fine language in which to write -- and maintain -- a parser.
Not only [is Clang's parser] handwritten but the same file handles parsing C, Objective-C and C++.
I haven't clicked through to the source code for Clang yet but, wow, that must be some file.
Finally, this closing comment in the paper hit close to the heart:
Although parser generators are still used in major language implementations, maybe it's time for universities to start teaching handwritten parsing?
I have persisted in having my compiler students write table-driven parsers by hand for over fifteen years. As I noted in this post at the beginning of the 2021 semester, my course is compilers for everyone in our major, or potentially so. Most of our students will not write another compiler in their careers, and traditional skills like implementing recursive descent and building a table-driven program are valuable to them more generally than knowing yacc or bison. Any of my compiler students who do eventually want to use a parser generator are well prepared to learn how, and they'll understand what's going on when they do, to boot.
My course is so old school that it's back to the forefront. I just had to be patient.
(I posted the seeds of this entry on Mastodon. Feel free to comment there!)
I'm trying to get back into the habit of writing here more regularly. In the early days of my blog, I posted quick snippets every so often. Here's a set to start 2023.
• Falsework
From A Bridge Over a River Never Crossed:
Funnily enough, traditional arch bridges were built by first having a wood framing on which to lay all the stones in a solid arch (YouTube). That wood framing is called falsework, and is necessary until the arch is complete and can stand on its own. Only then is the falsework taken away. Without it, no such bridge would be left standing. That temporary structure, even if no trace is left of it at the end, is nevertheless critical to getting a functional bridge.
Programmers sometimes write a function or an object that helps them build something else that they couldn't easily have built otherwise, then delete the bridge code after they have written the code they really wanted. A big step in the development of a student programmer is when they do this for the first time, and feel in their bones why it was necessary and good.
• Repair as part of the history of an object
From The Art of Imperfection and its link back to a post on making repair visible, I learned about Kintsugi, a practice in Japanese art...
that treats breakage and repair as part of the history of an object, rather than something to disguise.
I have this pattern around my home, at least on occasion. I often repair my backpack, satchel, or clothing and leave evidence of the repair visible. My family thinks it's odd, but figure it's just me.
Do I do this in code? I don't think so. I tend to like clean code, with no distractions for future readers. The closest thing to Kintsugi I can think of now are comments that mention where some bit of code came from, especially if the current code is not intuitive to me at the time. Perhaps my memory is failing me, though. I'll be on the watch for this practice as I program.
• "It is good to watch the master."
I've been reading a rundown of the top 128 tennis players of the last hundred years, including this one about Pancho Gonzalez, one of the great players of the 1940s, '50s, and '60s. He was forty years old when the Open Era of tennis began in 1968, well past his prime. Even so, he could still compete with the best players in the game.
Even his opponents could appreciate the legend in their midst. Denmark's Torben Ulrich lost to him in five sets at the 1969 US Open. "Pancho gives great happiness," he said. "It is good to watch the master."
The old masters give me great happiness, too. With any luck, I can give a little happiness to my students now and then.
Yesterday, Ben Fulton posted on Mastodon:
TIL: C++ has a mismatch algorithm that returns the first non-equal pair of elements from two sequences. ...
C++'s mismatch was new to me, too, so I clicked through to the spec on cppreference.com to read a bit more. I learned that mismatch is an algorithm implemented as as a template function with several different signatures. My thoughts turned immediately to my spring course, Programming Languages, which starts with an introduction to Racket and functional programming. mismatch would make a great example or homework problem for my students, as they learn to work with Racket lists and functions! I stopped working on what I was doing and used the C++ spec to draw up a family of functions for my course:
; Return the first mismatching pair of elements from two lists. ; Compare using eq?. ; (mismatch lst1 lst2) ; ; Compare using a given binary predicate comp?. ; (mismatch comp? lst1 lst2) ; ; Compare using a given binary predicate comp?, ; as a higher-order function. ; ((make-mismatch comp?) lst1 lst2) ; ; Return the first mismatching pair of elements from two ranges, ; also as a higher-order function. ; If last2 is not provided, it denotes first2 + (last1 - first1). ; (make-mismatch first1 last1 first2 [last2]) -> (f lst1 lst2)
Of course, this list is not exhaustive, only a start. With so many related possibilities, mismatch will make a great family of examples or homework problems for the course! What a fun distraction from the other work in my backlog.
Ben's post conveniently arrived in the middle of an email discussion with the folks who teach our intro course, about ChatGPT and the role it will play in Intro. I mentioned ChatGPT in a recent post suggesting that we all think about tools like ChatGPT and DALL-E from the perspective of cultural adaptation: how do we live with new AI tools knowing that we change our world to accommodate our technologies? In that post, I mentioned only briefly the effect that these tools will have on professors, their homework assignments, and the way we evaluate student competencies and performance. The team preparing to teach Intro this spring has to focus on these implications now because they affect how the course will work. Do we want to mitigate the effects of ChatGPT and, if so, how?
I think they have decided mostly to take a wait-and-see approach this semester. We always have a couple of students who do not write their own code, and ChatGPT offers them a new way not to do so. When we think students have not written the code they submitted, we talk with them. In particular, we discuss the code and ask the student to explain or reason about it.
Unless the presence of ChatGPT greatly increases the number of students submitting code they didn't write, this approach should continue to work. I imagine we will be fine. Most students want to learn; they know that writing code is where they learn the most. I don't expect that access to ChatGPT is going to change the number of students taking shortcuts, at least not in large numbers. Let's trust our students as we keep a watchful eye out for changes in behavior.
The connection between mismatch and the conversation about teaching lies in the role that a family of related functions such as mismatch can play in building a course that is more resistant to the use of AI assistants in a way that harms student learning. I already use families of related function specs as a teaching tool in my courses, for purely pedagogical reasons. Writing different versions of the same function, or seeing related functions used to solve slightly different problems, is a good way to help students deepen understanding of an idea or to help them make connections among different concepts. My mismatches give me another way to help students in Programming Languages learn about processing lists, passing functions as arguments, returning functions as values, and accepting a variable number of arguments. I'm curious to see how this family of functions works for students.
A set of related functions also offers a tool both for helping professors determine whether students have learned to write code. We already ask students in our intro course to modify code. Asking students to convert a function with one spec into a function with a slightly different spec, like writing different versions of the same function, give them the chance benefit from their understanding the existing code. It is easier for a programmer to modify a function if they understand it. The existing code is a scaffold that enables the student to focus on the single feature or concept they need to write the new code.
Students who have not written code like the code they are modifying have a harder time reading and modifying the given code, especially when operating under any time or resource limitation. In a way, code modification exercises do something simpler to asking students to explain code to us: the modification task exposes when students don't understand code they claim to have written.
Having ChatGPT generate a function for you won't be as valuable if you will soon be asked to explain the code in detail or to modify the code in a way that requires you understand it. Increasing the use of modification tasks is one way to mitigate the benefits of a student having someone else write the code for them. Families of functions such as mismatch above are a natural source of modification tasks.
Beyond the coming semester, I am curious how our thinking about writing code will evolve in the presence of ChatGPT-like tools. Consider the example of auto-complete facilities in our editors. Few people these days think of using auto-complete as cheating, but when it first came out many professors were concerned that using auto-complete was a way for students not to learn function signatures and the details of standard libraries. (I'm old enough to still have a seed of doubt about auto-complete buried somewhere deep in my mind! But that's just me.)
If LLM-based tools become the new auto-complete, one level up from function signatures, then how we think about programming will probably change. Likewise how we think about teaching programming... or not. Did we change how we teach much as a result of auto-complete?
The existence of ChatGPT is a bit disconcerting for today's profs, but the long-term implications are kind of interesting.
In the meantime, coming across example generators like C++'s mismatch helps me deal with the new challenge and gives me unexpected fun writing code and problem descriptions.
A recent issue of Ted Gioia's newsletter asks, What can we learn from Barnes & Noble's surprising turnaround? In one sentence, the answer is:
If you want to sell books, you must love those books.
Perhaps we can apply Barnes & Noble's lesson to education.
If anything will save the mid-sized comprehensive university in the face of changing demographics and state funding, it will likely be this:
If you want to teach students, you must love both teaching and students.
Comprehensive universities (regional universities that focus on undergraduate education) are caught in the middle between large research-focused schools and small, mostly private schools. They try to offer the best of both worlds, without having the resources that buttress those other school's operation. The big research schools have external research funding, large media contracts for their athletics programs, and primacy of place in the minds of potential students. The small private schools offer the "small school experience", often to targeted audiences of students and often with considerable endowments and selective admissions that heighten the appeal.
Mid-sized comprehensives are unsung jewels in many states, but economic changes make it harder to serve their mission than it was forty or even twenty years ago. They don't have much margin for error. What are they to do? As Barnes & Noble is demonstrating, the key to success for a bookstore is to put books and readers first. For the comprehensives, the key to success is almost certainly to put students and teaching first.
Other lessons from the Barnes & Noble turnaround may help, too. For example, in tough economic times, universities tend to centralize resources and decision making, in the name of focus and efficiency. However, decentralization empowers those closest to the students to meet the needs of the students in each academic disciplines. When given the chance, faculty and staff in the academic departments need to take this responsibility seriously. But then, most faculty are at comprehensives precisely because they want to work with undergraduates. The key element to it all, though, is putting students and teaching first, and everything else second.
This passage is from Lewis Thomas's The Lives of a Cell, in the essay "On Societies as Organisms":
The system of communications used in science should provide a neat, workable model for studying mechanisms of information-building in human society. Ziman, in a recent "Nature" essay, points out, "the invention of a mechanism for the systematic publication of fragments of scientific work may well have been the key event in the history of modern science." He continues:A regular journal carries from one research worker to another the various ... observations which are of common interest. ... A typical scientific paper has never pretended to be more than another little piece in a larger jigsaw -- not significant in itself but as an element in a grander scheme. The technique of soliciting many modest contributions to the store of human knowledge has been the secret of Western science since the seventeenth century, for it achieves a corporate, collective power that is far greater than any one individual can exert [italics mine].
In the 21st century, sites like arXiv lowered the barrier to publishing and reading the work of other scientists further. So did blogs, where scientists could post even smaller, fresher fragments of knowledge. Blogs also democratized science, by enabling scientists to explain results for a wider audience and at greater length than journals allow. Then came social media sites like Twitter, which made it even easier for laypeople and scientists in other disciplines to watch -- and participate in -- the conversation.
I realize that this blog post quotes an essay that quotes another essay. But I would never have seen the Ziman passage without reading Lewis. Perhaps you would not have seen the Lewis passage without reading this post? When I was in college, the primary way I learned about things I didn't read myself was by hearing about them from classmates. That mode of sharing puts a high premium on having the right kind of friends. Now, blogs and social media extend our reach. They help us share ideas and inspirations, as well as helping us to collaborate on science.
~~~~
I first mentioned The Lives of a Cell a couple of weeks ago, in If only ants watched Netflix.... This post may not be the last to cite the book. I find something quotable and worth further thought every few pages.
My social media feeds are full of ChatGPT screenshots and speculation these days, as they have been with LLMs and DALL-E and other machine learning-based tools for many months. People wonder what these tools will mean for writers, students, teachers, artists, and anyone who produces ordinary text, programs, and art.
These are natural concerns, given their effect on real people right now. But if you consider the history of human technology, they miss a bigger picture. Technologies often eliminate the need for a certain form of human labor, but they just as often create a new form of human labor. And sometimes, they increase the demand for the old kind of labor! If we come to rely on LLMs to generate text for us, where will we get the text with which to train them? Maybe we'll need people to write even more replacement-level prose and code!
As Robin Sloan reminds us in the latest edition of his newsletter, A Year of New Avenues, we redesign the world to fit the technologies we create and adopt.
Likewise, here's a lesson from my work making olive oil. In most places, the olive harvest is mechanized, but that's only possible because olive groves have been replanted to fit the shape of the harvesting machines. A grove planted for machine harvesting looks nothing like a grove planted for human harvesting.
Which means that our attention should be on how programs like GPT-2 might lead us to redesign the world we live and work in better to accommodate these new tools:
For me, the interesting questions sound more likeThat last question will, on the timescale of decades, turn out to be the most consequential, by far. Think of cars ... and of how dutifully humans have engineered a world just for them, at our own great expense. What will be the equivalent, for AI, of the gas station, the six-lane highway, the parking lot?
- What new or expanded kinds of human labor might AI systems demand?
- What entirely new activities do they suggest?
- How will the world now be reshaped to fit their needs?
Many professors worry that ChatGPT makes their homework assignments and grading rubrics obsolete, which is a natural concern in the short run. I'm old enough that I may not live to work in a world with the AI equivalent of the gas station, so maybe that world seems too far in the future to be my main concern. But the really interesting questions for us to ask now revolve around how tools such as these will lead us to redesign our worlds to accommodate and even serve them.
Perhaps, with a little thought and a little collaboration, we can avoid engineering a world for them at our own great expense. How might we benefit from the good things that our new AI technologies can provide us while sidestepping some of the highest costs of, say, the auto-centric world we built? Trying to answer that question is a better long-term use of our time and energy that fretting about our "Hello, world!" assignments and advertising copy.
Most people, when they become teachers, tell themselves that they won't do all the annoying things that their teachers did. If they teach for very long, though, they almost all find themselves slipping back to practices they didn't like as a student but which they now understand from the other side of the fence. Dynomight has written a nice little essay explaining why. Like deadlines. Why have deadlines? Let students learn and work at their own pace. Grade what they turn in, and let them re-submit their work later to demonstrate their newfound learning.
Indeed, why not? Because students are clever and occasionally averse to work. A few of them will re-invent a vexing form of the ancient search technique "Generate and Test". From the essay:
- Write down some gibberish.
- Submit it.
- Make a random change, possibly informed by feedback on the last submission.
- Resubmit it. If the grade improved, keep it, otherwise revert to the old version.
- Goto 3.
You may think this is a caricature, but I see this pattern repeated even in the context of weekly homework assignments. A student will start early and begin a week-long email exchange where they eventually evolve a solution that they can turn in when the assignment is due.
I recognize that these students are responding in a rational way to the forces they face: usually, uncertainty and a lack of the basic understanding needed to even start the problem. My strategy is to try to engage them early on in the conversation in a way that helps them build that basic understanding and to quiet their uncertainty enough to make a more direct effort to solve the problem.
Why even require homework? Most students and teachers want for grades to reflect the student's level of mastery. If we eliminate homework, or make it optional, students have the opportunity to demonstrate their mastery on the final exam or final project. Why indeed? As the essay says:
But just try it. Here's what will happen:
- Like most other humans, your students will be lazy and fallible.
- So many of them will procrastinate and not do the homework.
- So they won't learn anything.
- So they will get a terrible grade on the final.
- And then they will blame you for not forcing them to do the homework.
Again, the essay is written in a humorous tone that exaggerates the foibles and motivations of students. However, I have been living a variation of this pattern in my compilers course over the last few years. Here's how things have evolved.
I assign the compiler project as six stages of two weeks each. At the end of the semester, I always ask students for ways I might improve the course. After a few years teaching the course, students began to tell me that they found themselves procrastinating at the start of each two-week cycle and then having to scramble in the last few days to catch up. They suggested I require future students to turn something in at the end of the first week, as a way to get them started working sooner.
I admired their self-awareness and added a "status check" at the midpoint of each two-week stage. The status check was not to be graded, but to serve as a milepost they could aim for in completing that cycle's work. The feedback I provided, informal as it was, helped them stay course, or get back on course, if they had missed something important.
For several years, this approach worked really well. A few teams gamed the system, of course (see generate-and-test above), but by and large students used the status checks as intended. They were able to stay on track time-wise and to get some early feedback that helped them improve their work. Students and professor alike were happy.
Over the last couple of years, though, more and more teams have begun to let the status checks slide. They are busy, overburdened in other courses or distracted by their own projects, and ungraded work loses priority. The result is exactly what the students who recommended the status checks knew would happen: procrastination and a mad scramble in the last few days of the stage. Unfortunately, this approach can lead a team to fall farther and farther behind with each passing stage. It's hard to produce a complete working compiler under these conditions.
Again, I recognize that students usually respond in a rational way to the forces they face. My job now is to figure out how we might remove those forces, or address them in a more productive way. I've begun thinking about alternatives, and I'll be debriefing the current offering of the course with my students over the next couple of weeks. Perhaps we can find something that works better for them.
That's certainly my goal. When a team succeeds at building a working compiler, and we use it to compile and run an impressive program -- there's no feeling quite as joyous for a programmer, or a student, or a professor. We all want that feeling.
Anyway, check out the full essay for an entertaining read that also explains quite nicely that teachers are usually responding in a rational way to the forces they face, too. Cut them a little slack.
From Matthew Butterick, of Beautiful Racket fame:
I always think about—and encourage others to think about—how design and typography can complement the underlying object by finding a way to communicate its virtues. If your writing contains beautiful ideas, then your presentation of that writing should be likewise beautiful.
I am old-fashioned, perhaps, in feeling this way about software. A program that embodies beautiful ideas should itself be beautifully designed, and beautifully presented. The universe calls us in this direction.
The above passage comes from a charming essay on how Butterick ended up living a life of typography, in addition to his skills as a programmer and a lawyer. Spoiler: the title "Power, Corruption & Lies" refers not to any political intrigue but to an album by the rock band New Order.
Butterick is also one of the principals pursuing action against GitHub Copilot for violating the terms of the open-source licenses on the software it mined to build the tool. Sometimes, programmers have to deal with things less beautiful than the code we like to write, in order to protect that code and its creators.
From Robin Sloan Sloan's newsletter:
There was a book I wanted very badly to write; a book I had been making notes toward for nearly ten years. (In my database, the earliest one is dated December 13, 2013.) I had not, however, set down a single word of prose. Of course I hadn't! Many of you will recognize this feeling: your "best" ideas are the ones you are most reluctant to realize, because the instant you begin, they will drop out of the smooth hyperspace of abstraction, apparate right into the asteroid field of real work.
I can't really say that there is a book I want very badly to write. In the early 2000s I worked with several colleagues on elementary patterns, and we brainstormed writing an intro CS textbook built atop a pattern language. Posts from the early days of this blog discuss some of this work from ChiliPLoP, I think. I'm not sure that such a textbook could ever have worked in practice, but I think writing it would have been a worthwhile experience anyway, for personal growth. But writing such a book takes a level of commitment that I wasn't able to make.
That experience is one of the reasons I have so much respect for people who do write books.
While I do not have a book for which I've been making notes in recent years, I do recognize the feeling Sloan describes. It applies to blog posts and other small-scale writing. It also applies to new courses one might create, or old courses one might reorganize and teach in a very different way.
I've been fortunate to be able to create and re-create many courses over my career. I also have some ideas that sit in the back of my mind because I'm a little concerned about the commitment they will require, the time and the energy, the political wrangling. I'm also aware that the minute I begin to work on them, they will no longer be perfect abstractions in my mind; they will collide with reality and require compromises and real work.
(TIL I learned the word "apparate". I'm not sure how I feel about it yet.)
I've been meaning to write a post about my fall compilers course since the beginning of the semester but never managed to set aside time to do anything more than jot down a few notes. Now we are at the end of Week 9 and I just must write. Long-time readers know what motivates me most: a fun program to write in my student's source language!
TFW you run across a puzzle and all you want to do now is write a program to solve it. And teach your students about the process.
-- https://twitter.com/wallingf/status/1583841233536884737
Yesterday, it wasn't a puzzle so much as discovering a new kind of number, Carmichael numbers. Of course, I didn't discover them (neither did Carmichael, though); I learned of them from a Quanta article about a recent proof about these numbers that masquerade as primes. One way of defining this set comes from Korselt:
A positive composite integer n is a Carmichael number if and only if it has multiple prime divisors, no prime divisor repeats, and for each prime divisor p, p-1 divides n-1.
This definition is relatively straightforward, and I quickly imagined am imperative solution with a loop and a list. The challenge of writing a program to verify a number is a Carmichael number in my compiler course's source language is that it has neither of these things. It has no data structures or even local variables; only basic integer and boolean arithmetic, if expressions, and function calls.
Challenge accepted. I've written many times over the years about the languages I ask my students to write compilers for and about my adventures programming in them, from Twine last year through Flair a few years ago to a recurring favorite, Klein, which features prominently in popular posts about Farey sequences and excellent numbers.
This year, I created a new language, Graphene, for my students. It is essentially a small functional subset of Google's new language Carbon. But like it's predecessors, it is something of an integer assembly language, fully capable of working with statements about integers and primes. Korselt's description of Carmichael numbers is right in Graphene's sweet spot.
As I wrote in the post about Klein and excellent numbers, my standard procedure in cases like this is to first write a reference program in Python using only features available in Graphene. I must do this if I hope to debug and test my algorithm, because we do not have a working Graphene compiler yet! (I'm writing my compiler in parallel with my students, which is one of the key subjects in my phantom unwritten post.) I was proud this time to write my reference program in a Graphene-like subset of Python from scratch. Usually I write a Pythonic solution, using loops and variables, as a way to think through the problem, and then massage that code down to a program using a limited set of concepts. This time, I started with short procedural outline:
# walk up the primes to n # - find a prime divisor p: # - test if a repeat (yes: fail) # - test if p-1 divides n-1 (no : fail) # return # prime divisors > 1and then implemented it in a single recursive function. The first few tests were promising. My algorithm rejected many small numbers not in the set, and it correctly found 561, the smallest Carmichael number. But when I tested all the numbers up to 561, I saw many false positives. A little print-driven debugging found the main bug: I was stopping too soon in my search for prime divisors, at sqrt(n), due to some careless thinking about factors. Once I fixed that, boom, the program correctly handled all n up to 3000. I don't have a proof of correctness, but I'm confident the code is correct. (Famous last words, I know.)
As I tested the code, it occurred to me that my students have a chance to one-up standard Python. Its rather short standard stack depth prevented my program from reaching even n=1000. When I set sys.setrecursionlimit(5000), my program found the first five Carmichael numbers: 561, 1105, 1729, 2465, and 2821. Next come 6601 and 8911; I'll need a lot more stack frames to get there.
All those stack frames are unnecessary, though. My main "looping" function is beautifully tail recursive: two failure cases, the final answer case checking the number of prime divisors, and two tail-recursive calls that move either to the next prime as potential factor or to the next quotient when we find one. If my students implement proper tail calls -- an optimization that is optional in the course but encouraged by their instructor with gusto -- then their compiler will enable us to solve for values up to the maximum integer in the language, 231-1. We'll be limited only by the speed of the target language's VM, and the speed of the target code the compiler generates. I'm pretty excited.
Now I have to resist the urge to regale my students with this entire story, and with more details about how I approach programming in a language like Graphene. I love to talk shop with students about design and programming, but our time is limited... My students are already plenty busy writing the compiler that I need to run my program!
This lark resulted in an hour or so writing code in Python, a few more minutes porting to Graphene, and an enjoyable hour writing this blog post. As the song says, it was a good day.
In The Orchid Thief, there is a passage where author Susan Orlean describes a drive across south Florida on her way to a state preserve, where she'll be meeting an orchid hunter. She ends the passage this way:
The land was marble-smooth and it rolled without a pucker to the horizon. My eyes grazed across the green band of ground and the blue bowl of sky and then lingered on a dead tire, a bird in flight, an old fence, a rusted barrel. Hardly any cars came toward me, and I saw no one in the rearview mirror the entire time. I passed so many vacant acres and looked past them to so many more vacant acres and looked ahead and behind at the empty road and up at the empty sky; the sheer bigness of the world made me feel lonely to the bone. The world is so huge that people are always getting lost in it. There are too many ideas and things and people, too many directions to go. I was starting to believe that the reason it matters to care passionately about something is that it whittles the world down to a more manageable size. It makes the world seem not huge and empty but full of possibility. If I had been an orchid hunter I wouldn't have see this space as sad-making and vacant--I think I would have seen it as acres of opportunity where the things I loved were waiting to be found.
John Laroche, the orchid hunter at the center of The Orchid Thief, comes off as obsessive, but I think many of us know that condition. We have found an idea or a question or a problem that grabs our attention, and we work on it for years. Sometimes, we'll follow a lead so far down a tunnel that it feels a lot like the swamps Laroche braves in search of the ghost orchid.
Even a field like computer science is big enough that it can feel imposing if a person doesn't have a specific something to focus their attention and energy on. That something doesn't have to be forever... Just as Laroche had cycled through a half-dozen obsessions before turning his energy to orchids, a computer scientist can work deeply in an area for a while and then move onto something else. Sometimes, there is a natural evolution in the problems one focuses on, while other times people choose to move into a completely different sub-area. I see a lot of people moving into machine learning these days, exploring how it can change the sub-field they used to focus exclusively on.
As a prof, I am fortunate to be able to work with young adults as they take their first steps in computer science. I get to watch many of them find a question they want to answer, a problem they want to work on for a few years, or an area they want to explore in depth until they master it. It's also sad, in a way, to work with a student who never quite finds something that sparks their imagination. A career in software, or anything, really, can look as huge and empty as Orlean's drive through south Florida if someone doesn't care deeply about something. When they do, the world seems not huge and empty, but full of possibility.
I'm about halfway through The Orchid Thief and am quite enjoying it.
![]() |
Yesterday afternoon I attended a Teaching with Patterns workshop that is part of PLoP 2022.
When the pandemic hit in 2020, PLoP went virtual, as did so many conferences. It was virtual last year and will be again this year as well. On the spur of the moment, I attended a couple of the 2020 keynote talks online. Without a real commitment to the conference, though I let my day-to-day teaching and admin keep me from really participating. (You may recall that I did it right for StrangeLoop last year, as evidenced by several blog posts ending here.)
When I peeked in on PLoP virtually a couple of years ago, it had already been too many years away from my last PLoP, which itself came after too many years away! Which is to say: I miss PLoP and my friends and colleagues in the software patterns world.
PLoP 2022 itself is in October. Teaching with Patterns was a part of PLoPourri, a series of events scheduled throughout the year in advance of the conference proper. It was organized by Michael Weiss of Carleton University in Ottawa, a researcher on open source software.
Upon joining the Zoom session for the workshop, I was pleased to see Michael, a colleague I first met at PLoP back in the 1990s, and Pavel Hruby, whom I met at one of the first ChiliPLoPs many years ago. There were a handful of other folks participating, too, bringing experience from a variety of domains. One, Curt McNamara, has significant experience using patterns in his course on sustainable graphic design.
The workshop was a relaxed affair. First, participants shared their experiences teaching with patterns. Then we all discussed our various approaches and shared lessons we have learned. Michael gave a few opening remarks and then asked four questions to prime the conversation:
My answers may sound familiar to anyone who has read my blog long enough to hear me talk about the courses I teach and, going way back, to my work at PLoP and ChiliPLoP.
I use patterns to teach design techniques and trade-offs. It's easy to to lecture students on particular design solutions, but that's not very effective at helping students learn how to do design, to make choices based on the forces at play in any given problem. Patterns help me to think about the context in which a technique applies, the tradeoffs it helps us to make, and the state of their solution.
I rarely have students read patterns themselves, at least in one of the stylized pattern forms. Instead, I integrate the patterns into the stories I tell, into the conversations I have with my students about problems and solutions, and finally into the session notes I write up for them and me.
These days, I mostly teach courses on programming languages and compilers. I have written my own patterns for the programming languages course and use them to structure two units, on structurally recursive programming and closures for managing state. I've long dreamed of writing more patterns for the compiler course, which seems primed for the approach: So many of the structures we build when writing a compiler are the result of decades of experience, with tradeoffs that are often left implicit in textbooks and in the heads of experts.
I have used my own patterns as well as patterns from the literature when teaching other courses as well, back in the days when I taught three courses a semester. When I taught knowledge-based systems every year, I used patterns that I wrote to document the "generic task" approach to KBS in which my work lived. In our introductory OOP courses, I tended to use patterns from the literature both as a way to teach design techniques and trade-offs and as way to prepare students to graduate into a world where OO design patterns were part of the working vocabulary.
I like how patterns help me discuss code with students and how they help me evaluate solutions -- and communicate how I evaluate solutions.
That is more information than I shared yesterday... This was only a one and a half-hour workshop, and all of the participants had valuable experience to share.
I didn't take notes on what others shared or on the conversations that followed, but I did note a couple of things in general. Several people use a set of patterns or even a small pattern language to structure their entire course. I have done this at the unit level of a course, never at the scale of the course. My compiler course would be a great candidate here, but doing so would require that I write a lot of new material to augment and replace some of what I have now.
Likewise, a couple of participants have experimented with asking students to write patterns themselves. My teaching context is different than theirs, so I'm not sure this is something that can work for me in class. I've tried it a time or two with master's-level students, though, and it was educational for both the student and me.
I did grab all the links posted in the chat room so that I can follow up on them to read more later:
I also learned one new tech thing at the workshop: Padlet, a real-time sharing platform that runs in the browser. Michael used a padlet as the workshop's whiteboard, in lieu of Zoom's built-in tool (which I've still never used). The padlet made it quite straightforward to make and post cards containing text and other media. I may look into it as a classroom tool in the future.
Padlet also made it pretty easy to export the whiteboard in several formats. I grabbed a PDF copy of the workshop whiteboard for future reference. I considered linking to that PDF here, but I didn't get permission from the group first. So you'll just have to trust me.
Attending this workshop reminded me that I do not get to teach enough, at least not formal classroom teaching, and I miss it! As I told Michael in a post-workshop thank-you e-mail:
That was great fun, Michael. Thanks for organizing! I learned a few things, but mostly it is energizing simply to hear others talk about what they are doing."Energizing" really is a good word for what I experienced. I'm hyped to start working in more detail on my fall course, which starts in less than four weeks.
I'm also energized to look into the remaining PLoPourri events between now and the conference. (I really wish now that I had signed up for The Future of Education workshop in May, which was on "patterns for reshaping learning and the campus".) PLoP itself is on a Monday this year, in late October, so there is a good chance I can make it as well.
I told Michael that Teaching with Patterns would be an excellent regular event at PLoP. He seemed to like that thought. There is a lot of teaching experience out there to be shared -- and a lot of energy to be shared as well.
Morgan Housel's recent piece on experts trying too hard includes a sentence that made me think of what I teach:
A doctor once told me the biggest thing they don't teach in medical school is the difference between medicine and being a doctor — medicine is a biological science, while being a doctor is often a social skill of managing expectations, understanding the insurance system, communicating effectively, and so on.
Most of the grads from our CS program go on to be software developers or to work in software-adjacent jobs like database administrator or web developer. Most of the rest work in system administration or networks. Few go on to be academic computer scientists. As Housel's doctor knows about medicine, there is a difference between academic computer science and being a software developer.
The good news is, I think the CS profs at many schools are aware of this, and the schools have developed computer science programs that at least nod at the difference in their coursework. The CS program at my school has a course on software engineering that is more practical than theoretical, and another short course that teaches practical tools such as version control, automated testing, and build tools, and skills such as writing documentation. All of our CS majors complete a large project course in which students work in teams to create a piece of software or a secure system, and the practical task of working as a team to deliver a piece of working software is a focus of the course. On top of those courses, I think most of our profs try to keep their courses real for students they know will want to apply what they learn in Algorithms, say, or Artificial Intelligence in their jobs as developers.
Even so, there is always a tension in classes between building foundational knowledge and building practical skills. I encounter this tension in both Programming Languages and Translation of Programming Languages. There are a lot of cool things we could learn about type theory, some of which might turn out to be quite useful in a forty-year career as a developer. But any time we devote to going deeper on type theory is time we can't devote to the concrete languages skills of a software developer, such as learning and creating APIs or the usability of programming languages.
So, we CS profs have to make design trade-offs in our courses as we try to balance the forces of students learning computer science and students becoming software developers. Fortunately, we learn a little bit about recognizing, assessing, and making trade-offs in our work both as computer scientists and as programmers. That doesn't make it easy, but at least we have some experience for thinking about the challenge.
The sentence quoted above reminds me that other disciplines face a similar challenge. Knowing computer science is different from being a software developer, or sysadmin. Knowing medicine is different from being a doctor. And, as Housel explains so well in his own work, knowing finance is different from being an investor, which is usually more about psychology and self-awareness than it is about net present value or alpha ratios. (The current stock market is a rather harsh reminder of that.)
Thanks to Housel for the prompt. The main theme of his piece — that experts makes mistakes that novices can't make, which leads to occasional unexpected outcomes — is the topic for another post.
I recently ran across an old post by @joepolitz, Beginner REPL Stumbles, that records some of the ways he has observed students struggle as they learn to use IDEs with REPLs and files. As I mentioned on Twitter, it struck a chord with me even though I don't teach beginning programmers much these days. My tweets led to a short conversation that I'd like to record, and slightly expoand on, here.
I've noticed the first of the struggles in my junior-level Programming Languages class: students not knowing, or not taking seriously enough, the value of ctrl-up to replay and edit a previous interaction. If students cannot work effectively in the REPL, they will resort to typing all of their code in the definitions pane and repeatedly re-running it. This programming style misses out on the immense value of the REPL as a place to evolve code rapidly, with continual feedback, on the way to becoming a program.
As recommended in the post, I now demonstrate ctrl-up early in the course and note whenever I use it in a demo. If a student finds that their keyboard maps ctrl-up to another behavior, I show them how to define a shortcut in Preferences. This simple affordance can have an inordinate effect on the student's programming experience.
The other observations that Politz describes may be true for my students, too, and I just don't see them. My students are juniors and seniors who already have a year of experience in Python and perhaps a semester using Java. We aren't in the lab together regularly. I usually hear about their struggles with content when they ask questions, and when they do, they don't usually ask about process or tools. Sometimes, they will demo some interaction for me and I'll get to see an unexpected behavior in usage and help them, but that's rare.
(I do recall a student coming into my office once a few years ago and opening up a source file -- in Word. They said they had never gotten comfortable with Dr. Racket and that Word helped them make progress typing and editing code faster. We talked about ways to learn and practice Dr. Racket, but I don't think they ever switched.)
Having read about some of the usage patterns that Politz reports, I think I need to find ways to detect misunderstandings and difficulties with tools sooner. The REPL, and the ability to evolve code from interactions in the REPL into programs in the definitions pane, are powerful tools -- if one groks them and learns to use them effectively. As Politz notes, direct instruction is a necessary antidote to address these struggles. Direct instruction up front may also help my students get off to a better start with the tools.
There is so much room for improvement hidden inside assumptions that are baked into our current tools and languages. Observing learners can expose things we never think about, if we pay attention. I wonder what else I have been missing...
Fortunately, both Joe Politz and Shriram Krishnamurthi encountered my tweets. Krishnamurthi provided helpful context, noting that the PLT Scheme team noticed many of these issues in the early days of Dr. Scheme. They noticed others while running teacher training sessions for @Bootstrapworld. In both cases, instructors were in the lab with learners while they used the tools. In the crush to fix more pressing problems, the interaction issues went unaddressed. In my experience, they are also subtle and hard to appreciate fully without repeated exposure.
Politz provided a link to a workshop paper on Repartee, a tool that explicitly integrates interactive programming and whole-program editing. Very cool. As Krishnamurthi noted to close the conversation, Repartee demonstrates that we may be able to do better than simply teach students to use a REPL more effectively. Perhaps we can make better tools.
I've been learning a lot about CS education research the last few years. It is so much more than the sort of surface-level observations and uncontrolled experiments I saw, and made, at the beginning of my career. This kind of research demands a more serious commitment to science but offers the potential of real improvement in return for the effort. I'm glad to know CS ed researchers are making that commitment. I hope to help where I can.
In my previous post, I wrote joyously of a fun bit of programming: implementing ordered pairs using sets.
Alas, there was a bug in my solution. Thanks to Carl Friedrich Bolz-Tereick for finding it so quickly:
Heh, this is fun, great post! I wonder what happens though if a = b? Then the set is {{a}}. first should still work, but would second fail, because the set difference returns the empty set?
Carl Friedrich had found a hole in my small set of tests, which sufficed for my other implementations because the data structures I used separate cells for the first and second parts of the pair. A set will do that only if the first and second parts are different!
Obviously, enforcing a != b is unacceptable. My first code thought was to guard second()'s behavior:
if my formula finds a result then return that result else return (first p)
This feels like a programming hack. Furthermore, it results in an impure implementation: it uses a boolean value and an if expression. But it does seem to work. That would have to be good enough unless I could find a better solution.
Perhaps I could use a different representation of the pair. Helpfully, Carl Friedrich followed up with pointers to several blog posts by Mark Dominus from last November that looked at the set encoding of ordered pairs in some depth. One of those posts taught me about another possibility: Wiener pairs. The idea is this:
(a,b) = { {{a},∅}, {{b}} }
Dominus shows how Wiener pairs solve the a == b edge case in Kuratowski pairs, which makes it a viable alternative.
Would I ever have stumbled upon this representation, as I did onto the Kuratowski pairs? I don't think so. The representation is more complex, with higher-order sets. Even worse for my implementation, the formulas for first() and second() are much more complex. That makes it a lot less attractive to me, even if I never want to show this code to my students. I myself like to have a solid feel for the code I write, and this is still at the fringe of my understanding.
Fortunately, as I read more of Dominus's posts, I found there might be a way to save my Kuratowski-style solution. It turns out that the if expression I wrote above parallels the set logic used to implement a second() accessor for Kuratowski pairs: a choice between the set that works for a != b pairs and a fallback to a one-set solution.
From this Dominus post, we see the correct set expression for second() is:
... which can be simplified to:
The latter expression is useful for reasoning about second(), but it doesn't help me implement the function using set operations. I finally figured out what the former equation was saying: if (∪ p) is same as (∩ p), then the answer comes from (∩ p); otherwise, it comes from their difference.
I realized then that I could not write this function purely in terms of set operations. The computation requires the logic used to make this choice. I don't know where the boundary lies between pure set theory and the logic in the set comprehension, but a choice based on a set-empty? test is essential.
In any case, I think I can implement the my understanding of the set expression for second() as follows. If we define union-minus-intersection as:
(set-minus (apply set-union aSet) (apply set-intersect aSet))then:
(second p) = (if (set-empty? union-minus-intersection) (set-elem (apply set-intersect aSet)) (set-elem union-minus-intersection))
The then clause is the same as the body of first(), which must be true: if the union of the sets is the same as their intersection, then the answer comes from the interesection, just as first()'s answer does.
It turns out that this solution essentially implements my first code idea above: if my formula from the previous blog entry finds a result, then return that result. Otherwise, return first(p). The circle closes.
Success! Or, I should: Success!(?) After having a bug in my original solution, I need to stay humble. But I think this does it. It passes all of my original tests as well as tests for a == b, which is the main edge case in all the discussions I have now read about set implementations of pairs. Here is a link to the final code file, if you'd like to check it out. I include the two simple test scenarios, for both a == b and a == b, as Rackunit tests.
So, all in all, this was a very good week. I got to have some fun programming, twice. I learned some set theory, with help from a colleague on Twitter. I was also reacquainted with Mark Dominus's blog, the RSS feed for which I had somehow lost track of. I am glad to have it back in my newsreader.
This experience highlights one of the selfish reasons I like for students to ask questions in class. Sometimes, they lead to learning and enjoyment for me as well. (Thanks, Henry!) It also highlights one of the reasons I like Twitter. The friends we make there participate in our learning and enjoyment. (Thanks, Carl Friedrich!)
Yesterday's session of my course was a quiz preceded by a fun little introduction to data abstraction. As part of that, I use a common exercise. First, I define a simple API for ordered pairs: (make-pair a b), (first p), and (second p). Then I ask students to brainstorm all the ways that they could implement the API in Racket.
They usually have no trouble thinking of the data structures they've been using all semester. Pairs, sure. Lists, yes. Does Racket have a hash type? Yes. I remind my students about vectors, which we have not used much this semester. Most of them haven't programmed in a language with records yet, so I tell them about structs in C and show them Racket's struct type. This example has the added benefit of seeing that Racket generates constructor and accessor functions that do the work for us.
The big payoff, of course, comes when I ask them about using a Racket function -- the data type we have used most this semester -- to implement a pair. I demonstrate three possibilities: a selector function (which also uses booleans), message passaging (which also uses symbols), and pure functions. Most students look on these solutions, especially the one using pure functions, with amazement. I could see a couple of them actively puzzling through the code. That is one measure of success for the session.
This year, one student asked if we could use sets to implement a pair. Yes, of course, but I had never tried that... Challenge accepted!
While the students took their quiz, I set myself a problem.
The first question is how to represent the pair. (make-pair a b) could return {a, b}, but with no order we can't know which is first and which is second. My students and I had just looked at a selector function, (if arg a b), which can distinguish between the first item and the second. Maybe my pair-as-set could know which item is first, and we could figure out the second is the other.
So my next idea was (make-pair a b) → {{a, b}, a}. Now the pair knows both items, as well as which comes first, but I have a type problem. I can't apply set operations to all members of the set and will have to test the type of every item I pull from the set. This led me to try (make-pair a b) → {{a}, {a, b}}. What now?
My first attempt at writing (first p) and (second p) started to blow up into a lot of code. Our set implementation provides a way to iterate over the members of a set using accessors named set-elem and set-rest. In fine imperative fashion, I used them to start whacking out a solution. But the growing complexity of the code made clear to me that I was programming around sets, but not with sets.
When teaching functional programming style this semester, I have been trying a new tactic. Whenever we face a problem, I ask the students, "What function can help us here?" I decided to practice what I was preaching.
Given p = {{a}, {a, b}}, what function can help me compute the first member of the pair, a? Intersection! I just need to retrieve the singleton member of ∩ p:
(first p) = (set-elem (apply set-intersect p))
What function can help me compute the second member of the pair, b? This is a bit trickier... I can use set subtraction, {a, b} - {a}, but I don't know which element in my set is {a, b} and which is {a}. Serendipitously, I just solved the latter subproblem with intersection.
Which function can help me compute {a, b} from p? Union! Now I have a clear path forward: (∪ p) – (∩ p):
(second p) = (set-elem (set-minus (apply set-union p) (apply set-intersect p)))
I implemented these three functions, ran the tests I'd been using for my other implementations... and they passed. I'm not a set theorist, so I was not prepared to prove my solution correct, but the tests going all green gave me confidence in my new implementation.
Last night, I glanced at the web to see what other programmers had done for this problem. I didn't run across any code, but I did find a question and answer on the mathematics StackExchange that discusses the set theory behind the problem. The answer refers to something called "the Kuratowski definition", which resembles my solution. Actually, I should say that my solution resembles this definition, which is an established part of set theory. From the StackExchange page, I learned that there are other ways to express a pair as a set, though the alternatives look much more complex. I didn't know the set theory but stumbled onto something that works.
My solution is short and elegant. Admittedly, I stumbled into it, but at least I was using the tools and thinking patterns that I've been encouraging my students to use.
I'll admit that I am a little proud of myself. Please indulge me! Department heads don't get to solve interesting problems like this during most of the workday. Besides, in administration, "elegant" and "clever" solutions usually backfire.
I'm guessing that most of my students will be unimpressed, though they can enjoy my pleasure vicariously. Perhaps the ones who were actively puzzling through the pure-function code will appreciate this solution, too. And I hope that all of my students can at least see that the tools and skills they are learning will serve them well when they run into a problem that looks daunting.
Wordle has been the popular game du jour for a while now. Whenever this sort of thing happens, CS profs think, "How can I turn this into an assignment?" I've seen a lot of discussion of ways to create a Wordle knock-off assignment for CS 1. None of this conversation has interested me much. First, I rarely teach CS 1 these days, and a game such as Wordle doesn't fit into the courses I do teach right now. Second, there's a certain element of Yogi Berra wisdom driving me away from Wordle: "It's so popular; no one goes there any more."
Most important, I had my students implementing Mastermind in my OO course back in the 2000-2005 era. I've already had most of the fun I can have with this game as an assignment. And it was a popular one with students, though challenging. The model of the game itself presents challenges for representation and algorithm, and the UI has the sort of graphic panache that tends to engage students. I remember receiving email from a student who had transferred to another university, asking me if I would still help her debug and improve her code for the assignment; she wanted to show it to her family. (Of course I helped!)
However I did see something recently that made me think of a cool assignment: 5x6 Art, a Twitter account that converts paintings and other images into minimalist 5 block-by-6 block abstract grids. The connection to Wordle is in the grid, but the color palette is much richer. Like any good CS prof, I immediately asked myself, "How can I turn this into an assignment?"
I taught our intro course using the media computation approach popularized by Mark Guzdial back in 2006. In that course, my students processed images such as the artwork displayed above. They would have enjoyed this challenge! There are so many cool ways to think about creating a 5x6 abstraction of an input image. We could define a fixed palette of n colors, then map the corresponding region of the image onto a single block. But how to choose the color?
We could compute the average pixel value of the range and then choose the color in the palette closest to that value. Or we could create neighborhoods of different sizes around all of the palette colors so that we favor some colors for the grid over others. What if we simply compute the average pixel for the region and use that as the grid color? That would give us a much larger but much less distinct set of possible colors. I suspect that this would produce less striking outputs, but I'd really like to try the experiment and see the grids it produces.
What if we allowed ourselves a bigger grid, for more granularity in our output images? There are probably many other dimensions we could play with. The more artistically inclined among you can surely think of interesting twists I haven't found yet.
That's some media computation goodness. I may assign myself to teach intro again sometime soon just so that I can develop and use this assignment. Or stop doing other work for a day or two and try it out on my own right now.
We are at the point in my programming languages course where my students have learned a little Racket, a little functional programming, and a little recursive programming over inductive datatypes. Even though I've been able to connect many of the ideas we've studied to programming tasks out in the world that they care about themselves, a couple of students have asked, "Why are we doing this again?"
This is a natural question, and one I'm asked every time I teach this course. My students think that they will be heading out into the world to build software in Java or Python or C, and the ideas we've seen thus far seem pretty far removed from the world they think they will live in.
These paragraphs from near the end of Chelsea Troy's 3-part essay on API design do a nice job of capturing one part of the answer I give my students:
This is just one example to make a broader point: it is worthwhile for us as technologists to cultivate knowledge of the context surrounding our tools so we can make informed decisions about when and how to use them. In this case, we've managed to break down some web request protocols and build their pieces back up into a hybrid version that suits our needs.
When we understand where technology comes from, we can more effectively engage with its strengths, limitations, and use cases. We can also pick and choose the elements from that technology that we would like to carry into the solutions we build today.
The languages we use were designed and developed in a particular context. Knowing that context gives us multiple powers. One power is the ability to make informed decisions about the languages -- and language features -- we choose in any given situation. Another is the ability to make new languages, new features, and new combinations of features that solve the problem we face today in the way that works best in our current context.
Not knowing context limits us to our own experience. Troy does a wonderful job of demonstrating this using the history of web API practice. I hope my course can help students choose tools and write code more effectively when they encounter new languages and programming styles.
Computing changes. My students don't really know what world they will be working in in five years, or ten, or thirty. Context is a meta-tool that will serve them well.
It's been a while since I read a non-technical article and made as many notes as I did this morning on this Paris Review interview with Billy Collins. Collins was poet laureate of the U.S. in the early 2000s. I recall reading his collection, Sailing Alone Around the Room, at PLoP in 2002 or 2003. Walking the grounds at Allerton with a poem in your mind changes one's eyes and hears. Had I been blogging by then, I probably would have commented on the experience, and maybe one or two of the poems, in a post.
As I read this interview, I encountered a dozen or so passages that made me think about things I do, things I've thought, and even things I've never thought. Here are a few.
I'd like to get something straightened out at the beginning: I write with a Uni-Ball Onyx Micropoint on nine-by-seven bound notebooks made by a Canadian company called Blueline. After I do a few drafts, I type up the poem on a Macintosh G3 and then send it out the door.
Uni-Ball Micropoint pens are my preferred writing implement as well, though I don't write enough on paper any more to make buying a particular pen much worth the effort. Unfortunately, just yesterday my last Uni-Ball Micro wrote its last line. Will I order more? It's a race between preference and sloth.
I type up most of the things I write these days on a 2015-era MacBook Pro, often connected to a Magic Keyboard. With the advent of the M1 MacBook Pros, I'm tempted to buy a new laptop, but this one serves me so well... I am nothing if not loyal.
The pen is an instrument of discovery rather than just a recording implement. If you write a letter of resignation or something with an agenda, you're simply using a pen to record what you have thought out. In a poem, the pen is more like a flashlight, a Geiger counter, or one of those metal detectors that people walk around beaches with. You're trying to discover something that you don't know exists, maybe something of value.
Programming may be like writing in many ways, but the search for something to say isn't usually one of them. Most of us sit down to write a program to do something, not to discover some unexpected outcome. However, while I may know what my program will do when I get done, I don't always know what that program will look like, or how it will accomplish its task. This state of uncertainty probably accounts for my preference in programming languages over the years. Smalltalk, Ruby, and Racket have always felt more like flashlights or Geiger counters than tape recorders. They help me find the program I need more readily than Java or C or Python.
I love William Matthews's idea--he says that revision is not cleaning up after the party; revision is the party!
Refactoring is not cleaning up after the party; refactoring is the party! Yes.
... nothing precedes a poem but silence, and nothing follows a poem but silence. A poem is an interruption of silence, whereas prose is a continuation of noise.
I don't know why this passage grabbed me. Perhaps it's just the imagery of the phrases "interruption of silence" and "continuation of noise". I won't be surprised if my subconscious connects this to programming somehow, but I ought to be suspicious of the imposition. Our brains love to make connections.
She's this girl in high school who broke my heart, and I'm hoping that she'll read my poems one day and feel bad about what she did.
This is the sort of sentence I'm a sucker for, but it has no real connection to my life. Though high school was a weird and wonderful time for me, as it was for so many, I don't think anything I've ever done since has been motivated in this way. Collins actually goes on to say the same thing about his own work. Readers are people with no vested interest. We have to engage them.
Another example of that is my interest in bridge columns. I don't play bridge. I have no idea how to play bridge, but I always read Alan Truscott's bridge column in the Times. I advise students to do the same unless, of course, they play bridge. You find language like, South won with dummy's ace, cashed the club ace and ruffed a diamond. There's always drama to it: Her thirteen imps failed by a trick. There's obviously lots at stake, but I have no idea what he's talking about. It's pure language. It's a jargon I'm exterior to, and I love reading it because I don't know what the context is, and I'm just enjoying the language and the drama, almost like when you hear two people arguing through a wall, and the wall is thick enough so you can't make out what they're saying, though you can follow the tone.
I feel seen. Back when we took the local daily paper, I always read the bridge column by Charles Goren, which ran on the page with the crossword, crypto cipher, and other puzzles. I've never played bridge; most of what I know about the game comes from reading Matthew Ginsberg's papers about building AI programs to bid and play. Like Collins, I think I was merely enjoying sound of the language, a jargon that sounds serious and silly at the same time.
Yeats summarizes this whole thing in "Adam's Curse" when he writes: "A line will take us hours maybe, / Yet if it does not seem a moment's thought / Our stitching and unstitching has been naught."
I'm not a poet, and my unit of writing is rarely the line, but I know a feeling something like this in writing lecture notes for my students. Most of the worst writing consists of paragraphs and sections I have not spent enough time on. Most of the best sounds natural, a clean distillation of deep understanding. But those paragraphs and sections are the result of years of evolution. That's the time scale on which some of my courses grow, because no course ever gets my full attention in any semester.
When I finish a set of notes, I usually feel like the stitching and unstitching have not yet reached their desired end. Some of the text "seems a moment's thought", but much is still uneven or awkward. Whatever the state of the notes, though, I have move on to the next task: grading a homework assignment, preparing the next class session, or -- worst of all -- performing the administrivia that props up the modern university. More evolution awaits.
~~~~
This was a good read for a Sunday morning on the exercise bike, well recommended. The line on revision alone was worth the time; I expect it will be a stock tool in my arsenal for years to come.
My recent post on what language or tool I should dive into next got some engagement on Twitter, with many helpful suggestions. Thank you all! So I figure I should post a quick update to report what I'm thinking at this point.
In that post, I mentioned JavaScript and Pharo by name, though I was open to other ideas. Many folks pointed out the practical value of JavaScript, especially in a context where many of my students know and use it. Others offered lots of good ideas in the Smalltalk vein, both Pharo and several lighter-weight Squeaks. A couple of folks recommended Glamorous Toolkit (GToolkit), from @feenkcom, which I had not heard of before.
I took to mind several of the suggestions that commenters made about the how to think about making the decision. For example, there is more overhead to studying Pharo and GToolkit than JavaScript or one of the lighter-weight Squeaks. Choosing one of the latter would make it easier to tinker. I think some of these comments had students in mind, but they are true even for my own study during the academic semester. Once I get into a term (my course begins one week from today), my attention gets pulled in many directions for fifteen or sixteen weeks. Being able to quickly switch contexts when jumping into a coding session means that I can jump more often and more productively.
Also, as Glenn Vanderburg pointed out, JavaScript and Pharo aren't likely to teach me much new. I have a lot of background with Smalltalk and, in many ways, JavaScript is just another language. The main benefit of working with either would be practical, not educational.
GToolkit might teach me something, though. As I looked into GToolkit, it became more tempting. The code is Smalltalk, because it is implemented in Pharo. But the project has a more ambitious vision of software that is "moldable": easier to understand, easier to figure out. GToolkit builds on Smalltalk's image in the direction of a computational notebook, which is an idea I've long wanted to explore. (I feel a little guilty that I haven't look more into the work that David Schmüdde has done developing a notebook in Clojure.) GToolkit sounds like a great way for me to open several doors at once and learn something new. To do it justice, though, I need more time and focus to get started.
So I have decided on a two-pronged approach. I will explore JavaScript during the spring semester. This will teach me more about a language and ecosystem that are central to many of my students' lives. There is little overhead to picking it up and playing with it, even during the busiest weeks of the term. I can have a little fun and maybe make some connections to my programming languages course along the way. Then for summer, I will turn my attention to GToolkit, and perhaps a bigger research agenda.
I started playing with JavaScript on Tuesday. Having just read a blog post on scripting to compute letter frequencies in Perl, I implemented some of the same ideas in JavaScript. For the most part, I worked just as my students do: relying on vague memories of syntax and semantics and, when that failed, searching about for examples online.
A couple of hours working like this refreshed my memory on the syntax I knew from before and introduced me to some features that were new to me. It took a few minutes to re-internalize the need for those pesky semicolons at the end of every line... The resulting code is not much more verbose than Perl. I drifted pretty naturally to using functional programming style, as you might imagine, and it felt reasonably comfortable. Pretty soon I was thinking more about the tradeoff between clarity and efficiency in my code than about syntax, which is a good sign. I did run into one of JavaScript's gotchas: I used for...in twice instead of for...of and was surprised by the resulting behavior. Like any programmer, I banged my head on wall for a few minutes and then recovered. But I have to admit that I had fun. I like to program.
I'm not sure what I will write next, or when I will move into the browser and play with interface elements. Suggestions are welcome!
I am pretty sure, though, that I'll start writing unit tests soon. I used SUnit briefly and have a lot of experience with JUnit. Is JSUnit a thing?
When I was in high school, the sponsor of our our Math Club, Mr. Harpring, liked to give books as prizes and honors for various achievements. One time, he gave me Women in Mathematics, by Lynn Osen. It introduced me to Émilie du Châtelet, Sophie Germain, Emmy Noether, and a number of other accomplished women in the field. I also learned about some cool math ideas.
![]() |
Another time, I received The Unexpected Hanging and Other Mathematical Diversions, a collection of Martin Gardner's columns from Scientific American. One of the chapters was about Hexapawn, a simple game played with chess pawns on a 3x3 board. The chapter described an analog computer that learned how to play a perfect game of Hexapawn. I was amazed.
I played a lot of chess in high school and was already interested in computer chess programs. Now I began to wonder what it would be like to write a program that could learn to play chess... I suspect that Gardner's chapter planted one of the seeds that grew into my study of computer science in college. (It took a couple of years, though. From the time I was eight years old, I had wanted to be an architect, and that's where my mind was focused.)
As I wrote those words, it occurred to me that I may have written about the Gardner book before. Indeed I have, in a 2013 post on building the Hexapawn machine. Some experiences stay with you.
They also intersect with the rest of the world. This week, I read Jeff Atwood's recent post about his project to bring the 1973 book BASIC Computer Games into the 21st century. This book contains the source code of BASIC programs for 101 simple games. The earliest editions of this book used a version of BASIC before it included the GOSUB command, so there are no subroutines in any of the programs! Atwood started the project as a way to bring the programs in this book to a new audience, using modern languages and idioms.
You may wonder why he and other programmers would feel so fondly about BASIC Computer Games to reimplement its programs in Java or Ruby. They feel about these books the way I felt about The Unexpected Hanging. Books were the Github of the day, only in analog form. Many people in the 1970s and 1980s got their start in computing by typing these programs, character for character, into their computers.
I was not one of those people. My only access to a computer was in the high school, where I took a BASIC programming class my junior year. I had never seen a book like BASIC Computer Games, so I wrote all my programs from scratch. As mentioned in an old OOPSLA post from 2005, the first program I wrote out of passion was a program to implement a ratings system for our chess club. Elo ratings were great application for a math student and beginning programmer.
Anyway, I went to the project's Github site to check out what was available and maybe play a game or two. And there it was: Hexapawn! Someone has already completed the port to Python, so I grabbed it and played a few games. The text interface is right out of 1973, as promised. But the program learns, also as promised, and eventually plays a perfect game. Playing it brings back memories of playing my matchbox computer from high school. I wonder now if I should write my own program that learns Hexapawn faster, hook it up with the program from the book, and let them duke it out.
Atwood's post brought to mind pleasant memories at a time when pleasant memories are especially welcome. So many experiences create who we are today, yet some seem to have made an outsized contribution. Learning BASIC and reading Martin Gardner's articles are two of those for me.
Reading that blog post and thinking about Hexapawn also reminded me of Mr. Harpring and the effect he had on me as a student of math and as a person. The effects of a teacher in high school or grade school can be subtle and easy to lose track of over time. But they can also be real and deep, and easy not to appreciate fully when we are living them. I wish I could thank Mr. Harpring again for the books he gave me, and for the gift of seeing a teacher love math.
I've never been one to write year-end retrospectives on my blog, or prospective posts about my plans for the new year. That won't change this year, this post notwithstanding.
I will say that 2021 was a weird year for me, as it was for many people. One positive was purchasing a 29" ultra-wide monitor for work at home, seen in this post from my Strange Loop series. Programming at home has been more fun since the purchase, as have been lecture prep, data-focused online meetings, and just about everything. The only downside is that it's in my basement office, which hides me away. When I want to work upstairs to be with family, it's back to the 15" laptop screen. First-world problems.
Looking forward, I'm feeling a little itchy. I'll be teaching programming languages again this spring and plan to inject some new ideas, but the real itch is: I am looking for a new project to work on, and a new language to study. This doesn't have to be a new language, just one that one I haven't gone deep on before. I have considered a few, including Swift, but right now I am thinking of Pharo and JavaScript.
Thinking about mastering JavaScript in 2022 feels like going backward. It's old, as programming languages go, and has been a dominant force in the computing world for well over a decade. But it's also the most common language many of my students know that I have never gone deep on. There is great value in studying languages for their novel ideas and academic interest, but there is also value in having expertise with a language and toolchain that my students already care about. Besides, I've really enjoyed reading about work on JIT compilation of JavaScript over the years, and it's been a long time since I wrote code in a prototype-based OO language. Maybe it's time to build something useful in JavaScript.
Studying Pharo would be going backward for me in a different way. Smalltalk always beckons. Long-time followers of this blog have read many posts about my formative experiences with Smalltalk. But it has been twenty years since I lived in an image every day. Pharo is a modern Smalltalk with a big class library and a goal of being suitable for mission-critical systems. I don't need much of a tug; Smalltalk always beckons.
My current quandary brings to mind a dinner at a Dagstuhl seminar in the summer of 2019 (*). It's been a while now, so I hope my memory doesn't fail me too badly. Mark Guzdial was talking about a Pharo MOOC he had recently completed and how he was thinking of using the language to implement a piece of software for his new research group at Michigan, or perhaps a class he was teaching in the fall. If I recall correctly, he was torn between using Pharo and... JavaScript. He laid out some of the pros and cons of each, with JavaScript winning out on several pragmatic criteria, but his heart was clearly with Pharo. Shriram Krishnamurthi gently encouraged Mark to follow his heart: programming should be joyful, and programming languages allow us to build in languages that give us enjoyment. I seconded the (e)motion.
And here I sit mulling a similar choice.
Maybe I can make this a two-language year.
~~~~~
(*) Argh! I never properly blogged about about this seminar, on the interplay between notional machines and programming language semantics, or the experience of visiting Europe for the first time. I did write one post that mentioned Dagstuhl, Paris, and Montenegro, with an expressed hope to write more. Anything I write now will be filtered through two and a half years of fuzzy memory, but it may be worth the time to get it down in writing before it's too late to remember anything useful. In the meantime: both the seminar and the vacation were wonderful! If you are ever invited to participate in a Dagstuhl seminar, consider accepting.
Over the last four or five offerings of my compiler course, I have been making progress in how I teach code generation, with teams becoming increasingly successful at producing a working code generator. In the 2019 offering, students asked a few new questions about some low-level mechanical issues in the run-time system. So I whipped up a simple one the night before class, both to refamiliarize myself with the issues and to serve as a potential example. It was not a great piece of software, but it was good enough for a quick in-class demo and as a seed for discussion.
Jump ahead to 2021. As I mentioned in my previous post, this fall's group had a lot more questions about assembly language, the run-time stack, activation records, and the like. When I pulled out my demo run-time system from last time, I found that it didn't help them as much as it had the previous group. The messiness of the code got in the way. Students couldn't see the bigger picture from the explanatory comments, and the code itself seemed opaque to them.
Working with a couple of students in particular, I began to refine the code. First, I commented the higher-level structure of generator more clearly. I then used those comments to reorganize the code bit, with the goal of improving the instructional presentation rather than the code's efficiency or compactness. I chose to leave some comments in rather than to factor out functions, because the students found the linear presentation easier to follow.
Finally, I refined some sections of the code and rewrote others entirely, to make them clearer. At this point, I did extract a helper function or two in an attempty not to obscure the story the program was telling with low-level details.
I worked through two iterations of this process: comment, reorganize, rewrite. At the end, I had a piece of code that is pretty good, and one that is on the student's path to a full code generator.
Of course, I could have designed my software up front and proceeded more carefully as I wrote this code. I mean, professionals and academics understand compiler construction pretty well. The result might well have been a better example of what the run-time generator should look like when students are done.
But I don't think that would have been as helpful to most members of my class. This process was much more like how my students program, and how many of us program, frankly, when we are first learning a new domain. Following this process, and working in direct response to questions students had as we discussed the code, gave me insights into some of the challenges they encounter in my course, including tracking register usage and seeing how the calling and return sequences interact. I teach these ideas in class, but my students were seeing that material as too abstract. They couldn't make the leap to code quite as easily as I had hoped; they were working concretely from the start.
In the end, I ended up with both a piece of code I like and a better handle on how my students approach the compiler project. In terms of outcomes assessment, this experience gives me some concrete ideas for improving the prerequisite courses students take before my course, such as computer organization. More immediately, it helps me improve the instruction in my own course. I have some ideas about how I can reorganize my code generator units and how I might simplify some of my pedagogical material. This may lead to a bigger redesign of the course in a coming semester.
I must admit: I had a lot of fun writing this code -- and revising and improving it! One bit of good news from the experience is that the advice I give in class is pretty good. If they follow my practical suggestions for writing their code, they can be successful. What needs improvement now is finding ways for students to have the relevant bits of advice at hand when they need them. Concrete advice that gets separated from concrete practice tends to get lost in the wind.
Finally, this experience reminded me first hand that the compiler project is indeed a challenge. It's fun, but it's a challenge, especially for undergrads attempting to write their first large piece of software as part of a team. There may be ways I can better help them to succeed.
Well, fall semester really got away from me quickly. It seems not long ago that I wrote of launching the course with a renewed mindset of "compilers for the masses, not compilers for compiler people". I'm not sure how well that went this time, as many students came into the course with less understanding of the underlying machine model and assembly language than ever before. As a result, many of them ended up stressing over low-level implementation details while shoring up that knowledge than thinking about some of the higher-level software engineering ideas. I spent more time this semester working with more teams to help them understand parsing rules, semantic actions, and activation records than in anytime I can remember.
I suspect that the students' programming maturity and state of knowledge at the start of the course are in large part a result of experiencing the previous two and a half semesters under the damper of the pandemic. Some classes were online, others were hybrid, and all were affected by mitigation efforts, doubt, and stress. Students and professors alike faced these effects, me included, and while everyone has been doing the best they could under the circumstances, sometimes the best we can do comes up a little short.
At the beginning of the course, I wrote about a particular uncertainty raised by the preceding pandemic semesters: how isolation and the interruption of regular life had reduced the chances for students to make friends in the major and to build up personal and professional connections with their classmates. I underestimated, I think, the effect that the previous year and a half would have on learning outcomes in our courses.
The effect on project teams themselves turned out to be a mixed bag. Three of the five teams worked pretty well together, even if one of the teammates was unable to contribute equally to the project. That's pretty typical. Two other teams encountered more serious difficulties working together effectively. Difficulties derailed one project that got off to an outstanding start, and the second ended up being a one-person show (a very impressive one-person show, in fact). In retrospect, many of these challenges can be traced back to problems some students had with content: they found themselves falling farther behind their teammates and responded by withdrawing from group work. The result is a bad experience for those still plugging along.
That's perhaps too many words about the difficulties. Several teams seemed to have pretty typical experiences working one another, even though they didn't really know each other before working together.
The combination of some students struggling with course content and some struggling with collaboration led to mixed bag of results. Two teams produced working compilers that handled essentially all language features correctly, or nearly so. That's pretty typical for a five-team semester. One team produced an incomplete system, but one they could be proud of after working pretty hard the entire semester. That's typical, too.
Two teams produced systems without code generators beyond a rudimentary run-time system. That's a bit unusual. These teams were disappointed because they had set much higher goals for themselves. Many of these students were taking heavy course and research loads and, unfortunately, all that work eventually overwhelmed them. I think I felt as bad for them as they did, knowing what they might have accomplished with a more forgiving schedule. I do hope they found some value in the course and will be able to look back on the experience as worthwhile. They learned a lot about working on a big project, and perhaps about themselves.
What about me? A few weeks into the course, I declared that I was programming like a student again, trying to implement the full compiler project I set before my students. Like many of my students, I accomplished some of my goals and fell short when outside obstacles got in the way. One the front end, my scanner is in great shape, while my parser is correct but in need of some refactoring. At that point in the semester, I got busy both with department duties and with working one on one with the teams, and my productivity dropped off.
I did implement a solid run-time system, one I am rather happy with. My work on it came directly out of answering students' questions about code generation and working with them to investigate and debug their programs. I'll have more to say about my run-time system in the next post.
So, my latest compiler course is in the books. All in all, my students and I did about as well as we could under the circumstances. There is still great magic in watching a team's compiler generate an executable, then running that executable on an input that produces tens of thousands of activation records and executes several million lines of assembly. The best we can do is often quite good enough.
Yesterday someone retweeted this message from Hillel Wayne into my timeline:
A surprising and undervalued aspect of mastery comes from narrow-scope "microskills". In software, these would be things like
- Python list comprehensions
- Grep flags
- Debugging specific error messages
- Using a window manager
We can, and should, do targeted training.
Later in the short thread, Hillel says something that brought my students to mind:
In other words, the high level thinking is the difference between a task being possible and impossible, but the microskills are the difference between a task being 20 minutes and six hours. That's why it can take us days to find the right one-liner.
Some students love to program and look for every opportunity to write code. These students develop a satchelful of microskills, usually far behind what we could every teach them or expose them to in class. They tend to do fine on the programming assignments we give in class.
Other students complain that a programming assignment I gave them, intended as a one-hour practice session, took them eight or ten hours. My first reaction is usually to encourage them never to spin their wheels that long without asking me a question. I want them to develop grit, but usually that kind of wheel spinning is a sign that they may be missing a key idea or piece of information. I'd like to help them get moving sooner.
There is another reason, though, that many of these students spin their wheels. For a variety of reasons, they program only when they are required by a class. They are ambivalent about programming, either because they don't find it as interesting as their peers or because they don't enjoy the grind of working in the trenches yet. I say "yet" because one of the ways we all come to enjoy the grind is... to grind away! That's how we develop the microskills Hillel is talking about, which turn a six-hour task into a 20-minute task, or a disappointing one-hour experience with a homework problem into a five-minute joy.
Students who have yet to experience the power of microskills are prone to underestimate their value. That makes it less enjoyable to practice programming, which makes it hard to develop new skills. It's a self-reinforcing loop, and not the kind we want to encourage in our students.
Even after all these years in the classroom, I still struggle to find ways to help my students practice programming skills in a steady, reliable way. Designing engaging problems to work on helps. So does letting students choose the problems they work on, which works better in some courses than others. Ultimately, though, I think what works best is to develop as much of a personal relationship with each student as possible. This creates a condition in which they are more inclined to ask for help when they need it and to trust suggestions from that sound a little crazy to the beginner's mind.
I am teaching my compiler development course this semester, which means that I am working with students near the end of their time with us, whose habits are deeply ingrained from previous courses. The ones who don't already possess a few of the microskills they need are struggling as the task of writing a parser or semantic checker or code generator stretches out like an endless desert before them.
Next semester, I teach my programming languages course and have the joy of introducing my students to Racket and functional programming. This essay me already thinking of how I can help my students develop some of the microskills they will want and need in the course and beyond. Perhaps a more explicit focus early on the use Dr. Racket to create, run, test, and debug code can set them up for a more enjoyable experience later in the course -- and help put them on a virtuous self-reinforcing loop developing skills and using them to enjoy the next bit of learning they do.
A couple of weeks back, I saw an article in which Malcom Gladwell noted that he did not know The Triggering Town, a slim book of essays by poet Richard Hugo. I was fortunate to hear about Hugo many years ago from software guru Richard Gabriel, who is also a working poet. It had been fifteen years or more since I'd read The Triggering Town, so I stopped into the library on my way home one day and picked it up. I enjoyed it the second time around as much as the first.
I frequently make notes of passages to save. Here are five from this reading.
Actually, the hard work you do on one poem is put in on all poems. The hard work on the first poem is responsible for the sudden ease of the second. If you just sit around waiting for the easy ones, nothing will come. Get to work.
That advice works for budding software developers, too.
Emotional honesty is a rare thing in the academic world or anywhere else for that matter, and nothing is more prized by good students.
Emotion plays a much smaller role in programming than in writing poetry. Teaching, though, is deeply personal, even in a technical discipline. All students value emotional honesty, and profs who struggle to be open usually struggle making connections to their students.
Side note: Teachers, like policemen, firemen, and service personnel, should be able to retire after twenty years with full pension. Our risks may be different, but they are real. In twenty years most teachers have given their best.
This is a teacher speaking, so take the recommendation with caution. But more than twenty years into this game, I know exactly what Hugo means.
Whatever, by now, I was old enough to know explanations are usually wrong. We never quite understand and we can't quite explain.
Yet we keep trying. Humans are an optimistic animal, which is one of the reasons we find them so endearing.
... at least for me, what does turn me on lies in a region of myself that could not be changed by the nature of my employment. But it seems important (to me even gratifying) that the same region lies untouched and unchanged in a lot of people, and in my innocent way I wonder if it is reason for hope. Hope for what? I don't know. Maybe hope that humanity will always survive civilization.
This paragraph comes on the last page of the book and expresses one of the core tenets of Hugo's view of poetry and poets. He fought in World War 2 as a young man, then worked in a Boeing factory for 15-20 years, and then became an English professor at a university. No matter the day job, he was always a poet. I have never been a poet, but I know quite well the region of which he speaks.
Also: I love the sentence, "Maybe hope that humanity will always survive civilization."
For the first time in many years, I got the urge this fall to implement the compiler project I set before my students.
I've written here about this course many times over the years. It serves students interested in programming languages and compilers as well as students looking for a big project course and students looking for a major elective. Students implement a compiler for a small language by hand in teams of 2-5, depending on the source language and the particular group of people in the course.
Having written small compilers like this many times, I don't always implement the entire project each offering. That would not be a wise use of my time most semesters. Instead, when something comes up in class, I will whip up a quick scanner or type checker or whatever so that we can explore an idea. In recent years, the bits I've written have tended to be on the backend, where I have more room to learn.
But this fall, I felt the tug to go all in.
I created a new source language for the class this summer, which I call Twine. Much of its concrete syntax was inspired by SISAL, a general-purpose, single-assignment functional language with implicit parallelism and efficient array handling. SISAL was designed in the mid-1980s to be a high-level language for large numerical programs, to be run on a variety of multiprocessors. With advances in multiprocessors and parallel processors, SISAL is well suited for modern computation. Of course, it contains many features beyond what we can implement in a one-semester compiler course where students implement all of their own machinery. Twine is essentially a subset of SISAL, with a few additions and modifications aimed at making the language more suitable for our undergraduate course.
![]() |
(Whence the name "Twine"? The name of SISAL comes from the standard Unix word list. It was chosen because it contains the phrase "sal", which is an acronym for "single assignment language". The word "sisal" itself is the name of a flowering plant native to southern Mexico but widely cultivated around the world. Its leaves are crushed to extract a fiber that is used to create rope and twine. So, just as the sisal plant is used to create twine, the SISAL programming language was used to create the Twine programming language.)
With new surface elements, the idea of implementing a new front end appealed to me. Besides, the experience of implementing a complete system feels different than implementing a one-off component... That's one of the things we want our students to experience in our project courses! After eighteen months of weirdness and upheaval at school and in the world, I craved that sort of connection to some code. So here I am.
Knocking out a scanner in my free time over the last week and getting started on my parser has been fun. It has also reminded me how the choice of programming language affects how I think about the code I am writing.
I decided to implement in Python, the language most of my student teams are using this fall, so that I might have recent experience with specific issues they encounter. I'd forgotten just how list-y Python is. Whenever I look at Python code on the web, it seems that everything is a list or a dictionary. The path of least resistance flows that way... If I walk that path, I soon find myself with a list of lists of lists, and my brain is swimming in indices. Using dictionaries replaces integer indices with keys of other types, but the conceptual jumble remains.
It did not take me long to appreciate anew why I like to work with objects. They give me the linguistic layers I need to think about my code independent of languages primitives. I know, I know, I can achieve the same thing with algebraic types and layers of function definitions. However, my mind seems to work on a wavelength where data encapsulation and abstract messages go together. Blame Smalltalk for ruining me, or enlightening me, whichever your stance.
Python provides a little extra friction to classes and objects that seems to interrupt my flow occasionally. For a few minutes this week, I felt myself missing Java and wondering if I ought to have chosen it for the project instead of Python. I used to program in Java every day, and this was the first time in a long while that I felt the pull back. After programming so much in Racket the last decade, though, the wordiness of Java keeps me away. Alas, Python is not the answer. Maybe I'm ready to go deep on a new language, but which one? OOP doesn't seem to be in vogue these days. Maybe I need to return to Ruby or Smalltalk.
For now I will live with OOP in Python and see whether its other charms can compensate. Living with Python's constraints shows up as a feature of another choice I made for this project: to let pycodestyle tell me how to format my code. This is an obstacle for any programmer who is as idiosyncratic as I am. After a few rounds of reformatting my code, though, I am finding surrender easier to accept. This has freed me to pay attention to more important matters, which is one of the keys ideas behind coding and style standards in the first place. But I am a slow learner.
It's been fun so far. I look forward to running Twine programs translated by my compiler in a few weeks! As long as I've been programming, I have never gotten over the thrill of watching my compiler I've written -- or any big program I've written -- do its thing. Great joy.
We will be forming project teams in my course this week, and students will begin work in earnest on Friday. Or so thinks the prof, who releases the first assignment on Thursday... I can dream.
I noticed one change this year when I surveyed students about their preferences for forming teams. In an ordinary year, most students submit at least one or two names of others in the class with whom they'd like to work; some already have formed the teams they want to work in. A few indicate someone they'd rather not work with, usually based on experiences in previous courses. This helps me help them form teams with a mix of new and familiar, with some hedge against expected difficulties. It's never perfect, but most years we end up with a decent set of teams and project experiences.
This year, though, students barely offered any suggestions for forming teams. Most students expressed no preference for whom they want to work with, and no one indicated someone they don't want to work with.
At first, this seemed strange to me, but then I realized that it is likely an effect of three semesters distorted by COVID-19. With one semester forced online and into isolation, a second semester with universal masking, no extracurricular activities, and no social life, and a third semester with continued masking and continued encouragement not to gather, these students have had almost no opportunitiy to get to know one another!
This isolation eliminates one of the great advantages of a residential university, both personally and professionally. I made so many friends in college, some of whom I'm still close to, and spent time with them whenever I wasn't studying (which, admittedly, was a lot). But it also affects the classroom, where students build bonds over semesters of taking courses together in various configurations. Those bonds carry over into a project course such as mine, where they lubricate the wheels of teams who have to work together more closely than before. They at least begin the project knowing each other a bit and sharing a few academic experiences.
Several students in my class this semester said, "I have no friends in this class" or even "I don't know any other CS majors". That is sad. It also raises the stakes for the compiler project, which may be there only chance to make acquaintances in their major before they graduate. I feel a lot more responsibility as I begin to group students into teams this semester, even as I know that I have less information available than ever before for doing a credible job.
I'm going to keep all this in mind as the semester unfolds and pay closer attention to how students and teams seem to be doing. Perhaps this course can not only help them have a satisfying and educational experience building a big piece of software, but also help them form some of the personal bonds that add grace notes to their undergrad years.
~~~~~
On an unrelated note, I received word a couple of weeks ago that this blog had been selected by Feedspot as one of the Top 20 Computer Science Blogs on the web. It's always nice to be recognized in this way. Given how little little I've blogged over the last couple of years, it is rather generous to include me on this list! I see there a number of top-quality blogs, several of which I read religiously, and most of which post entries with admirable regularity. It remains a goal of mine to return to writing here more regularly. Perhaps two entries within a week, light as they are, offer hope.
Well, a month has passed. Already, the first week of classes are in the books. My compiler course is off to as good a start as one might hope.
Week 1 of the course is an orientation to the course content and project. Content-wise, Day 1 offers a bird's-eye view of what a compiler does, then Day 2 tries to give a bird's-eye view of how a compiler works. Beginning next week, we go deep on the stages of a compiler, looking at techniques students can use to implement their compiler for a small language. That compiler project is the centerpiece and focus of the course.
Every year, I think about ways to shake up this course. (Well, not last year, because we weren't able to offer it due to COVID.) As I prepared for the course, I revisited this summary of responses to a Twitter request from John Regehr: What should be taught in a modern undergrad compiler class? It was a lot of fun to look back through the many recommendations and papers linked there. In the end, though, the response that stuck with me came from Celeste Hollenbeck, who "noted the appeal of focusing on the basics over esoterica": compilers for the masses, not compilers for compiler people.
Our class is compilers for everyone in our major, or potentially so. Its main role in our curriculum is to be one of four so-called project courses, which serve as capstones for a broad set of electives. Many of the students in the course take it to satisfy their project requirement, others take it to satisfy a distribution requirement, and a few take it just because it sounds like fun.
The course is basic, and a little old-fashioned, but that works for us. The vast majority of our students will never write a compiler again. They are in the course to learn something about how compilers work conceptually and to learn what it is like to build a large piece of software with a team. We talk about modern compiler technology such as LLVM, but working with such complicated systems would detract from the more general goals of the course for our students. Some specific skills for writing lexers and scanners, a little insight into how compilers work, and experience writing a big program with others (and living with design decisions for a couple of months!) are solid outcomes for an undergrad capstone project.
That's not to say that some students don't go on to do more with compilers... Some do. A few years ago, one of our undergrads interviewed his way into an internship with Sony PlayStation's compiler team, where he now works full time. Other students have written compilers for their own languages, including one that was integrated as a scripting language into a gaming engine he had built. In that sense, the course seems to serve the more focused students well, too.
Once more unto the breach, dear friends, once more...
-- Henry V
So, we are off. I still haven't described the source language my students will be processing this semester, as promised in my last post. Soon. Since then, though, I wrote a bunch of small programs in the language just to get a feel for it. That's as much fun as a department head gets to have most days these days.
On teaching, via Robert Talbert:
Look at the course you teach most often. If you had the power to remove one significant topic from that course, what would it be, and why?
I have a high degree of autonomy in most of the courses I teach, so power isn't the limiting factor for me. Time is a challenge to making big changes, of course. Gumption is probably what I need most right now. Summer is a great time for me to think about this, both for my compiler course this fall and programming languages next spring.
On research, via Kris Micinski:
i remember back to Dana Scott's lecture on the history of the lambda calculus where he says, "If Turing were alive today, I don't know what he'd be doing, but it wouldn't be recursive function theory." I think about that a lot.
Now I am, too. Seriously. I'm no Turing, but I have a few years left and some energy to put into something that matters. Doing so will require some gumption to make other changes in my work life first. I am reaching a tipping point.
I have been enjoying a few of James Propp's essays recently. Last month he wrote about the creation of zero. In Who Needs Zero, he writes:
But in mathematics, premature attempts to reach philosophical clarity can get in the way of progress both at the individual level and at the cultural level. Sometimes you have to just start talking before you understand what you're talking about.
This reminded me of a passage by Iris Murdoch in Metaphysics as a Guide to Morals, which I encountered in one of Robin Sloan's newsletters:
The achievement of coherence is itself ambiguous. Coherence is not necessarily good, and one must question its cost. Better sometimes to remain confused.
My brain seems hardwired to seek out and create abstractions. Perhaps it's just a deeply ingrained habit. Even so I am a pragmatist at heart. As Propp says, "Zero is as zero does."
Allowing oneself to remain confused, to forge ahead without having reached clarity yet, is essential to doing research, or to learning anything at all, really.
A couple of weeks ago, a former student emailed me after many years. Felix immigrated to the US from the Sudan back in the 1990s and wound up at my university, where he studied computer science. While in our program, he took a course or two with me, and I supervised his undergrad research project. He graduated and got busy with life, and we lost touch.
He emailed to let me know that he was about to defend his Ph.D. dissertation, titled "Efficient Reconstruction and Proofreading of Neural Circuits", at Harvard. After graduating from UNI, he programmed at DreamWorks Interactive and EA Sports, before going to grad school and working to "unpack neuroscience datasets that are almost too massive to wrap one's mind around". He defended his dissertation successfully this week.
Congratulations, Dr. Gonda!
Felix wrote initially to ask permission to acknowledge me in his dissertation and defense. As I told him, it is an honor to be remembered so fondly after so many years. People often talk about how teachers affect their students' futures in ways that are often hard to see. This is one of those moments for me. Arriving at the end of what has been a challenging semester in the classroom for me, Felix's note boosted my spirit and energizes me a bit going into the summer.
If you'd like to learn more about Felix and his research, here is his personal webpage The Harvard School of Engineering also has a neat profile of Felix that shows you what a neat person he is.
This is short story about a student finding something helpful in class and making my day, preceded by a long-ish back story.
In my programming languages course yesterday, I did a session on optimization. It's a topic of some importance, and students are usually interested in what it means for an interpreter or compiler to "optimize" code. I like to show students a concrete example that demonstrates the value of an optimization. Given where we are in the course and the curriculum, though, it would be difficult to do that with a full-featured language such as Python or Java, or even Racket. On the other end of the spectrum, the little languages they have been implementing and using all semester are too simple to benefit from meaningful optimization.
I found a sweet spot in between these extremes with BF. (Language alert!) I suppose it is more accurate to say that Eli Bendersky found the sweet spot, and I found Bendersky's work. Back in 2017, he wrote a series of blog posts on how to write just-in-time compilers, using BF as his playground. The first article in that series inspired me to implement something similar in Python and to adapt it for use with my students.
BF is well-suited for my purposes. It is very simple language, consisting of only eight low-level operators. It is possible to write a small interpreter for BF that students with only a background in data structures can understand. Even so, the language is Turing complete, which means that we can write interesting and arbitrarily complex programs.
The low-level simplicity of BF combines with its Turing completeness to create programs that are horribly inefficient if they are interpreted in a naive manner. There are many simple ways to optimize BF programs, including creating a jump table to speed up loops and parsing runs of identical opcodes (moves, increments, and decrements) as more efficient higher-level operators. Even better, the code to implement these optimizations is also understandable to a student with only data structures and a little background in programming languages.
My session is built around a pair of interpreters, one written in a naive fashion and the other implementing an optimization. This semester, we preprocessed BF programs to compute a table that makes jumping to the beginning or end of a loop an O(1) operation just like BF's other six primitives. The speed-up on big BF programs, such as factoring large numbers or computing a Mandelbrot set, is impressive.
Now to the story.
At the end of class, I talk a bit about esoteric languages more broadly as a way for programmers to test the boundaries of programming language design, or simply to have fun. I get to tell students a story about a four-hour flight back from OOPSLA one year during which I decided to roll a quick interpreter for Ook in Scheme. (What can I say; programming is fun.)
To illustrate some of the fun and show that programmers can be artists, too, I demo programs in the language Piet, which is named for the Dutch abstract painter Piet Mondrian. He created paintings that look like this:
That is not a Mondrian, but it is a legal program in the Piet language. It prints 'Piet'. Here is another legal Piet program:
It prints "Hello, World". Here's another:
That program reads an integer from standard input, determines whether it is prime or not, and prints 'Y' or 'N'. Finally, how about this:
If you are a certain age, you may notice something special about this image: It is made up exclusively of Tetris pieces. The program prints... "Tetris". Programming truly is an art!
One of my students was inspired. While reviewing the session notes, he searched for more information about Piet online and found this interactive editor. He then used it to create a Piet program in honor of a friend of his who passed away earlier this semester. It prints the Xbox gamertag of his late friend. In his email to me, he said that writing this program was therapeutic.
I'm not sure one of my class sessions has ever had a more important outcome. I'm also not sure that I have ever been happier to receive email from a student.
This has been a tough year for most everyone, and especially for students who are struggling with isolation and countermeasures against a nasty virus. I'm so glad that programming gave one student a little solace, at least for an evening. I'm also glad he shared his story with me.
A common complaint from students is that the professor makes them teach themselves then material.
From In Defense of Teaching Yourself the Material:
Higher education institutions must orient themselves toward teaching students how to teach themselves, or risk becoming irrelevant. I'll add to the above that self-teaching (and self-regulation) are also valuable job skills. During my time at Steelcase, I learned that what the company wanted was not so much a recent college graduate with straight A's, but someone who could learn quickly and get up to speed without having to pull someone else off their job to teach them. So self-teaching is not only the ultimate goal of higher education and the main instantiation of lifelong learning, it's also what gives graduates a competitive advantage on the job market and sets them up not to be stuck in a loop for their careers. I want my students to be able to say truthfully in an interview, when asked why they should be hired: I know how to learn things, and learn fast, without a lot of hand-holding. That is music to the employer's ears. The word will get out about which colleges equip students well in this area. Similarly for those that don't.
Talbert has more to say about the value of students' being able to teach themselves. One of our jobs as instructors is to provide the scaffolding that students need to learn how to do this effectively in our discipline, and then slowing dismantle the scaffold and let students take over.
A few weeks ago, a Scott Galloway video clip made the rounds. In it, Galloway was saying something about "finding your passion" that many people have been saying for a long time, only in that style that makes Galloway so entertaining. Here's a great bit of practical advice on the same topic from tech guru Kevin Kelly:
Following your bliss is a recipe for paralysis if you don't know what you are passionate about. A better motto for most youth is "master something, anything". Through mastery of one thing, you can drift towards extensions of that mastery that bring you more joy, and eventually discover where your bliss is.
My first joking thought when I read this was, "Well, maybe not anything..." I mean, I can think of lots of things that don't seem worth mastering, like playing video games. But then I read about professional gamers making hundreds of thousands of dollars a year, so who am I to say? Find something you are good at, and get really good at it. As Galloway says, like Chris Rock before him, it's best to become good at something that other people will pay you for. But mastery of anything opens doors that passion can only bang on.
The key to the "master something, anything" mantra is the next sentence of Kelly's advice. When we master something, our expertise creates opportunities. We can move up or down the hierarchy of activities built from that mastery, or to related domains. That is where we are most likely to find the life that brings us joy. Even better, we will find it in a place where our mastery helps us get through the inevitable drudge work and over the inevitable obstacles that will pop in our way. I love to program, but some days debugging is a slog, and other days I butt up against thorny problems beyond my control. The good news is that I have skills to get through those days, and I like what I'm doing enough to push on through to the more frequent moments and days of bliss.
Passion is wonderful if you have it, but it's hard to conjure up on its own. Mastering a skill, or a set of skills, is something every one of us can do, and by doing it we can find our way to something that makes us happy.
Last summer, I tried something new: I stored the entire directory of materials for my database course in Git. This included all of my code, the course website, and everything else. It worked well.
The idea came from a post or tweet many years ago by Martin Fowler who, if I recall correctly, had put his entire home directory under version control. It sounded like the potential advantages might be worth the cost, so I made a note to try it myself sometime. I wasn't quite ready last summer to go all the way, so I took a baby step by creating my new course directory as a git repo and growing it file by file.
My context is pretty simple. I do almost all of my work on a personal MacBook Pro or a university iMac in my office. My main challenge is to keep my files in sync. When I make changes to a small number of files, or when the stakes of a missing file are low, copying files by hand works fine, with low overhead and no tooling necessary.
When I make a lot of changes in a short period of time, however, as I sometimes do when writing code or building my website, doing things by hand becomes more work. And the stakes of losing code or web pages are a lot higher than losing track of some planning notes or code I've been noodling with. To solve this problem, for many years I have been using rsync and a couple of simple shell scripts to manage code directories and my course web sites.
So, the primary goal for using Git in a new workflow was to replace rsync. Not being a Git guru, as many of you are, I figured that this would also force me to live with git more often and perhaps expand my tool set of handy commands.
My workflow for the semester was quite simple. When I worked in the office, there were four steps:
git merge laptop
git commit
git push
On my laptop, the opening and closing git commands changed:
git pull origin main
git commit
git push origin laptop
My work on a course is usually pretty straightforward. The most
common task is to create files and record information with
commit
. Every once in a while, I had to back up a step
with checkout
.
You may say, "But you are not using git for version control!" You
would be correct. The few times I checked out an older version of a
file, it was usually to eliminate a spurious conflict, say, a
.DS_Store
file that was out of sync. Locally, I don't
need a lot of version control, but using Git this way was a form of
distributed version control, making sure that, wherever I was working,
I had the latest version of every file.
I think this is a perfectly valid way to use Git. In some ways, Git is the new Unix. It provided me with a distributed filesystem and a file backup system all in one. The git commands ran effectively as fast as their Unix counterparts. My repo was not very much bigger than the directory would have been on its own, and I always had a personal copy of the entire repo with me wherever I went, even if I had to use another computer.
Before I started, several people reminded me that Git doesn't always work well with large images and binaries. That didn't turn out to be much of a problem for me. I had a couple of each in the repo, but they were not large and never changed. I never noticed a performance hit.
The most annoying hiccup all semester was working with OS X's
.DS_Store
files, which record screen layout information
for OS X. I like to keep my windows looking neat and occasionally
reorganize a directory layout to reflect what I'm doing.
Unfortunately, OS X seems to update these files at odd times, after
I've closed a window and pushed changes. Suddenly the two repos
would be out of sync only because one or more .DS_Store
files had changed after the fact. The momentary obstacle was quickly
eliminated with a checkout
or two before merging.
Perhaps I should have left the .DS_Store
s untracked...
All in all, I was pretty happy with the experience. I used more git, more often, than ever before and thus am now a bit more fluent than I was. (I still avoid the hairier corners of the tool, as all right-thinking people do whenever possible.) Even more, the repository contains a complete record of my work for the semester, false starts included, with occasional ruminations about troubles with code or lecture notes in my commit messages. I had a little fun after the semester ended looking back over some of those messages and making note of particular pain points.
The experiment went well enough that I plan to track my spring course in Git, too. This will be a bigger test. I've been teaching programming languages for many years and have a large directory of files, both current and archival. Not only are there more files, there are several binaries and a few larger images. I'm trying decide if I should put the entire folder into git all at once upfront or start with an empty folder a lá last semester and add files as I want or need them. The latter would be more work at early stages of development but might be a good way to clear out the clutter that has built up over twenty years.
If you have any advice on that choice, or any other, please let me know by email or on Twitter. You all have taught me a lot over the years. I appreciate it.
In a conversation with Tyler Cowen, economist Garett Jones said:
... my job in the classroom is not to teach the details of any theory. My job is to give students a reason to feel passionate enough about the topic so that they'll go home for two or three hours and study it on their own.
Perhaps this is a matter of context, but I don't think this assetion is entirely accurate. It might give the wrong impression to the uninitiated by leaving an essential complementary task implicit.
One could read this as saying that the instructor's job is purely one of motivation. Closures! Rah-rah! Get students excited enough to go learn everything about them on the own, and the instructor has succeeded.
If you think that's true, then I can introduce you to many students who have struggled or failed to learn something new despite being excited to learn and putting in a lot of time. They were missing some prerequisite knowledge or didn't have the experience they needed to navigate the complexities of a new area of study. In principle, if they plugged away at it long enough, they would eventually get there, but then why bother having an instructor at all?
So I think that, as instructor, I have two jobs. I do need to motivate students to put in the time and effort they need to study. Learning happens inside the student, and that requires personal study. I also, though, have to help create the conditions under which they can succeed. This involves all sorts of things: giving them essential background, pointing them toward useful resources, helping them practice the skills they'll need to learn effectively, and so on.
Motivation is in some ways a necessary part of the instructor's job. If students don't want to invest time in study and practice, then they will not learn much. But motivation is not sufficient. The instructor must also put the student in position to succeed to learn effectively.
In the middle of an old post about computing an "impossible" integral, John Cook says:
In the artificial world of the calculus classroom, everything works out nicely. And this is a shame.
When I was a student, I probably took comfort in the fact that everything was supposed to work out nicely on the homework we did. There *was* a solution; I just had to find the pattern, or the key that turned the lock. I suspect that I was a good student largely because I was good at finding the patterns, the keys.
It wasn't until I got to grad school that things really changed, and even then course work was typically organized pretty neatly. Research in the lab was very different, of course, and that's where my old skills no longer served me so well.
In university programs in computer science, where many people first learn how to develop software, things tend to work out nicely. That is a shame, too. But it's a tough problem to solve.
In most courses, in particular introductory courses, we create assignments with that have "closed form" solutions, because we want students to practice a specific skill or learn a particular concept. Having a fixed target can be useful in achieving the desired outcomes, especially if we want to help students build confidence in their abilities.
It's important, though, that we eventually take off the training wheels and expose students to messier problems. That's where they have an opportunity to build other important skills they need for solving problems outside the classroom, which aren't designed by a benevolent instructor to have follow a pattern. As Cook says, neat problems can create a false impression that every problem has a simple solution.
Students who go on to use calculus for anything more than artificial homework problems may incorrectly assume they've done something wrong when they encounter an integral in the wild.
CS students need experience writing programs that solve messy problems. In more advanced courses, my colleagues and I all try to extend students' ability to solve less neatly-designed problems, with mixed results.
It's possible to design a coherent curriculum that exposes students to an increasingly messy set of problems, but I don't think many universities do this. One big problem is that doing so requires coordination across many courses, each of which has its own specific content outcomes. There's never enough time, it seems, to teach everything about, say, AI or databases, in the fifteen weeks available. It's easier to be sure that we cover another concept than it is to be sure students take a reliable step along the path from being able to solve elementary problems to being able to solve to the problems they'll find in the wild.
I face this set of competing forces every semester and do my best to strike a balance. It's never easy.
Courses that involve large systems projects are one place where students in my program have a chance to work on a real problem: writing a compiler, an embedded real-time system, or an AI-based system. These courses have closed form solutions of sorts, but the scale and complexity of the problems require students to do more than just apply formulas or find simple patterns.
Many students thrive in these settings. "Finally," they say, "this is a problem worth working on." These students will be fine when they graduate. Other students struggle when they have to do battle for the first time with an unruly language grammar or a set of fussy physical sensors. One of my challenges in my project course is to help this group of students move further along the path from "student doing homework" to "professional solving problems".
That would be a lot easier to do if we more reliably helped students take small steps along that path in their preceding courses. But that, as I've said, is difficult.
This post describes a problem in curriculum design without offering any solutions. I will think more about how I try to balance the forces between neat and messy in my courses, and then share some concrete ideas. If you have any approaches that have worked for you, or suggestions based on your experiences as a student, please email me or send me a message on Twitter. I'd love to learn how to do this better.
I've written a number of posts over the years that circle around this problem in curriculum and instruction. Here are three:
I'm re-reading these to see if past me has any ideas for present-day me. Perhaps you will find them interesting, too.(This is an almost entirely personal entry. If that's not your thing, feel free to move on.)
Last Monday morning, I sat down and wrote a blog entry. It was no big deal, just an observation from some reading I've been doing. A personal note, a throwaway. I'm doing the same this morning.
I'd forgotten how good that can feel, which tells you something about my summer and fall.
Last spring, I wrote that I would be teaching a new course in the fall. I was pretty excited for the change of pace, even if it meant not teaching my compiler course this year, and was already thinking ahead to course content, possible modes of delivery in the face of Covid-19, and textbooks.
Then came summer.
Some of my usual department head tasks, like handling orientation sessions for incoming first-year students, were on the calendar, but the need to conduct them remotely expanded what used to be a few hours of work each week to a few hours each day. (It must have been even worse for the university staff who organize and run orientation!) Uncertainty due to the pandemic and the indecisiveness of upper administration created new work, such as a seemingly endless discussion of fall schedule, class sizes, and room re-allocation.
One of the effects of all this work was that, when August rolled around, I was not much better prepared to teach my class than I had been in May when I solicited everyone's advice.
Once fall semester started, my common refrain in conversations with friends was, "It feels like I'm on a treadmill." As soon as I finished preparing a week's in-class session, I had to prepare the week's online activity. Then there were homework assignments to write, and grade. Or I had to write an exam, or meet with student's to discuss questions or difficulties, made all the more difficult but the stress the pandemic placed on them. I never felt like I could take a day or hour off, and when I did, class was still there in my mind, reminding of all I had to do before another week began and the cycle began again.
That went on for fourteen weeks. I didn't feel out of sorts so much as simply always busy. It would be over soon enough, my rational mind told me.
When the semester ended at Thanksgiving, the treadmill of new work disappeared and all that was left was the grading. I did not rush that work, letting it spread over most of the week and a half I had before grades were due. I figured that it was time to decompress a bit.
After grades were in and I had time to get back to normal, I noticed some odd things happening. First of all I was sleeping a lot: generous naps most days, and a couple of Fridays where I was in bed for ten hours of rest (followed, predictably, by a nap later in the day). I'm no insomniac by nature, but this was much more sleep than I usually take, or need.
My workout data told a tale of change, too. My elliptical and bike performances had been steadily showing the small improvements of increased capability through May or so. They leveled off into the summer months, when I was able to ride outside more with my wife. Then fall started, and my performance levels declined steadily into November. The numbers started to bounce back in December, and I feel as strong as I've felt in a long while.
I guess fall semester hit me harder than I realized.
In most ways, I feel like I'm back to normal now. I guess we will find out next week, when my attention turns to spring semester, both as department head and instructor. At least I get to teach programming languages, where I have a deep collection of session materials in hand and years of thinking and practice to buoy me up. Even with continued uncertainty due to the pandemic, I'm in pretty good shape.
Another effect of the summer and fall was that my already too-infrequent blogging slowed to a trickle. The fate of most blogs is a lack of drama. For me, blogging tends to flag when I am not reading or doing interesting work. The gradual expansion of administrative duties over the last few years has certainly taken its toll. But teaching a new course usually energizes me and leads to more regularly writing here. That didn't happen this fall.
With the semester now over, I have a better sense of the stress I must have been feeling. It affected my sleep, my workouts, and my teaching. It's no surprise that it affected my writing, too.
One of my goals for the coming year is to seek the sort of conscious, intentional awakening of the senses that Gide alludes to the passage quoted by that blog post. I'm also going to pay better attention to the signs that the treadmill is moving too fast. Running faster isn't always the solution.
From this enlightening article that was being passed around a while back:
Talking posed a challenge for me. While my Mandarin was strong for someone who had grown up in the US, I wasn't fluent enough to express myself in the way I wanted. This had some benefits: I had to think before I spoke. I was more measured. I was a better listener. But it was also frustrating, as though I'd turned into a person who was meek and slow on the uptake. It made me think twice about the Chinese speakers at work or school in the US whom I'd judged as passive or retiring. Perhaps they were also funny, assertive, flirtatious, and profane in their native tongue, as I am in mine.
When people in the US talk about the benefits of learning a second language, they rarely, if ever, mention the empathy one can develop for others who speak and work in in a second language. Maybe that's because so few of us Americans learn a foreign language well enough to reach this level of enlightenment.
I myself learned just enough German in school to marvel at the accomplishment of exchange students studying here in their second language, knowing that I was nowhere near ready to live and study in a German-speaking land. Marvel, though, is not quite as valuable in this context as empathy.
Note: The following comes from the bottom of my previous post. It gets buried there beneath a lot of code and thinking out loud, but it's a message that stands on its own.
~~~~~
I demo'ed a variation of my database-briven passphrase generator to my students as we closed the course last week. It let me wrap up my time with them by reminding them that they are developing skills that can change how they see every problem they encounter in the future.
Knowing how to write programs gives you a new power. Whenever you encounter a problem, you can ask yourself, "How might a program help me solve this?"
The same is true for many more specialized CS skills. People who know how to create a language and implement an interpreter can ask themselves, "How might a language help me solve this problem?" That's one of the outcomes, I hope, of our course in programming languages.
The same is true for databases, too. Whenever you encounter a problem, you can ask yourself, "Can a database help me solve this?"
Computer science students can use the tools they learn each semester to represent and interpret information. That's a power they can use to solve many problems. It's easy to lose sight of that fact during a busy semester and worth reflecting on in calmer moments.
A long semester -- shorter in time than usual by more than a week, but longer psychologically than any in a long time -- is coming to an end. Teaching databases for the first time was a lot of fun, though the daily grind of preparing so much new material in real time wore me down. Fortunately, I like to program enough that there were moments of fun scattered throughout the semester as I played with SQLite for the first time.
In the spirit of the Three Bears pattern, I looked for opportunities all semester to use SQLite to solve a problem. When I read about the Diceware technique for generating passphrases, I found one.
Diceware is a technique for generating passphrases using dice to select words from the "Diceware Word List", in which each word is paired with a five digit number. All of the digits are between one and six, so five dice rolls are all you need to select a word from the list. Choose a number of words for the passphrase, roll your dice, and select your words.
The Diceware Word List is a tab-separated file of dice rolls and words. SQLite can import TSV data directly into a table, so I almost have a database. I had to preprocess the file twice to make it importable. First, the file is wrapped as a PGP signed message, so I stripped the header and footer by hand, to create diceware-wordlist.txt.
Second, some of the words in this list contain quote characters. Like many applications, SQLite struggles with CSV and TSV files that contain embedded quote characters. There may be some way to configure it to handle these files gracefully, but I didn't bother looking for one. I just replaced the ' and " characters with _ and __, respectively:
cat diceware-wordlist.txt \ | sed "s/\'/_/g" \ | sed 's/\"/__/g' \ > wordlist.txt
Now the file is ready to import:
sqlite> CREATE TABLE WordList( ...> diceroll CHAR(5), ...> word VARCHAR(30), ...> PRIMARY KEY(diceroll) ...> );sqlite> .mode tabs sqlite> .import 'wordlist.txt' WordList
sqlite> SELECT * FROM WordList ...> WHERE diceroll = '11113'; 11113 a_s
That's one of the words that used to contain an apostrophe.
So, I have a dice roll/word table keyed on the dice roll. Now I want to choose words at random from the table. To do that, I needed a couple of SQL features we had not used in class: random numbers and string concatenation. The random() function returns a big integer. A quick web search showed me this code to generate a random base-10 digit:
SELECT abs(random())%10 FROM (SELECT 1);which is easy to turn into a random die roll:
SELECT 1+abs(random())%6 FROM (SELECT 1);
I need to evaluate this query repeatedly, so I created a view that wraps the code in what acts, effectively, a function:
sqlite> CREATE VIEW RandomDie AS ...> SELECT 1+abs(random())%6 AS n ...> FROM (SELECT 1);
Aliasing the random value as 'n' is important because I need to string together a five-roll sequence. SQL's concatenation operator helps there:
SELECT 'Eugene' || ' ' || 'Wallingford';
I can use the operator to generate a five-character dice roll by selecting from the view five times...
sqlite> SELECT n||n||n||n||n FROM RandomDie; 21311 sqlite> SELECT n||n||n||n||n FROM RandomDie; 63535
... and then use that phrase to select random words from the list:
sqlite> SELECT word FROM WordList ...> WHERE diceroll = ...> (SELECT n||n||n||n||n FROM RandomDie); fan
Hurray! Diceware defaults to three-word passphrases, so I need to do this three times and concatenate.
This won't work...
sqlite> SELECT word, word, word FROM WordList ...> WHERE diceroll = ...> (SELECT n||n||n||n||n FROM RandomDie); crt|crt|crt... because the dice roll is computed only once. A view can help us here, too:
sqlite> CREATE VIEW OneRoll AS ...> SELECT word FROM WordList ...> WHERE diceroll = ...> (SELECT n||n||n||n||n FROM RandomDie);
OneRoll acts like a table that returns a random word:
sqlite> SELECT word FROM OneRoll; howdy sqlite> SELECT word FROM OneRoll; scope sqlite> SELECT word FROM OneRoll; snip
Almost there. Now, this query generates three-word passphrases:
sqlite> SELECT Word1.word || ' ' || Word2.word || ' ' || Word3.word FROM ...> (SELECT * FROM OneRoll) AS Word1, ...> (SELECT * FROM OneRoll) AS Word2, ...> (SELECT * FROM OneRoll) AS Word3; eagle crab pinch
Yea! I saved this query in gen-password.sql and saved the SQLite database containing the table WordList and the views RandomDie and OneRoll as diceware.db. This lets me generate passphrases from the command line:
$ sqlite3 diceware.db < gen-password.sql ywca maine overFinally, I saved that command in a shell script named gen-password, and I now have passphrase generator ready to use with a few keystrokes. Success.
Yes, this is a lot of work to get a simple job done. Maybe I could do better with Python and a CSV reader package, or some other tools. But that wasn't the point. I was revisiting SQL and learning SQLite with my students. By overusing the tools, I learned them both a little better and helped refine my sense of when they will and won't be helpful to me in the future. So, success.
~~~~~
I demo'ed a variation of this to my students on the last day of class. It let me wrap up my time with them by pointing out that they are developing skills which can change how they see every problem they encounter in the future.
Knowing how to write programs gives you a new power. Whenever you encounter a problem, you can ask yourself, "How might a program help me solve this?" I do this daily, both as faculty member and department head.
The same is true for many more specialized CS skills. People who know how to create a language and implement an interpreter can ask themselves, "How might a language help me solve this problem?" That's one of the outcomes, I hope, of our course in programming languages.
The same is true for databases. When I came across a technique for generating passphrases, I could ask myself, "How might a database help me build a passphrase generator?"
Computer science students can use the tools they learn each semester to represent and interpret information. That's a power they can use to solve many problems. It's easy to lose sight of this incredible power during a hectic semester, and worth reflecting on in calmer moments after the semester ends.
People often look at the difference between the highest-rated male chess player in a group and the highest-rated female chess player in the same group and conclude that there is a difference between the abilities of men and women to play chess, despite the fact that there are usually many, many more men in the group than women. But that's not even good evidence that there is an achievement gap. From What Gender Gap in Chess?:
It's really quite simple. Let's say I have two groups, A and B. Group A has 10 people, group B has 2. Each of the 12 people gets randomly assigned a number between 1 and 100 (with replacement). Then I use the highest number in Group A as the score for Group A and the highest number in Group B as the score for Group B. On average, Group A will score 91.4 and Group B 67.2. The only difference between Groups A and B is the number of people. The larger group has more shots at a high score, so will on average get a higher score. The fair way to compare these unequally sized groups is by comparing their means (averages), not their top values. Of course, in this example, that would be 50 for both groups -- no difference!
I love this paragraph. It's succinct and uses only the simplest ideas from probability and statistics. It's the sort of statistics that I would hope our university students learn in their general education stats course. While learning a little math, students can also learn about an application that helps us understand something important in the world.
The experiment described is also simple enough for beginning programmers to code up. Over the years, I've used problems like this with intro programming students in Pascal, Java, and Python, and with students learning Scheme or Racket who need some problems to practice on. I don't know whether learning science supports my goal, but I hope that this sort of problem (with suitable discussion) can do double duty for learners: learn a little programming, and learn something important about the world.
With educational opportunities like this available to us, we really should be able to turn graduates who have a decent understanding of why so many of our naive conclusions about the world are wrong. Are we putting these opportunities to good use?
I read two passages in the last few days that echo one another. First, I read this from Wallace Shawn, in a Paris Review interview:
But my God, without writers, humanity might be trapped in a swamp of idiotic, unchanging provincial clichés. Yes, there are writers who merely reinforce people's complacency, but a writer like Rachel Carson inspired the activism of millions, and writers like Lady Murasaki, Milton, and Joyce have reordered people's brains! And for any writers to exist at all, there must surely be a tradition of writing. Maybe in order for one valuable writer to exist, there must be a hundred others who aren't valuable at all, but it isn't possible at any given moment for anyone to be sure who the valuable one is.
Then, in his response to Marc Andreessen's "It's Time to Build", Tanner Greer writes:
To consistently create brilliant poets, you need a society awash in mediocre, even tawdry poetry. Brilliant minds will find their way towards poem writing when poem writing and poem reading is the thing that people do.
I once blogged briefly about The Art of Fear tells the story of an artist sketching hundreds of roosters, which laid the foundation for creating a single sketch for his client. For us as individuals, this means that "talent is rarely distinguishable, over the long run, from perseverance and lots of hard work." As Richard Gabriel often says, "Talent determines only how fast you get good, not how good you get". Volume creates the conditions under which quality can appear.
Shawn and Greer remind us that the same dynamic applies at the scale of a culture. A community that produces many writers has a better chance to produce great writers. When the community values writers, it increases the chances that someone will do the work necessary to becoming a writer. The best way to produce a lot of writers is to have a tradition that welcomes, even encourages, all members of the community to write, even if they aren't great.
The same applies to other forms of achievement, too. In particular, I think it applies to programming.
Sandi Metz's latest newsletter is about the heuristic not to name a class after the design pattern it implements. Actually, it's about a case in which Metz wanted to name a class after the pattern it implements in her code and then realized what she had done. She decided that she either needed to have a better reason for doing it than "because it just felt right" or she needed practice what she preaches to the rest of us. What followed was some deep thinking about what makes the rule a good one to follow and her efforts to put her conclusions in writing for the benefit of her readers.
I recognize with Metz's sense of discomfort at breaking a rule when it feels right and her need to step back and understand the rule at a deeper level. Between her set up and her explanation, she writes:
I've built a newsletter around this rule not only because I believe that it's useful, but also because my initial attempts to explain it exposed deep holes in my understanding. This was a revelation. Had I not been writing a book, I might have hand-waved around these gaps in my knowledge forever.
People sometimes say, "If you you really want to understand something, teach it to others." Metz's story is a great example of why this is really true. I mean, sure, you can learn any new area and then benefit from explaining it to someone else. Processing knowledge and putting it in your own words helps to consolidate knowledge at the surface. But the real learning comes when you find yourself in a situation where you realize there's something you've taken for granted for months or for years, something you thought you knew, but suddenly you sense a deep hole lying under the surface of that supposed understanding. "I just know breaking the rule is the right thing to do here, but... but..."
I've been teaching long enough to have had this experience many times in many courses, covering many areas of knowledge. It can be terrifying, at least momentarily. The temptation to wave my hands and hurry past a student's question is enormous. To learn from teaching in these moments requires humility, self-awareness, and a willingness to think and work until you break through to that deeper understanding. Learning from these moments is what sets the best teachers and writers apart from the rest of us.
As you might guess from Metz's reaction to her conundrum, she's a pretty good teacher and writer. The story in the newsletter is from the new edition of her book "99 Bottles of OOP", which is now available. I enjoyed the first edition of "99 Bottles" and found it useful in my own teaching. It sounds like the second edition will be more than a cleanup; it will have a few twists that make it a better book.
I'm teaching our database systems course for the first time ever this fall. This is a brand new prep for me: I've never taught a database course before, anywhere. There are so many holes in my understanding, places where I've internalized good practices but don't grok them in the way an expert does. I hope I have enough humility and self-awareness this semester to do my students right.
Rands recently wrote about his work-from-home routine. I love the idea of walking around a large wooded yard while doing audio meetings... One of his reasons for feeling so at ease struck a chord with me:
Everyone desperately wants to return to normality. I am a professional optimist, but we are not returning to normal. Ever. This is a different forever situation, and the sooner we realize that and start to plan accordingly, the sooner we will feel unstuck.
I have written or spoken a variation of this advice so many times over my fifteen years as department head, most often in the context of state funding and our university budget.
Almost every year for my first decade as head, we faced a flat or reduced budget, and every time several university colleagues expressed a desire to ride the storm out: make temporary changes to how we operate and wait for our budgets to return to normal. This was usually accompanied by a wistful desire that we could somehow persuade legislators of our deep, abiding value and thus convince them to allocate more dollars to the university or, failing that, that new legislators some future legislature would have different priorities.
Needless to say, the good old days never returned, and our budget remained on a downward slide that began in the late 1990s. This particular form of optimism was really avoidance of reality, and it led to many people living in a state of disappointment and discomfort for years. Fortunately, over the last five or ten years, most everyone has come to realize that what we have now is normal and has begun to plan accordingly. It is psychologically powerful to accept reality and begin acting with agency.
As for the changes brought on by the pandemic, I must admit that I am undecided about how much of what has changed over the last few months will be the normal way of the university going forward.
My department colleagues and I have been discussing how the need for separation among students in the classroom affects how we teach. Our campus doesn't have enough big rooms for everyone to move each class into a room with twice the capacity, so most of us are looking at ways to teach hybrid classes, with only half of our students in the classroom with us on any given day. This makes most of us sad and even a little depressed: how can we teach our courses as well as we always have in the past when new constraints don't allow us to do what we have optimized our teaching to do?
I have started thinking of the coming year in terms of hill climbing, an old idea from AI. After years of hard work and practice, most of us are at a local maximum in our teaching. The pandemic has disoriented us by dropping us at a random point in the environment. The downside of change in position is that we are no longer at our locally-optimal point for teaching our courses. The upside is that we get to search again under new conditions. Perhaps we can find a new local maximum, perhaps even one higher than our old max. If not, at least we have conducted a valuable experiment under trying conditions and can use what we learn going forward.
This analogy helps me approach my new course with more positive energy. A couple of my colleagues tell me it has helped them, too.
As many others have noted, the COVID-19 crisis has accelerated a few changes that were already taking place in our universities, in particular in the use of digital technology to engage students and to replace older processes. Of the other changes we've seen, some will certainly stick, but I'm not sure anyone really knows which ones. Part of the key to living with the uncertainty is not to tie ourselves too closely to what we did before.
![]() |
It's been a long time since I was excited by a new piece of software the way I was excited by Loglo, Avi Bryant's new creation. Loglo is "LOGO for the Glowforge", an experimental programming environment for creating SVG images. That's not a problem I need to solve, but the way Loglo works drew me in immediately. It consists of a stack programming language and a set of primitives for describing vector graphics, integrated into a spreadsheet interface. It's the use of a stack language to program a spreadsheet that excites me so much.
Actually, it's the reverse relationship that really excites me: using a spreadsheet to build and visualize a stack-based program. Long-time readers know that I am interested in this style of programming (see Summer of Joy for a post from last year) and sometimes introduce it in my programming languages course. Students understand small examples easily enough, but they usually find it hard to grok larger programs and to fully appreciate how typing in such a language can work. How might Loglo help?
In Loglo, a cell can refer to the values produced by other cells in the familiar spreadsheet way, with an absolute address such as "a1" or "f2". But Loglo cells have two other ways to refer to other cell's values. First, any cell can access the value produced by the cell to its left implicitly, because Loglo leaves the result of a cell's computation sitting on top of the stack. Second, a cell can access the value produced by the cell above it by using the special variable "^". These last two features strike me as a useful way for programmers to see their computations grow over time, which can be an even more powerful mode of interaction for beginners who are learning this programming style.
Stack-oriented programming of this sort is concatenative: programs are created by juxtaposing other programs, with a stack of values implicitly available to every operator. Loglo uses the stack as leverage to enable programmers to build images incrementally, cell by cell and row by row, referring to values on the stack as well as to predecessor cells. The programmer can see in a cell the value produced by a cumulative bit of code that includes new code in the cell itself. Reading Bryant's description of programming in Loglo, it's easy to see how this can be helpful when building images. I think my students might find it helpful when learning how to write concatenative programs or learning how types and programs work in a concatenative language.
For example, here is a concatenative program that works in Loglo as well as other stack-based languages such as Forth and Joy:
2 3 + 5 * 2 + 6 / 3 /
Loglo tells us that it computes the value 1.5:
This program consists of eleven tokens, each of which is a program in its own right. More interestingly, we can partition this program into smaller units by taking any subsequences of the program:
2 3 + 5 * 2 + 6 / 3 / --------- ------- ---These are the programs in cells A1, B1, and C1 of our spreadsheet. The first computes 25, the second uses that value to compute 4.5, and the third uses the 4.5 to compute 1.5. Notice that the programs in cells B1 and C1 require an extra value to do their jobs. They are like functions of one argument. Rather than pass an argument to the function, Loglo allows it to read a value from the stack, produced by the cell to its left.
By making the intermediate results visible to the programmer, this interface might help programmers better see how pieces of a concatenative program work and learn what the type of a program fragment such as 2 + 6 / (in cell B1 above) or 3 / is. Allowing locally-relative references on a new row will, as Avi points out, enable an incremental programming style in which the programmer uses a transformation computed in one cell as the source for a parameterized version of the transformation in the cell below. This can give the novice concatenative programmer an interactive experience more supportive than the usual REPL. And Loglo is a spreadsheet, so changes in one cell percolate throughout the sheet on each update!
Am I the only one who thinks this could be a really cool environment for programmers to learn and practice this style of programming?
Teaching concatenative programming isn't a primary task in my courses, so I've never taken the time to focus on a pedagogical environment for the style. I'm grateful to Avi for demonstrating a spreadsheet model for stack programs and stimulating me to think more about it.
For now, I'll play with Loglo as much as time permits and think more about its use, or use of a tool like it, in my courses. There are couple of features I'll have to get used to. For one, it seems that a cell can access only one item left on the stack by its left neighbor, which limits the kind of partial functions we can write into cells. Another is that named functions such as rotate push themselves onto the stack by default and thus require a ! to apply them, whereas operators such as + evaluate by default and thus require quotation in a {} block to defer execution. (I have an academic's fondness for overarching simplicity.) Fortunately, these are the sorts of features one gets used to whenever learning a new language. They are part of the fun.
Thinking beyond Loglo, I can imagine implementing an IDE like this for my students that provides features that Loglo's use cases don't require. For example, it would be cool to enable the programmer to ctrl-click on a cell to see the type of the program it contains, as well as an option to see the cumulative type along the row or built on a cell referenced from above. There is much fun to be had here.
To me, one sign of a really interesting project is how many tangential ideas flow out of it. For me, Loglo is teeming with ideas, and I'm not even in its target demographic. So, kudos to Avi!
Now, back to administrivia and that database course...
There's value to going into a field that you find difficult to grasp, as long as you're willing to be persistent. Even better, others can benefit from your persistence, too.
In an old essay, James Propp notes that working in a field where you lack intuition can "impart a useful freedom from prejudice". Even better...
... there's value in going into a field that you find difficult to grasp, as long as you're willing to be really persistent, because if you find a different way to think about things, something that works even for someone like you, chances are that other people will find it useful too.
This reminded me of a passage in Bob Nystroms's post about his new book, Crafting Interpreters. Nystrom took a long time to finish the book in large part because he wanted the interpreter at the end of each chapter to compile and run, while at the same time growing into the interpreter discussed in the next chapter. But that wasn't the only reason:
I made this problem harder for myself because of the meta-goal I had. One reason I didn't get into languages until later in my career was because I was intimidated by the reputation compilers have as being only for hardcore computer science wizard types. I'm a college dropout, so I felt I wasn't smart enough, or at least wasn't educated enough to hack it. Eventually I discovered that those barriers existed only in my mind and that anyone can learn this.
Some students avoid my compilers course because they assume it must be difficult, or because friends said they found it difficult. Even though they are CS majors, they think of themselves as average programmers, not "hardcore computer science wizard types". But regardless of the caliber of the student at the time they start the course, the best predictor of success in writing a working compiler is persistence. The students who plug away, working regularly throughout the two-week stages and across the entire project, are usually the ones who finish successfully.
One of my great pleasures as a prof is seeing the pride in the faces of students who demo a working compiler at the end of the semester, especially in the faces of the students who began the course concerned that they couldn't hack it.
As Propp points out in his essay, this sort of persistence can pay off for others, too. When you have to work hard to grasp an idea or to make something, you sometimes find a different way to think about things, and this can help others who are struggling. One of my jobs as a teacher is to help students understand new ideas and use new techniques. That job is usually made easier when I've had to work persistently to understand the idea myself, or to find a better way to help the students who teach me the ways in which they struggle.
In Nystrom's case, his hard work to master a field he didn't grasp immediately pays of for his readers. I've been following the growth of Crafting Interpreters over time, reading chapters in depth whenever I was able. Those chapters were uniformly easy to read, easy to follow, and entertaining. They have me thinking about ways to teach my own course differently, which is probably the highest praise I can give as a teacher. Now I need to go back and read the entire book and learn some more.
Teaching well enough that students grasp what they thought was not graspable and do what they thought was not doable is a constant goal, rarely achieved. It's always a work in progress. I have to keep plugging away.
"What am I really sick of?" is where innovation begins.
For a lot of entrepreneurs, they see something and they say, "I have to have this," and that will start them building their own.
Necessity is the mother of invention, so our willingness to solve problems is about to surge.
A lot of people are facing a lot of different stresses right now, with the prospect that many of those stresses will continue on into the foreseeable future. For instance, I know a lot of CS faculty who are looking at online instruction and remote learning much carefully now that they may be doing it again in the fall. Many of us have some things to learn, and some real problems need to be solved.
"What am I really sick of?" can turn the dial up on our willingness to solve problems that have been lingering in the background for a while. Let's hope that some good can come from the disruption.
As a financial writer for Forbes and the Wall Street Journal, Jason Zweig has received a lot of letters from readers, many of them forcefully suggesting that his columns could be better. In this interview, he speaks calmly about processing negative feedback:
... when you get negative feedback, you have to sort it. You can't just take all negative feedback and throw it in the "I'm not reading this" bucket. You have to go through it. And you have to say: "Is this person, who says I'm wrong, right or wrong?" Because if the person says you're wrong, and is wrong, then how does that hurt you? But if the person who says you're wrong is right, it's devastating to you if you don't listen.
It's not about winning. It's about learning.
I know profs who refuse to read their student assessments because it's too hard emotionally to deal with negative feedback. I understand the temptation... There are semesters when thirty-nine reviews are positive, yet the one negative review lodges itself in my brain and won't let go. Even after decades of teaching, it can be hard to shake off those comments immediately. And when there many comments that are "constructive" or just plain negative, well, reading the assessments can really demoralize.
But as Zweig says, closing myself off to the feedback is ultimately a losing proposition. Sometimes I assess a comment and decide that it's off the mark, or the result of singular event or experience and therefore isn't worth sweating over. But what about when the reviewer is right? Or when there's a kernel of truth in an otherwise unnecessarily personal comment? Ignoring the truth doesn't do me any good. I want to get better.
I did not receive student assessments this spring. When the university moved to remote instruction suddenly, the administration and faculty agreed to suspended assessments for the semester, with the idea that teaching and learning would both be a bit bumpier than usual under the extreme conditions. Just before the last week of the term, they agreed to bring optional assessments back purely for the prof's personal use, but by then I decided to pass. Some of my students provided some helpful feedback, including constructive criticism, all on their own.
I'll actually miss reading my assessments this month, if not the sudden spike in my blood pressure that sometimes accompanies them. Students are usually helpful and surprisingly generous in their evaluations, and I still usually learn a lot from the positive ones and the negative ones alike.
Steve Wozniak in Founders at Work:
If you can just quickly whip something out and it's done, maybe it's time, once in a while, to think and think and think, "Can I make it better than it is, a little superior?" What that does is not necessarily make the product better in the end, but it brings you closer to the product, and your own head understands it better. Your neurons have gone through the code you wrote, or the circuits you designed, have gone through it more times, and it's just a little more solidly in your head and once in a while you'll wake up and say, "Oh my God, I just realized a bug that's in there, something I hadn't thought of."
Or, if you have to modify something, or add something new, you can do it very quickly when it's all in your head. You don't have to pull out the listing and find out where and maybe make a mistake. You don't make as many mistakes.
Many programmers know this feeling, of having a program in your head and moving in sync with it. When programs are small, it's easy for me to hold a program in my head. As it grows larger and spreads out over many functions, classes, and files, I have to live with it over an extended period of time. Taking one of Woz's dives into the just to work on it is a powerful way to refresh the feeling.
Beginning programmers have to learn this feeling, I think, and we should help them. In the beginning, my students know what it's like to have a program in their head all at once. The programs are small, and the new programmer has to pay attention to every detail. As programs grow, it becomes harder for them. They work locally to make little bits of code work, and suddenly they have a program that does fit naturally in their head. But they don't have the luxury of time to do what Woz suggests, because they are on to the next reading assignment, the next homework, the next class across campus.
One of the many reasons I like project courses such as my compiler course is that students live with the same code for an entire semester. Sure, they finish the scanner and move on to the parser, and then onto a type checker and a code generator, but they use their initial stages every day and live with the decisions they made. It's not uncommon for a student to tell me 1/2 or 3/4 of the way through the course, "I was looking at our scanner (or parser) the other day, and now I understand why we were having that problem I told you about. I'm going to fix it over Thanksgiving break."
In my programming languages course, we close with a three week three assignment project building an interpreter. I love when a student submitting on Part 3 says, "Hey, I just noticed that some parts of Part 2 could be better. I hope you don't mind that I improved that, too." Um, no. No, I don't mind at all. They get it.
It's easy to shortchange our students with too many small projects. I like most of my courses to have at least some code grow over the course of the semester. Students may not have the luxury of a lot of free time, but at least they work in proximity to their old code for a while. Serendipity may strike if we create the right conditions for it.
I have already begun to think about how I can foster this in my new course this fall. I hope to design it into the course upfront.
I blogged weekly about the sudden switch to remote instruction starting in late March, but only for three weeks. I stopped mostly because my sense of disorientation had disappeared. Teaching class over Zoom started to feel more normal, and my students and I got back into the usual rhythm. A few struggled in ways that affected their learning and performance, and a smaller few thrived. My experience was mostly okay: some parts of my work suffered as I learned how to use tools effectively, but not having as many external restrictions on my schedule offset the negatives. Grades are in, summer break begins to begin, and at least some things are right with the world.
Fall offers something new for me to learn. My fall compilers course had a lower enrollment than usual and, given the university's current financial situation, I had to cancel it. This worked out fine for the department, though, as one of our adjunct instructors asked to take next year off in order to deal with changes in his professional and personal lives. So there was a professor in need of a course, and a course in need of a professor: Database Systems.
Databases is one of the few non-systems CS courses that I have never taught as a prof or as a grad student. It's an interesting course, mixing theory and design with a lot of practical skills that students and employers prize. In this regard, it's a lot of like our OO design and programming course in Java, only with a bit more visible theory. I'm psyched to give it a go. At the very least, I should be able to practice some of those marketable skills and learn some of the newer tools involved.
As with all new preps, this course has me looking for ideas. I'm aware of a few of the standard texts, though I am hoping to find a good open-source text online, or a set of online materials out of which to assemble the readings my students will need for the semester. I'm going to be looking scouting for all the other materials I need to teach the course as well, including examples, homework assignments, and projects. I tend to write a lot of my own stuff, but I also like to learn from good courses and good examples already out there. Not being a database specialist, I am keen to see what specialists think is important, beyond what we find in traditional textbooks.
Then there is the design of the course itself. Teaching a course I've never taught before means not having an old course design to fall back on. This means more work, of course, but is a big win for curious mind. Sometimes, it's fun to start from scratch. I have always found instructional design fascinating, much like any kind of design, and building a new course leaves open a lot of doors for me to learn and to practice some new skills.
COVID-19 is a big part of why I am teaching this course, but it is not done with us. We still do not know what fall semester will look like, other than to assume that it won't look like a normal semester. Will be on campus all semester, online all semester, or a mix of both? If we do hold instruction on campus, as most universities are hoping to do, social distancing requirements will require us to do some things differently, such as meeting students in shifts every other day. This uncertainty suggests that I should design a course that depends less on synchronous, twice-weekly, face-to-face direct instruction and more on ... what?
I have a lot to learn about teaching this way. My university is expanding its professional development offerings this summer and, in addition to diving deep into databases and SQL, I'll be learning some new ways to design a course. It's exciting but also means a bit more teaching prep than usual for my summer.
This is the first entirely new prep I've taught in a while. I think the most recent was the fall of 2009, when I taught Software Engineering for the first and only time. Looking back at the course website reminds me that I created this delightful logo for the course:
So, off to work I go. I could sure use your help. Do you know of model database courses that I should know about? What database concepts and skills should CS graduates in 2021 know? What tools should they be able to use? What has changed in the world since I last took database courses that must be reflected in today's database course? Do you know of a good online textbook for the course, or a print book that my students would find useful and be willing to pay for?
If you have any ideas to share, feel free to email me or contact me on Twitter. If not for me, do it for my students!
This week I read one of Craig Mod's old essays and found a great line, one that everyone who writes programs for other people should keep front of mind:
When it comes to software that people live in all day long, a 3% increase in fun should not be dismissed.
Working hard to squeeze a bit more speed out of a program, or to create even a marginally better interaction experience, can make a huge difference to someone who uses that program everyday. Some people spend most of their professional days inside one or two pieces of software, which accentuates further the human value of Mod's three percent. With shelter-in-place and work-from-home the norm for so many people these days, we face a secondary crisis of software that is no fun.
I was probably more sensitive than usual to Mod's sentiment when I read it... This week I used Blackboard for the first time, at least my first extended usage. The problem is not Blackboard, of course; I imagine that most commercial learning management systems are little fun to use. (What a depressing phrase "commercial learning management system" is.) And it's not just LMSes. We use various PeopleSoft "campus solutions" to run the academic, administrative, and financial operations on our campus. I always feel a little of my life drain away whenever I spend an hour or three clicking around and waiting inside this large and mostly functional labyrinth.
It says a lot that my first thought after downloading my final exams on Friday morning was, "I don't have to login to Blackboard again for a good long while. At least I have that going for me."
I had never used our LMS until this week, and then only to create a final exam that I could reliably time after being forced into remote teaching with little warning. If we are in this situation again in the fall, I plan to have an alternative solution in place. The programmer in me always feels an urge to roll my own when I encounter substandard software. Writing an entire LMS is not one of my life goals, so I'll just write the piece I need. That's more my style anyway.
Later the same morning, I saw this spirit of writing a better program in a context that made me even happier. The Friday of finals week is my department's biennial undergrad research day, when students present the results of their semester- or year-long projects. Rather than give up the tradition because we couldn't gather together in the usual way, we used Zoom. One student talked about alternative techniques for doing parallel programming in Python, and another presented empirical analysis of using IR beacons for localization of FIRST Lego League robots. Fun stuff.
The third presentation of the morning was by a CS major with a history minor, who had observed how history profs' lectures are limited by the tools they had available. The solution? Write better presentation software!
As I watched this talk, I was proud of the student, whom I'd had in class and gotten to know a bit. But I was also proud of whatever influence our program had on his design skills, programming skills, and thinking. This project, I thought, is a demonstration of one thing every CS student should learn: We can make the tools we want to use.
This talk also taught me something non-technical: Every CS research talk should include maps of Italy from the 1300s. Don't dismiss 3% increases in fun wherever they can be made.
As I closed down my remote class session yesterday, I felt a familiar feeling... That session can be better! I've been using variations of this session, slowly improving it, for a few years now, and I always leave the classroom thinking, "Wait 'til next time." I'm eager to improve it now and iterate, trying it again tomorrow. Alas, tomorrow is another day, with another class session all its own. Next time is next year.
![]() |
I feel this way about most of the sessions in most of my courses. Yesterday, it occurred to me that this must be what Phil Connors feels like in Groundhog Day.
Phil wakes up every day in the same place and time as yesterday. Part way through the film, he decides to start improving himself. Yet the next morning, there he is again, in the same place and time as yesterday, a little better but still flawed, in need of improvement.
Next spring, when I sit down to prep for this session, it will be like hitting that alarm clock and hearing Sonny and Cher all over again.
I told my wife about my revelation and my longing: If only I could teach this session 10,000 times, I'd finally get it right. You know what she said?
"Think how your students must feel. If they could do that session 10,000 times, they'd feel like they really got it, too."
My wife is wise. My students and I are in this together, getting a little better each day, we hope, but rarely feeling like we've figured all that much out. I'll keep plugging away, Phil Connors as CS prof. "Okay, campers, rise and shine..." Hopefully, today I'll be less wrong than yesterday. I wish my students the same.
Who knows, one of these days, maybe I'll leave a session and feel as Phil does in the last scene of the film, when he wakes up next to his colleague Rita. "Do you know what today is? Today is tomorrow. It happened. You're here." I'm not holding my breath, though.
Early in this Paris Review interview, Ray Bradbury says, "A conglomerate heap of trash, that's what I am." I smiled, because that's what I feel like sometimes, both culturally and academically. Later he confessed something that sealed my sense of kinship with him:
I am a librarian. I discovered me in the library. I went to find me in the library. Before I fell in love with libraries, I was just a six-year-old boy. The library fueled all of my curiosities, from dinosaurs to ancient Egypt.
![]() |
I was a library kid, too. I owned a few books, but I looked forward to every chance we had to go to the library. My grade school had books in every classroom, and my teachers shared their personal books with those of us who so clearly loved to read. Eventually my mom took me and my siblings to the Marion County public to get a library card, and the world of books available seemed limitless. When I got to high school, I spent free time before and after classes wandering the stacks, discovering science fiction, Vonnegut and Kafka and Voltaire, science and history. The school librarian got used to finding me in the aisles at times. She became as much a friend as any high school teacher could. So many of my friends have shelves and shelves of books; they talk about their addiction to Amazon and independent bookstores. But almost all of the books I have at home fit in a single bookshelf (at right). One of them is Bradbury's The Martian Chronicles, which I discovered in high school.
I do have a small chess library on another shelf across the room and a few sports books, most from childhood, piled nearby. I tried to get rid of the sports books once, in a fit of Marie Kondo-esque de-cluttering, but I just couldn't. Even I have an attachment to the books I own. Having so few, perhaps my attraction is even stronger than it might otherwise be, subject to some cosmic inverse square law of bibliophilia.
At my office, I do have two walls full of books, mostly textbooks accumulated over my years as a professor. When I retire, though, I'll keep only one bookcase full of those -- a few undergrad CS texts, yes, but mostly books I purchased because they meant something to me. Gödel, Escher, Bach. Metamagical Themas. Models of My Life. A few books about AI. These are books that helped me find me.
After high school, I was fortunate to spend a decade in college as an undergraduate and grad student. I would not trade those years for anything; I learned a lot, made friends with whom I remain close, and grew up. Bradbury, though, continued his life as an autodidact, going to the public library three nights a week for a decade, until he got married.
So I graduated from the library, when I was twenty-seven. I discovered that the library is the real school.
Even though I spent a decade as a student in college and now am a university prof, the library remains my second home. I rarely buy books to this day; I don't remember my last purchase. The university library is next to my office building, and I make frequent trips over in the afternoons. They give me a break from work and a chance to pick up my next read. I usually spend a lot more time there than necessary, wandering the stacks and exploring. I guess I'm still a library kid.
First the good news: after three more sessions, I am less despondent than I was after Week Two. I have taken my own advice from Week One and lowered expectations. After teaching for so many years and developing a decent sense of my strengths and weaknesses in the classroom, this move took me out of my usual groove. It was easy to forget in the rush of the moment not to expect perfection, and not being able to interact with students in the same way created different emotions about the class sessions. Now that I have my balance back, things feel a bit more normal.
Part of what changed things for me was watching the videos I made of our class sessions. I quickly realized that these sessions are no worse than my usual classes! It may be harder for students to pay attention to the video screen for seventy-five minutes in the same way they might pay attention in the classroom, but my actual presentation isn't all that different. That was comforting, even as I saw that the videos aren't perfect.
Another thing that comforted me: the problems with my Zoom sessions are largely the same as the problems with my classroom sessions. I can fall into the habit of talking too much and too long unless I carefully design exercises and opportunities for students to take charge. The reduced interaction channel magnifies this problem slightly, but it doesn't create any new problems in principle. This, too, was comforting.
For example, I notice that some in-class exercises work better than others. I've always know this from my in-person course sessions, but our limited interaction bandwidth really exposes problems that are at the wrong level for where the students are at the moment (for me, usually too difficult, though occasionally too easy). I am also remembering the value of the right hint at the right moment and the value of students interacting and sharing with one another. Improving on these elements of my remote course should result in corresponding improvements when we move back to campus.
I have noticed one new problem: I tend to lose track of time more easily when working with the class in Zoom, which leads me to run short on time at the end of the period. In the classroom, I glance at a big analog clock on the wall at the back of the room and use that to manage my time. My laptop has a digital clock in the corner, but it doesn't seem to help me as much. I think this is a function of two parameters: First, the clock on my computer is less obtrusive, so I don't look at it as often. Second, it is a digital clock. I feel the geometry of analog time viscerally in a way that I don't with digital time. Maybe I'm just old, or maybe we all experience analog clocks in a more physical way.
I do think that watching my lectures can help me improve my teaching. After Week One, I wondered, "In what ways can going online, even for only a month and a half, improve my course and materials?" How might this experience make me a better teacher or lead to better online materials? I have often heard advice that I should record my lectures so that I could watch them with an experienced colleague, with an eye to identifying strengths to build on and weaknesses to improve on. Even without a colleague to help, this few weeks of recording gives me a library of sessions I can use for self-diagnosis and improvement.
Maybe this experience will have a few positives to counterbalance its obvious negatives.
Earlier in the week I read this article by Jason Fried and circled these sentences:
Ultimately this major upheaval is an opportunity. This is a chance for your company, your teams, and individuals to learn a new skill. Working remotely is a skill.
After two weeks of the great COVID-19 school-from-home adventure, I very much believe that teaching remotely is a skill -- one I do not yet have.
Last week I shared a few thoughts about my first week teaching online. I've stopped thinking of this as "teaching online", though, because my course was not designed as an online course. It was pushed online, like so many courses everywhere, in a state of emergency. The result is a course that has been optimized over many years for face-to-face synchronous interaction being taken by students mostly asynchronously, without much face-to-face interaction.
My primary emotion after my second week teaching remotely is disappointment. Switching to this new mode of instruction was simple but not easy, at least not easy to do well. It was simple because I already have so much textual material available online, including detailed lecture notes. For students who can read the class notes, do exercises and homework problems, ask a few questions, and learn on their own, things seem to be going fine so far. (I'll know more as more work comes in for evaluation.) But approximately half of my students need more, and I have not figured out yet how best to serve them well.
I've now hosted four class sessions via Zoom for students who were available at class time and interested or motivated enough to show up. With the exception of one student, they all keep their video turned off, which offers me little or no visual feedback. Everyone keeps their audio turned off except when speaking, which is great for reducing the distraction of noises from everybody's homes and keyboards. The result, though, is an eerie silence that leaves me feeling as if I'm talking to myself in a big empty room. As I told my students on Thursday, it's a bit unnerving.
With so little perceptual stimulus, time seems to pass quickly, at least for me. It's easy to talk for far too long. I'm spared a bit by the fact that my classes intersperse short exposition by me with interactive work: I set them a problem, they work for a while, and then we debrief. This sort of session, though, requires the students to be engaged and up to date with their reading and homework. That's hard for me to expect of them under the best of conditions, let alone now when they are dispersed and learning to cope with new distractions.
After a few hours of trying to present material online, I very much believe that this activity requires skill and experience, of which I have little of either at this point. I have a lot of work to do. I hope to make Fried proud and use this as an opportunity to learn new skills.
I had expected by this point to have created more short videos that I could use to augment my lecture notes, for students who have no choice but to work on the course whenever they are free. Time has been in short supply, though, with everything on campus changing all at once. Perhaps if I can make a few more videos and flip the course a bit more, I will both serve those students better and find a path toward using our old class time better for the students who show up then and deserve a positive learning experience.
At level of nuts and bolts, I have already begun to learn some of the details of Panopto, Zoom, and our e-learning system. I like learning new tools, though the complications of learning them all at once and trying to use them at the same time makes me feel like a tech newbie. I guess that never changes.
The good news is that other parts of the remote work experience are going better, sometimes even well. Many administrative meetings work fine on Zoom, because they are mostly about people sharing information and reporting out. Most don't really need to be meetings anyway, and participating via Zoom is an improvement over gathering in a big room. As one administrator said at the end of one meeting recently, "This was the first council meeting online and maybe the shortest council meeting ever." I call that a success.
It's been a long time since I've written here. In fits and starts over a couple of weeks, we went from "Wow, coronavirus is in the news a lot" to "all teaching is online through the end of summer, and Eugene has a lot of work to do". The new work included rearranging faculty candidates who were not allowed to fly to campus, looking to cover courses for a faculty who would be taking leave before the end of the semester, and -- of course -- moving my own course online.
This is new territory for me. I've never taught online, and I've never made videos for my students before. One bit of good news is that I have a lot of textual material online already: detailed lecture notes, exercises, homework assignments, every line of code we write or examine in class, all my exams, and a surprising amount of bonus material and extra reading. (Few students take advantage of the extra stuff, but I can't bring myself not provide links to it for the few who do.)
Over break, I made my first video, walking through a problem from our last homework assignment before break. I recorded three takes before having something I was willing to show students. It is rough and wordy, but not too bad for a first effort (well, v 1.03).
We now have a week of classes in the books. This is an unusual week in my course even under typical conditions. It consists a two-day in-class exercise on which students worked in groups of two or three to write a lexical addresser for a little language. With students segregated in their own apartments and hometowns, the intended group collaboration disappeared. Most of our students are still adjusting to the new learning conditions and figuring out how to take all of their courses online at home.
On Tuesday, I made one short video to review briefly the ideas about variable declarations and references that we had studied before break. After they watched the video, I turned them loose with my usual session notes, modified so that hints I would typically offer in class and document in a web page for later review hidden away on their own web pages. This way, students could work on the problem and only click on a hint when they needed a nudge. A few students showed up in a Zoom room and worked during the assigned class time, so I was able to interact with them in real time and answer questions. On Thursday, we re-oriented ourselves to the problem, and I live-coded a solution to the problem. I recorded the session for students to watch later, at their leisure.
I've already learned a bit, only one week in. The temptation to write a long piece of code and talk through the process is great. The students who braved Thursday's session let me know that they tuned out after a while, only to bring their attention back later, somewhat out of synch with my presentation. That's not much different from what happens in a long lecture, of course, but when I'm standing in a room with the students, I'm more likely to notice their boredom or distraction and step out of the lecture sooner. Talking to a few students in Zoom, especially when they have their video off, made it too easy to talk and talk without a break. Over many years of teaching face-to-face, I've learned how to mix class up a bit and not get into long ruts. I'll have to be more consciously aware of those lessons as I do demos over Zoom.
I'm still a little nervous about the rest of the semester, but also a little excited. In what ways can going online, even for only a month and a half, improve my course and materials? Putting session notes up over the years has already forced me to be more careful in how I write explanations and more detailed in the code I post. Now, with little or no face-to-face interaction (most students will come to a session after it ends), how will I need to improve my presentation and the materials I provide? How much will my inexperience making videos limit the level of quality I can achieve over six weeks?
For now, I plan to keep things as simple as possible. I'm not sure what technology my students have available to them at home, or the conditions under which they must work. I am still using email and my course website (a very simple static site that would feel at home in 2000) as my main ways to present material, and have added Zoom as a to interact with students in lieu of regular class time. I will make a few short video to demonstrate ideas, to augment the written material and give students another sensory input with which to learn. I don't expect the videos to be great, or even good, but I hope they help my students. I also hope that, with practice, I get better at making them.
Sadly, I won't be in a classroom with this group of students again this year. I'll miss that. They are good people, and I enjoy working with them. This is a strange way to end a school year, and a reminder of how fortunate I am to get to teach.
... or at least differently. From Inside Google's Efforts to Engineer Its Food for Healthiness:
So, instead of merely changing the food, Bakker changed the foodscape, ensuring that nearly every option at Google is healthy -- or at least healthyish -- and that the options that weren't stayed out of sight and out of mind.
This is how I've been thinking lately about teaching functional programming to my students, who have experience in procedural Python and object-oriented Java. As with deciding what we eat, how we think about problems and programs is mostly unconscious, a mixture of habit and culture. It is also something intimate and individual, perhaps especially for relative beginners who have only recently begun to know a language well enough to solve problems with some facility. But even for us old hands, the languages and mental tools we use to write programs can become personal. That makes change, and growth, hard.
In my last few offerings of our programming languages course, in which students learn Racket and functional style, I've been trying to change the programming landscape my students see in small ways whenever I can. Here are a few things I've done:
By keeping functional tools closer at hand, I'm trying to make it easier for these tools to become the new habit. I've also noticed how the way I speak about problems and problem solving can subtly shape how students approach problems, so I'm trying to change a few of my own habits, too.
It's hard for me to do all these things, but it's harder still when I'm not thinking about them. This feels like progress.
So far students seem to be responding well, but it will be a while before I feel confident that these changes in the course are really helping students. I don't want to displace procedural or object-oriented thinking from their minds, but rather to give them a new tool set they can bring out when they need or want to.
At one point in this conversation between Elvis Costello and Tom Waits, the two songwriters discuss some of the difficulties they face in the creative process. Costello remarks that he sometimes writes notes that he can't sing, only to be disappoint himself when he gets into the studio. Waits commiserates:
Anything that has to travel all the way down from your cerebellum to your fingertips, there's a lot of things that can happen on the journey. Sometimes I'll listen to records, my own stuff, and I think god, the original idea for this was so much better than the mutation that we arrived at. What I'm trying to do now is get what comes and keep it alive. It's like carrying water in your hands. I want to keep it all, and sometimes by the time you get to the studio you have nothing.
This is something I notice all the time when I'm teaching. I'll have an idea for class somewhere: walking home, riding the exercise bike, reading something. My brain dives into the process of making the idea real: telling a story, writing code, explaining an idea in relation to things we've done in class before. Then comes deployment. We get into the classroom and... it feels flat. On rare occasions the new ideas bombs, but most often it just seems not quite right. It doesn't feel like what I had in my mind when I first had the idea, and I'm not sure how we ended up feeling the way we feel in class. The idea was so perfect.
Teaching ideas are abstraction. Teaching is concrete. It involves other humans. Their learning is what's important, and sometimes the idea doesn't make the connection intended by the teacher. The classroom is where reality sets in.
There is good news. Sometimes a disappointing or failed idea can be salvaged by analyzing the experience and redesigning the session. But most of the time it never feels as perfect as it did in my head at the moment of conception. Part of the art of becoming a teaching is making friends with this fact and moving forward.
I saw Robin Sloan's An App Can Be a Home-Cooked Meal floating around Twitter a few days back. It really is quite good; give it a read if you haven't already. This passage captures a lot of the essay's spirit in only a few words:
The exhortation "learn to code!" has its foundations in market value. "Learn to code" is suggested as a way up, a way out. "Learn to code" offers economic leverage, a squirt of power. "Learn to code" goes on your resume.
But let's substitute a different phrase: "learn to cook." People don't only learn to cook so they can become chefs. Some do! But far more people learn to cook so they can eat better, or more affordably, or in a specific way. Or because they want to carry on a tradition. Sometimes they learn just because they're bored! Or even because -- get this -- they love spending time with the person who's teaching them.
Sloan expresses better than I ever have an idea that I blog about every so often. Why should people learn to program? Certainly it offers a path to economic gain, and that's why a lot of students study computer science in college, whether as a major, a minor, or a high-leverage class or two. There is nothing wrong with that. It is for many a way up, a way out.
But for some of us, there is more than money in programming. It gives you a certain power over the data and tools you use. I write here occasionally about how a small script or a relatively small program makes my life so much easier, and I feel bad for colleagues who are stuck doing drudge work that I jump past. Occasionally I'll try to share my code, to lighten someone else's burden, but most of the time there is such a mismatch between the worlds we live in that they are happier to keep plugging along. I can't say that I blame them. Still, if only they could program and used tools that enabled them to improve their work environments...
But... There is more still. From the early days of this blog, I've been open with you all:
Here's the thing. I like to write code.
One of the things that students like about my classes is that I love what I do, and they are welcome to join me on the journey. Just today a student in my Programming Languages drifted back to my office with me after class , where we ended up talking for half an hour and sketching code on a whiteboard as we deconstructed a vocabulary choice he made on our latest homework assignment. I could sense this student's own love of programming, and it raised my spirits. It makes me more excited for the rest of the semester.
I've had people come up to me at conferences to say that the reason they read my blog is because they like to see someone enjoying programming as much as they do. many of them share links with their students as if to say, "See, we are not alone." I look forward to days when I will be able to write in this vein more often.
Sloan reminds us that programming can be -- is -- more than a line on a resume. It is something that everyone can do, and want to do, for a lot of different reasons. It would be great if programming "were marbled deeply into domesticity and comfort, nerdiness and curiosity, health and love" in the way that cooking is. That is what makes Computing for All really worth doing.
In 1957, Dan McCracken published Digital Computer Programming, perhaps the first book on the new art of programming. His book shows that the roots of extreme programming run deep. In this passage, McCracken encourages both the writing of tests before the writing of code and the involvement of the customer in the software development process:
The first attack on the checkout problem may be made before coding is begun. In order to fully ascertain the accuracy of the answers, it is necessary to have a hand-calculated check case with which to compare the answers which will later be calculated by the machine. This means that stored program machines are never used for a true one-shot problem. There must always be an element of iteration to make it pay. The hand calculations can be done at any point during programming. Frequently, however, computers are operated by computing experts to prepare the problems as a service for engineers or scientists. In these cases it is highly desirable that the "customer" prepare the check case, largely because logical errors and misunderstandings between the programmer and customer may be pointed out by such procedure. If the customer is to prepare the test solution is best for him to start well in advance of actual checkout, since for any sizable problem it will take several days or weeks to calculate the test.
I don't have a copy of this book, but I've read a couple of other early books by McCracken, including one of his Fortran books for engineers and scientists. He was a good writer and teacher.
I had the great fortune to meet Dan at an NSF workshop in Clemson, South Carolina, back in the mid-1990s. We spent many hours in the evening talking shop and watching basketball on TV. (Dan was cheering his New York Knicks on in the NBA finals, and he was happy to learn that I had been a Knicks and Walt Frazier fan in the 1970s.) He was a pioneer of programming and programming education who was willing to share his experience with a young CS prof who was trying to figure out how to teach. We kept in touch by email thereafter. It was honor to call him a friend.
You can find the above quotation in A History of Test-Driven Development (TDD), as Told in Quotes, by Rob Myers. That post includes several good quotes that Myers had to cut from his upcoming book on TDD. "Of course. How else could you program?"
I recently read an interview with documentary filmmaker Errol Morris, who has unorthodox opinions about documentaries and how to make them. In particular, he prefers to build his films out of extended interviews with a single subject. These interviews give him all the source material he needs, because they aren't about questions and answers. They are about stories:
First of all, I think all questions are more or less rhetorical questions. No one wants their questions answered. They just want to state their question. And, in answering the question, the person never wants to answer the question. They just want to talk.
Morris isn't asking questions; he is stating them. His subjects are not answering questions; they are simply talking.
(Think about this the next time your listening to an interview with a politician or candidate for office...)
At first, I was attracted to the sentiment in this paragraph. Then I became disillusioned with what I took to be its cynicism. Now, though, after a week or so, I am again enamored with its insight. How many of the questions I ask of software clients and professional colleagues are really statements of a position? How many of their answers are disconnected from the essential element of my questions? Even when these responses are disconnected, they communicate a lot to me, if only I listen. My clients and colleagues are often telling me exactly what they want me to know. This dynamic is present surprisingly often when I work with students at the university, too. I need to listen carefully when students don't seem to be answering my question. Sometimes it's because they have misinterpreted the question, and I need to ask differently. Sometimes it's because they are telling me what they want me to know, irrespective of the question.
And when my questions aren't really questions, but statements or some other speech act... well, I know I have some work to do.
In case you find Morris's view on interviews cynical and would prefer to ponder the new year with greater hope, I'll leave you with a more ambiguous quote about questions:
There are years that ask questions, and years that answer.
That's from Their Eyes Were Watching God, by Zora Neale Hurston. In hindsight, it may be true or false for any given year. As a way to frame the coming months, though, it may be useful.
I hope that 2020 brings you the answers you seek, or the questions.
I recently started reading posts in the archives of Jason Zweig's blog. He writes about finance for a living but blogs more widely, including quite a bit about writing itself. An article called On Writing Better: Sharpening Your Tools challenges writers to look at each word they write as "an alien object":
As the great Viennese journalist Karl Kraus wrote, "The closer one looks at a word, the farther away it moves." Your goal should be to treat every word you write as an alien object: You should be able to look at it and say, What is that doing here? Why did I use that word instead of a better one? What am I trying to say here? How can I get to where I'm going if I use such stale and lifeless words?
My mind immediately turned this into a writing game, an exercise that puts the idea into practice. Take any piece of writing.
Play the game for a fixed number of rounds or for a fixed period of time. A devilish alternative is to play until you get so frustrated with your writing that you can't continue. You could then judge your maturity as a writer by how long you can play in good spirits.
We could even automate the mechanics of the game by writing a program that chooses a random word in a document for us. Every time we save the document after a change, it jumps to a new word.
As with most first ideas, this one can probablyb be improved. Perhaps we should bias word selection toward words whose replacement or deletion are most likely to improve our writing. Changing "the" or "to" doesn't offer the same payoff as changing a lazy verb or deleting an abstract adverb. Or does it? I have a lot of room to improve as a writer; maybe fixing some "the"s and "to"s is exactly what I need to do. The Three Bears pattern suggests that we might learn something by tackling the extreme form of the challenge and seeing where it leads us.
Changing or deleting a single word can improve a piece of text, but there is bigger payoff available, if we consider the selected word in context. The best way to eliminate many vague nouns is to turn them back into verbs, where they act with vigor. To do that, we will have to change the structure of the sentence, and maybe the surrounding sentences. That forces us to think even more deeply about the text than changing a lone word. It also creates more words for us to fix in following rounds!
I like programming challenges of this sort. A writing challenge that constrains me in arbitrary ways might be just what I need to take time more often to improved my work. It might help me identify and break some bad habits along the way. Maybe I'll give this a try and report back. If you try it, please let me know the results!
And no, I did not play the game with this post. It can surely be improved.
Postscript. After drafting this post, I came across another article by Zweig that proposes just such a challenge for the narrower case of abstract adverbs:
The only way to see if a word is indispensable is to eliminate it and see whether you miss it. Try this exercise yourself:
- Take any sentence containing "actually" or "literally" or any other abstract adverb, written by anyone ever.
- Delete that adverb.
- See if the sentence loses one iota of force or meaning.
- I'd be amazed if it does (if so, please let me know).
We can specialize the writing game to focus on adverbs, another part of speech, or almost any writing weakness. The possibilities...
I like tennis. The Tennis Abstract blog helps me keep up with the game and indulge my love of sports stats at the same time. An entry earlier this month gave a gentle introduction to Elo ratings as they are used for professional tennis:
One of the main purposes of any rating system is to predict the outcome of matches--something that Elo does better than most others, including the ATP and WTA rankings. The only input necessary to make a prediction is the difference between two players' ratings, which you can then plug into the following formula:
1 - (1 / (1 + (10 ^ (difference / 400))))
This formula always makes me smile. The first computer program I ever wrote because I really wanted to was a program to compute Elo ratings for my high school chess club. Over the years I've come back to Elo ratings occasionally whenever I had an itch to dabble in a new language or even an old favorite. It's like a personal kata of variable scope.
I read the Tennis Abstract piece this week as my students were finishing up their compilers for the semester and as I was beginning to think of break. Playful me wondered how I might implement the prediction formula in my students' source language. It is a simple functional language with only two data types, integers and booleans; it has no loops, no local variables, no assignments statements, and no sequences. In another old post, I referred to this sort of language as akin to an integer assembly language. And, heaven help me, I love to program in integer assembly language.
To compute even this simple formula in Klein, I need to think in terms of fractions. The only division operator performs integer division, so 1/x for any x gives 0. I also need to think carefully about how to implement the exponentiation 10 ^ (difference / 400). The difference between two players' ratings is usually less than 400 and, in any case, almost never divisible by 400. So My program will have to take an arbitrary root of 10.
Which root? Well, I can use our gcd() function (implemented using Euclid's algorithm, of course) to reduce diff/400 to its lowest terms, n/d, and then compute the dth root of 10^n. Now, how to take the dth root of an integer for an arbitrary integer d?
Fortunately, my students and I have written code like this in various integer assembly languages over the years. For instance, we have a SQRT function that uses binary search to hone in on the integer closest to the square of a given integer. Even better, one semester a student implemented a square root program that uses Newton's method:
xn+1 = xn - f(xn)/f'(xn)
That's just what I need! I can create a more general version of the function that uses Newton's method to compute an arbitrary root of an arbitrary base. Rather than work with floating-point numbers, I will implement the function to take its guess as a fraction, represented as two integers: a numerator and a denominator.
This may seem like a lot of work, but that's what working in such a simple programming language is like. If I want my students' compilers to produce assembly language that predicts the result of a professional tennis match, I have to do the work.
This morning, I read a review of Francis Su's new popular math book, Mathematics for Human Flourishing. It reminds us that math isn't about rules and formulas:
Real math is a quest driven by curiosity and wonder. It requires creativity, aesthetic sensibilities, a penchant for mystery, and courage in the face of the unknown.
Writing my Elo rating program in Klein doesn't involve much mystery, and it requires no courage at all. It does, however, require some creativity to program under the severe constraints of a very simple language. And it's very much true that my little programming diversion is driven by curiosity and wonder. It's fun to explore ideas in a small space uses limited tools. What will I find along the way? I'll surely make design choices that reflect my personal aesthetic sensibilities as well as the pragmatic sensibilities of a virtual machine that know only integers and booleans.
As I've said before, I love to program.
I've been thinking a lot about technical debt this week. My student teams are in Week 13 of their compiler project and getting ready to submit their final systems. They've been living with their code long enough now to appreciate Ward Cunningham's original idea of technical debt: the distance between their understanding and the understanding embodied in their systems.
... it was important to me that we accumulate the learnings we did about the application over time by modifying the program to look as if we had known what we were doing all along and to look as if it had been easy to do in Smalltalk.
Actually, I think they are experiencing two gulfs: one relates to their understanding of the domain, compilers, and the other to their understanding of how to build software. Obviously, they are just learning about compilers and how to build one, so their content knowledge has grown rapidly while they've been programming. But they are also novice software engineers. They are really just learning how to build a big software system of any kind. Their knowledge of project management, communication, and their tools has also grown rapidly in parallel with building their system.
They have learned a lot about both content and process this semester. Several of them wish they had time to refactor -- to pay back the debt they accumulated honestly along the way -- but university courses have to end. Perhaps one or two of them will do what one or two students in most past semesters have done: put some of their own time into their compilers after the course ends, to see if they can modify the program to look as if they had known what they were doing all along, and to look as if it had been easy to do in Java or Python. There's a lot of satisfaction to be found at the end of that path, if they have the time and curiosity to take it.
One team leader tried to bridge the gulf in real time over the last couple of weeks: He was so unhappy with the gap between his team's code and their understanding that he did a complete rewrite of the first five stages of their compiler. This team learned what every team learns about rewrites and large-scale refactoring: they spend time that could otherwise have been spent on new development. In a time-boxed course, this doesn't always work well. That said, though, they will likely end up with a solid compiler -- as well as a lot of new knowledge about how to build software.
Being a prof is fun in many ways. One is getting to watch students learn how to build something while they are building it, and coming out on the other side with new code and new understanding.
Brian Marick recently retweeted this old tweet from Ron Jeffries:
You: Explain this code to me, please.
They: blah blah blah.
You: Show me where the code says that.
They: <silence>
You: Let's make it say that.
I find this strategy quite helpful when writing my own code. If I can't explain any bit of code to myself clearly and succinctly, then I can take a step back and work on fixing my understanding before trying to fix the code. Once I understand, I'm a big fan of creating functions or methods whose names convey their meaning.
This is also a really handy strategy for me in my teaching. As a prof, I spend a fair amount of time explaining code I've written to students. The act of explaining a piece of code, whether written or spoken, often points me toward ways I can make the program better. If I find myself explaining the same piece of code to several students over time, I know the code can probably be better. So I try to fix it.
I also use a gentler variation of Jeffries' approach when working directly with students and their code. I try whenever I can to help my students learn how to write better programs. It can be tempting to start lecturing them on ways that their program could be better, but unsolicited advice of this sort rarely finds a happy place to land in their memory. Asking questions can be more effective, because questions can lead to a conversation in which students figure some things out on their own. Asking general questions usually isn't helpful, though, because students may not have enough experience to connect the general idea to the details of their program.
So: I find it helpful to ask a student to explain their code to me. Often they'll give me a beautiful answer, short and clear, that stands in obvious contrast to the code we are looking at out. This discrepancy leads to a natural follow-up question: How might we change the code so that it says that? The student can take the lead in improving their own programs, guided by me with bits of experience they haven't had yet.
Of course, sometimes the student's answer is complex or rambles off into silence. That's a cue to both of us that they don't really understand yet what they are trying to do. We can take a step back and help them fix their understanding -- of the problem or of the programming technique -- before trying to fix the code itself.
I recently read Anne-Laure Le Cunff's Interleaving: Rethink The Way You Learn. Le Cunff explains why interleaving -- "the process of mixing the practice of several related skills together" -- is more effective for long-term learning than blocked practice, in which students practice a single skill until they learn it and then move on to the next skill. Interleaving forces the brain to retrieve different problem-solving strategies more frequently and under different circumstances, which reinforces neural connections and improves learning.
To illustrate the distinction between interleaving and blocked practice, Le Cunff uses this image:
When I saw that diagram, I thought immediately of Extreme Programming. In particular, I thought of a diagram I once saw that distinguished XP from more traditional ways of building software in terms of how quickly it moved through the steps of the development life cycle. That image looked something like this:
If design is good, why not do it all the time? If testing is good, why not do it all the time, too?
I don't think that the similarity between these two images is an accident. It reflects one of XP's most important, if sometimes underappreciated, benefits: By interleaving short spurts of analysis, design, implementation, and testing, programmers strengthen their understanding of both the problem and the evolving code base. They develop stronger long-term memory associations with all phases of the project. Improved learning enables them to perform even more effectively deeper in the project, when these associations are more firmly in place.
Le Cunff offers a caveat to interleaved learning that also applies to XP: "Because the way it works benefits mostly our long-term retention, interleaving doesn't have the best immediate results." The benefits of XP, including more effective learning, accrue to teams that persist. Teams new to XP are sometimes frustrated by the small steps and seemingly narrow focus of their decisions. With a bit more experience, they become comfortable with the rhythm of development and see that their code base is more supple. They also begin to benefit from the more effective learning that interleaved practice provides.
~~~~
Image 1: This image comes from Le Cunff's article, linked above. It is by Anne-Laure Le Cunff, copyright Ness Labs 2019, and reproduced here with permission.
Image 2: I don't remember where I saw the image I hold in my memory, and a quick search through Extreme Programming Explained and Google's archives did not turn up anything like it. So I made my own. It is licensed CC BY-SA 4.0.
Jan Schauma recently posted a list of one hundred Falsehoods CS Students (Still) Believe Upon Graduating. There is much good fun here, especially for a prof who tries to help CS students get ready for the world, and a fair amount of truth, too. I will limit my brief comments to three items that have been on my mind recently even before reading this list.
18. 'Email' and 'Gmail' are synonymous.
CS grads are users, too, and their use of Gmail, and systems modeled after it, contributes to the truths of modern email: top posting all the time, with never a thought of trimming anything. Two-line messages sitting atop icebergs of text which will never be read again, only stored in the seemingly infinite space given us for free.
Of course, some of our grads end up in corporate and IT, managing email as merely one tool in a suite of lowest-common-denominator tools for corporate communication. The idea of email as a stream of text that can, for the most part, be read as such, is gone -- let alone the idea that a mail stream can be processed by programs such as procmail to great benefit.
I realize that most users don't ask for anything more than a simple Gmail filter to manage their mail experience, but I really wish it were easier for more users with programming skills to put those skills to good use. Alas, that does not fit into the corporate IT model, and not even the CS grads running many of these IT operations realize or care what is possible.
38. Employers care about which courses they took.
It's the time of year when students register for spring semester courses, so I've been meeting with a lot of students. (Twice as many as usual, covering for a colleague on sabbatical.) It's interesting to encounter students on both ends of the continuum between not caring at all what courses they take and caring a bit too much. The former are so incurious I wonder how they fell into the major at all. The latter are often more curious but sometimes are captive to the idea that they must, must, must take a specific course, even if it meets at a time they can't attend or is full by the time they register.
I do my best to help them get into these courses, either this spring or in a late semester, but I also try to do a little teaching along the way. Students will learn useful and important things in just about every course they take, if they want to, and taking any particular course does not have to be either the beginning or the end of their learning of that topic. And if the reason they think they must take a particular course is because future employers will care, they are going to be surprised. Most of the employers who interview our students are looking for well-rounded CS grads who have a solid foundation in the discipline and who can learn new things as needed.
90. Two people with a CS degree will have a very similar background and shared experience/knowledge.
This falsehood operates in a similar space to #38, but at the global level I reached at the end of my previous paragraph. Even students who take most of the same courses together will usually end their four years in the program with very different knowledge and experiences. Students connect with different things in each course, and these idiosyncratic memories build on one another in subsequent courses. They participate in different extracurricular activities and work different part-time jobs, both of shape and augment what they learn in class.
In the course of advising students over two, three, or four years, I try to help them see that their studies and other experiences are helping them to become interesting people who know more than they realize and who are individuals, different in some respects from all their classmates. They will be able to present themselves to future employers in ways that distinguish them from everyone else. That's often the key to getting the job they desire now, or perhaps one they didn't even realize they were preparing for while exploring new ideas and building their skillsets.
The narrator in Rachel Cusk's "Transit" relates a story told to her by Pavel, the Polish builder who is helping to renovate her flat. Pavel left Poland for London to make money after falling out with his father, a builder for whom he worked. The event that prompted his departure was a reaction to a reaction. Pavel had designed and built a home for his family. After finishing, he showed it to his father. His father didn't like it, and said so. Pavel chose to leave at that moment.
'All my life,' he said, 'he criticise. He criticise my work, my idea, he say he don't like the way I talk -- even he criticise my wife and my children. But when he criticise my house' -- Pavel pursed his lips in a smile -- 'then I think, okay, is enough.'
I generally try to separate myself from the code and prose I write. Such distance is good for the soul, which does not need to be buffeted by criticism, whether external or internal, of the things I've created. It is also good for the work itself, which is free to be changed without being anchored to my identity.
Fortunately, I came out of home and school with a decent sense that I could be proud of the things I create without conflating the work with who I am. Participating in writers' workshops at PLoP conferences early in my career taught me some new tools for hearing feedback objectively and focusing on the work. Those same tools help me to give feedback better. I use them in an effort to help my students develop as people, writers and programmers independent of the code and prose they write.
Sometimes, though, we make things that are expressions of ourselves. They carry part of us in their words, in what they say to the world and how they say it. Pavel's house is such a creation. He made everything: the floors, the doors, and the roof; even the beds his children slept in. His father had criticized his work, his ideas, his family before. But criticizing the house he had dreamed and built -- that was enough. Cusk doesn't give the reader a sense that this criticism was a last straw; it was, in a very real way, the only straw that mattered.
I think there are people in this world who would like just once in their lives to make something that is so much a part of who they are that they feel about it as Pavel does his house. They wish to do so despite, or perhaps because of, the sharp line it would draw through the center of life.
Like me, you probably see references to this classic quote from Donald Rumsfeld all the time:
There are known knowns; there are things we know we know. We also know there are known unknowns; that is to say, we know there are some things we do not know. But there are also unknown unknowns -- the ones we don't know we don't know.
I recently ran across it again in an old Epsilon Theory post that uses it to frame the difference between decision making under risk (the known unknowns) and decision-making under uncertainty (the unknown unknowns). It's a good read.
Seeing the passage again for the umpteenth time, it occurred to me that no one ever seems to talk about the fourth quadrant in that grid: the unknown knowns. A quick web search turns up a few articles such as this one, which consider unknown knowns from the perspective of others in a community: maybe there are other people who know something that you do not. But my curiosity was focused on the first-person perspective that Rumsfeld was implying. As a knower, what does it mean for something to be an unknown known?
My first thought was that this combination might not be all that useful in the real world, such as the investing context that Ben Hunt writes about in Epsilon Theory. Perhaps it doesn't make any sense to think about things you don't know that you know.
As a student of AI, though, I suddenly made an odd connection ... to explanation-based learning. As I described in a blog post twelve years ago:
Back when I taught Artificial Intelligence every year, I used to relate a story from Russell and Norvig when talking about the role knowledge plays in how an agent can learn. Here is the quote that was my inspiration, from Pages 687-688 of their 2nd edition:Sometimes one leaps to general conclusions after only one observation. Gary Larson once drew a cartoon in which a bespectacled caveman, Zog, is roasting his lizard on the end of a pointed stick. He is watched by an amazed crowd of his less intellectual contemporaries, who have been using their bare hands to hold their victuals over the fire. This enlightening experience is enough to convince the watchers of a general principle of painless cooking.
I continued to use this story long after I had moved on from this textbook, because it is a wonderful example of explanation-based learning.
In a mathematical sense, explanation-based learning isn't learning at all. The new fact that the program learns follows directly from other facts and inference rules already in its database. In EBL, the program constructs a proof of a new fact and adds the fact to its database, so that it is ready-at-hand the next time it needs it. The program has compiled a new fact, but in principle it doesn't know anything more than it did before, because it could always have deduced that fact from things it already knows.
As I read the Epsilon Theory article, it struck me that EBL helps a learner to surface unknown knowns by using specific experiences as triggers to combine knowledge it already into a piece of knowledge that is usable immediately without having to repeat the (perhaps costly) chain of inference ever again. Deducing deep truths every time you need them can indeed be quite costly, as anyone who has ever looked at the complexity of search in logical inference systems can tell you.
When I begin to think about unknown knowns in this way, perhaps it does make sense in some real-world scenarios to think about things you don't know you know. If I can figure it all out, maybe I can finally make my fortune in the stock market.
Earlier this week, there was a meme on Twitter where people gave one-line advice to young students as they stepped onto a college campus as first-years, to help them enjoy and benefit from their college years. I didn't have anything clever or new to say, so I didn't join in, but something I read this morning triggered a bit of old wisdom that I wish more students would try to live out. In tweet-size form, it might be: "Learn the basics, struggle a bit, then ask questions." Here's the blog-size version.
In Tyler Cowen's conversation with Google economist Hal Varian, Cowen asks about a piece of advice Varian had once given to graduate students: "Don't look at the literature too soon." Is that still good advice, and why? Yes, Varian replied...
VARIAN: Because if you look at the literature, you'll see this completely worked-out problem, and you'll be captured by that person's viewpoint. Whereas, if you flounder around a little bit yourself, who knows? You might come across a completely different phenomenon. Now, you do have to look at the literature. I want to emphasize that. But it's a good idea to wrestle with a problem a little bit on your own before you adopt the standard viewpoint.
Grad students are often trying to create new knowledge, so it's best for them not to lock themselves into existing ways of thinking too soon. Thus: Don't look at the literature too soon.
I work mostly with undergrads, who study in a different context than grad students. But I think that the core of Varian's advice works well for undergrads, too: Start by learning a few basic ideas in class. Then try to solve problems. Then ask questions.
Undergrads are usually trying to master foundational material, not create new knowledge, so it's tempting to want to jump straight to answers. But it's still to valuable approach the task of learning as a process of building one's own understanding of problems before seeking answers. Banging on a bunch of problems helps us to build instincts about what the important issues and to explore the fuzzy perimeter between the basics and the open questions that will vex us after we master them. That happens best when we don't see a solution right away, when what we learned in class doesn't seem to point us directly to a solution and we have to find our own way.
But do ask questions! A common theme among students who struggle in my courses is the belief they just have to work harder or longer on a problem. Too many times I've had a student tell me "I spent an hour on each of the five homework problems." Really? My goal is for each problem to take 15 minutes or less. After half an hour, or maybe a second attempt the next day, maybe you are missing something small but important. Ask a question; maybe a little nudge can put you on the right track. Sometimes, your question will help me realize that it's the problem which is flawed and needs a tweak!
Back at the beginning of the process, too strong a belief in the ability to figure things out on one's own creates a different sort of breakdown in the learning process: It can be tempting to skip over what you read in your textbook and what you learn in class, and start trying to solving problems. "It's basic material, right? I'll figure it out." You might, but that's taking the idea to an unhealthy level. There's a difference between seeking answers too soon and trying to solve problems without the basic tools you need. Trust your profs a little bit... In class, they are trying to give you the basic tools you need to solve interesting problems.
There's nothing new here. But let's be honest; there isn't much new to be found in ways to learn. Even in the digital age, the basic tenets remain true. That's why I extol curiosity and persistence and why I'd rather be Mr. Miyagi than an answer machine. Learning will be uncomfortable. The trick is to find a way to balance the curiosity with the discomfort, the not-knowing with the receiving of answers and moving on. I wish I had great advice for how to find that balance, but I think people ultimately have to do that for themselves. We benefit by being part of a community of learners, but we each learn in our own ways and on our own time.
Actually, writing up this post has led me to a goal for myself as a teacher this year, and which may be good advice for my fellow teachers: Be more explicit about my expectations of students. This is true both at the micro-level of, say, how much time to spend on homework problems before seeking help, and at the macro-level of how to approach learning. If I want students to do something, I should at least remove the barriers between what they are thinking they should do and what I would like for them to do.
So there's some advice for students and some advice for teachers. Let's enjoy the new year and learn together.
As so often, Marvin Minsky loved to tell us about the beauty of programming. Kids love to play with construction sets like Legos, TinkerToys, and Erector sets. Programming provides an infinite construction kit: you never run out of parts!
In the linked essay, which was published as a preface to a 1986 book about Logo, Minsky tells several stories. One of the stories relates that once, as a small child, he built a large tower out of TinkerToys. The grownups who saw it were "terribly impressed". He inferred from their reaction that:
some adults just can't understand how you can build whatever you want, so long as you don't run out of sticks and spools.
Kids get it, though. Why do so many of us grow out of this simple understanding as we get older? Whatever its cause, this gap between children's imaginations and the imaginations of adults around them creates a new sort of problem when we give the children a programming language such as Logo or Scratch. Many kids take to these languages just as they do to Legos and TinkerToys: they're off to the races making things, limited only by their expansive imaginations. The memory on today's computers is so large that children never run out of raw material for writing programs. But adults often don't possess the vocabulary for talking with the children about their creations!
... many adults just don't have words to talk about such things -- and maybe, no procedures in their heads to help them think of them. They just do not know what to think when little kids converse about "representations" and "simulations" and "recursive procedures". Be tolerant. Adults have enough problems of their own.
Minsky thinks there are a few key ideas that everyone should know about computation. He highlights two:
Computer programs are societies. Making a big computer program is putting together little programs.
Any computer can be programmed to do anything that any other computer can do--or that any other kind of "society of processes" can do.
He explains the second using ideas pioneered by Alan Turing and long championed in the popular sphere by Douglas Hofstadter. Check out this blog post, which reflects on a talk Hofstadter gave at my university celebrating the Turing centennial.
The inability of even educated adults to appreciate computing is a symptom of a more general problem. As Minsky says toward the end of his essay, People who don't appreciate how simple things can grow into entire worlds are missing something important. If you don't understand how simple things can grow into complex systems, it's hard to understand much at all about modern science, including how quantum mechanics accounts for what we see in the world and even how evolution works.
You can usually do well by reading Minsky; this essay is a fine example of that. It comes linked to an afterword written by Alan Kay, another computer scientist with a lot to say about both the beauty of computing and its essential role in a modern understanding of the world. Check both out.
I wasn't getting any work done today on my to-do list, so I decided to write some code.
One of my learning exercises to open the Summer of Joy is to solve the term frequency problem from Crista Lopes's Exercises in Programming Style. Joy is a little like Scheme: it has a lot of cool operations, especially higher-order operators, but it doesn't have much in the way of practical level tools for basic tasks like I/O. To compute term frequencies on an arbitrary file, I need to read the file onto Joy's stack.
I played around with Joy's low-level I/O operators for a while and built a new operator called readfile, which expects the pathname for an input file on top of the stack:
DEFINE readfile == (* 1 *) [] swap "r" fopen (* 2 *) [feof not] [fgets swap swonsd] while (* 3 *) fclose.
The first line leaves an empty list and an input stream object on the stack. Line 2 reads lines from the file and conses them onto the list until it reaches EOF, leaving a list of lines under the input stream object on the stack. The last line closes the stream and pops it from the stack.
This may not seem like a big deal, but I was beaming when I got it working. First of all, this is my first while in Joy, which requires two quoted programs. Second, and more meaningful to me, the loop body not only works in terms of the dip idiom I mentioned in my previous post, it even uses the higher-order swonsd operator to implement the idiom. This must be how I felt the first time I mapped an anonymous lambda over a list in Scheme.
readfile leaves a list of lines on the stack. Unfortunately, the list is in reverse order: the last line of the file is the front of the list. Besides, given that Joy is a stack-based language, I think I'd like to have the lines on the stack itself. So I noodled around some more and implemented the operator pushlist:
DEFINE pushlist == (* 1 *) [ null not ] [ uncons ] while (* 2 *) pop.
Look at me... I get one loop working, so I write another. The loop on Line 1 iterates over a list, repeatedly taking (head . tail) and pushing head and tail onto the stack in that order. Line 2 pops the empty list after the loop terminates. The result is a stack with the lines from the file in order, first line on top:
line-n ... line-3 line-2 line-1
Put readfile and pushlist together:
DEFINE fileToStack == readfile pushlist.and you get fileToStack, something like Python's readlines() function, but in the spirit of Joy: the file's lines are on the stack ready to be processed.
I'll admit that I'm pleased with myself, but I suspect that this code can be improved. Joy has a lot of dandy higher-order operators. There is probably a better way to implement pushlist and maybe even readfile. I won't be surprised if there is a more idiomatic way to implement the two that makes the two operations plug together with less rework. And I may find that I don't want to leave bare lines of text on the stack after all and would prefer having a list of lines. Learning whether I can improve the code, and how, are tasks for another day.
My next job for solving the term frequency problem is to split the lines into individual words, canonicalize them, and filter out stop words. Right now, all I know is that I have two more functions in my toolbox, I learned a little Joy, and writing some code made my day better.
"Elementary" ideas are really hard & need to be revisited
& explored & re-revisited at all levels of mathematical
sophistication. Doing so actually moves math forward.
--
James Tanton
Three summers ago, I spent a couple of weeks re-familiarizing myself with the concatenative programming language Joy and trying to go a little deeper with the style. I even wrote a few blog entries, including a few quick lessons I learned in my first week with the language. Several of those lessons hold up, but please don't look at the code linked there; it is the raw code of a beginner who doesn't yet get the idioms of the style or the language. Then other duties at work and home pulled me away, and I never made the time to get back to my studies.
![]() |
I have dubbed this the Summer of Joy. I can't devote the entire summer to concatenative programming, but I'm making a conscious effort to spend a couple of days each week in real study and practice. After only one week, I have created enough forward momentum that I think about problems and solutions at random times of the day, such as while walking home or making dinner. I think that's a good sign.
An even better sign is that I'm starting to grok some of the idioms of the style. Joy is different from other concatenative languages like Forth and Factor, but it shares the mindset of using stack operators effectively to shape the data a program uses. I'm finally starting to think in terms of dip, an operator that enables a program to manipulate data just below the top of the stack. As a result, a lot of my code is getting smaller and beginning to look like idiomatic Joy. When I really master dip and begin to think in terms of other "dipping" operators, I'll know I'm really on my way.
One of my goals for the summer is to write a Joy compiler from scratch that I can use as a demonstration in my fall compiler course. Right now, though, I'm still in Joy user mode and am getting the itch for a different sort of language tool... As my Joy skills get better, I find myself refactoring short programs I've written in the past. How can I be sure that I'm not breaking the code? I need unit tests!
So my first bit of tool building is to implement a simple JoyUnit. As a tentative step in this direction, I created the simplest version of RackUnit's check-equal? function possible:
DEFINE check-equal == [i] dip i =.This operator takes two quoted programs (a test expression and an expected result), executes them, and compares the results. For example, this test exercises a square function:
[ 2 square ] [ 4 ] check-equal.
This is, of course, only the beginning. Next I'll add a message to display when a test fails, so that I can tell at a glance which tests have failed. Eventually I'll want my JoyUnit to support tests as objects that can be organized into suites, so that their results can be tallied, inspected, and reported on. But for now, YAGNI. With even a few simple functions like this one, I am able to run tests and keep my code clean. That's a good feeling.
To top it all off, implementing JoyUnit will force me to practice writing Joy and push me to extend my understanding while growing the set of programming tools I have at my disposal. That's another good feeling, and one that might help me keep my momentum as a busy summer moves on.
In Tyler Cowen's conversation with poet Elisa New, he asks closes with one of his standard questions:
COWEN: Last question. You meet an 18-year-old, and this person wants in some way to be a future version of you, Elisa New, and asks you for advice. What advice do you give them?
NEW: Teach.
COWEN: Teach.
NEW: Yes, teach the young, and yes, that's the advice. Because what teaching is, is learning to converse with others. It's to experience a topic as it grows richer and richer under the attentions of a community. That's what a classroom that really works is. It's a community that's ever rewarding.
New's justification for teaching has two parts. The first struck me as central to the task of becoming a poet, or a writer of any sort: learning to converse -- to express and exchange ideas -- with others. To converse is to use words and to experience their effects, both as speaker and listener. Over my years in the classroom, I've come to appreciate this benefit of teaching. It's made me a better teacher and, if not a better writer, at least a writer more aware of the different ways in which I can express my ideas.
New's second justification captures well the central value of teaching to an academic. To teach is to experience a topic as it grows richer under the attention of a community. What a wonderful phrase!
Some people think that teaching will steal time from their work as a literary scholar, historian, or scientist. But teaching helps us to see deeper into our discipline by urging us to examine it over and over from new vantage points. Every new semester and every new student creates a new conversation for me, and these conversations remind me that there is even more to a topic than I think -- more often than I ever thought they would before I became a professor. Just when I think I've mastered something, working with students seems most likely to help me see something new, in a way different than I might see something new through my own study.
This exposes one of the advantages of working in a graduate program or in an ongoing research lab: building a community that has some continuity over time. Teaching at an undergraduate institution means that not as many of my students will be able to work with me and one another on the same topic over time. Even so, follow-up courses and undergrad research projects do allow us to create overlapping communities with a lifespan longer than a single semester. It simply requires a different mindset than working in a big research lab.
So I heartily echo Professor New: teach, that's my advice.
From James Propp's Prof. Engel's Marvelously Improbable Machines:
Chip-firing has been rediscovered independently in three different academic communities: mathematics, physics, and computer science. However, its original discovery by Engel is in the field of math education, and I strongly feel that Engel deserves credit for having been the first to slide chips around following these sorts of rules. This isn't just for Engel's sake as an individual; it's also for the sake of the kind of work that Engel did, blending his expertise in mathematics with his experience in the classroom. We often think of mathematical sophistication as something that leads practitioners to create concepts that can only be understood by experts, but at the highest reaches of mathematical research, there's a love of clarity that sees the pinnacle of sophistication as being the achievement of hard-won simplicity in settings where before there was only complexity.
First of all, Petri nets! I encountered Petri nets for the first time in a computer architecture course, probably as a master's student, and it immediately became my favorite thing about the course. I was never much into hardware and architecture, but Petri nets showed me a connection back to graph theory, which I loved. Later, I studied how to apply temporal logic to modeling hardware and found another way to appreciate my architecture courses.
But I really love the point that Propp makes in this paragraph and the section it opens. Most people think of research and teaching as being different sort of activities. But the kind of thinking one does in one often crosses over into the other. The sophistication that researchers have and use help us make sense of complex ideas and, at their best, help us communicate that understanding to a wide audience, not just to researchers at the same level of sophistication. The focus that teachers put on communicating challenging ideas to relative novices can encourage us to seek new formulations for a complex idea and ways to construct more complex ideas out of the new formulations. Sometimes, that can lead to an insight we can use in research.
In recent years, my research has benefited a couple times from trying to explain and demonstrate concatenative programming, as in Forth and Joy, to my undergraduate students. These haven't been breakthroughs of the sort that Engel made with his probability machines, but they've certainly help me grasp in new ways ideas I'd been struggling with.
Propp argues convincingly that it's important that we tell stories like Engel's and recognize that his breakthrough came as a result of his work in the classroom. This might encourage more researchers to engage as deeply with their teaching as with their research. Everyone will benefit.
Do you know any examples similar to the one Propp relates, but in the field of computer science? If so, I would love to hear about them. Drop me a line via email or Twitter.
Oh, and if you like Petri nets, probability, or fun stories about teaching, do read Propp's entire piece. It's good fun and quite informative.
I ran across an old interview with Douglas Crockford recently. When asked what traits were common to the weak programmers he'd seen over his career, Crockford said:
That's an easy one: lack of curiosity. They were so satisfied with the work that they were doing was good enough (without an understanding of what 'good' was) that they didn't push themselves.
I notice a lack of curiosity in many CS students, too. It's even easier for beginners than professional programmers to be satisfied with meeting the minimal requirements of a project -- "I got the right answers!" or, much worse, "It compiles!" -- and not realize that good code can be more. Part of our goal as teachers is to help students develop higher standards and more refined taste while they are in school.
There's another sense, though, in which holding students' lack of curiosity against them is a dangerous trap for professors. In moments of weakness, I occasionally look at my students and think, "Why doesn't this excite them more? Why don't they want to write code for fun?" I've come to realize over the years that our school system doesn't always do much to help students cultivate their curiosity. But with a little patience and a little conversation, I often find that my students are curious -- just not always about the things that intrigue me.
This shouldn't be a surprise. Even at the beginning of my career as a prof, I was a different sort of person than most of my students. Now that I'm a few years older, it's almost certain that I will not be in close connection with my students and what interests them most. Why would they necessarily care about the things I care about?
Bridging this gap takes time and energy. I have to work to build relationships both with individuals and with the group of students taking my course each semester. This work requires patience, which I've learned to appreciate more and more as I've taught. We don't always have the time we need in one semester, but that's okay. One of the advantages of teaching at a smaller school is time: I can get to know students over several semesters and multiple courses. We have a chance to build relationships that enrich the students' learning experience -- and my experience as a teacher.
Trying to connect with the curiosity of many different students creates logistical challenges when designing courses, examples, and assignments, of course. I'm often drawn back to Alan Kay's paradigm, Frank Oppenheimer's Exploratorium, which Kay discussed in his Turing Award lecture. The internet is, in many ways, a programmer's exploratorium, but it's so big, so disorganized, and of such varying levels of quality... Can we create collections of demos and problems that will contain something to connect with just about every student? Many of my posts on this blog, especially in the early years, grappled with this idea. (Here are two that specifically mention the Exploratorium: Problems Are The Thing and Mathematics, Problems, and Teaching.)
Sometimes I think the real question isn't: "Why aren't students more curious?" It is: "Are we instructors curious enough about our students?"
Andy Ko, in SIGCSE 2019 report:
I always have to warn my students before they attend SIGCSE that it's not a place for deep and nuanced discussions about learning, nor is it a place to get critical feedback about their ideas.
It is, however, a wonderful place to be immersed in the concerns of CS teachers and their perceptions of evidence.
I'm not sure I agree that one can't have deep, nuanced discussions about learning at SIGCSE, but it certainly is not a research conference. It is a great place to talk to and learn from people in the trenches teaching CS courses, with a strong emphasis on the early courses. I have picked up a lot of effective, creative, and inspiring ideas at SIGCSE over the years. Putting them onto sure scientific footing is part of my job when I get back.
~~~~~
Stephen Kell, in Some Were Meant for C (PDF), an Onward! 2017 essay:
Unless we can understand the real reasons why programmers continue to use C, we risk researchers continuing to solve a set of problems that is incomplete and/or irrelevant, while practitioners continue to use flawed tools.
For example,
... "faster safe languages" is seen as the Important Research Problem, not better integration.
... whereas Kell believes that C's superiority in the realm of integration is one of the main reasons that C remains a dominant, essential systems language.
Even with the freedom granted by tenure, academic culture tends to restrict what research gets done. One cause is a desire to publish in the best venues, which encourages work that is valued by certain communities. Another reason is that academic research tends to attract people who are interested in a certain kind of clean problem. CS isn't exactly "round, spherical chickens in a vacuum" territory, but... Language support for system integration, interop, and migration can seem like a grungier sort of work than most researchers envisioned when they went to grad school.
"Some Were Meant for C" is an elegant paper, just the sort of work, I imagine, that Richard Gabriel had when envisioned the essays track at Onward. Well worth a read.
In a conversation with Tyler Cowen, economist John Nye expresses disappointment with the nature of discourse in his discipline:
The thing I'm really frustrated by is that it doesn't matter whether people are writing from a socialist or a libertarian perspective. Too much of the discussion of political economy is normative. It's about "what should the ideal state be?"
I'm much more concerned with the questions of "what good states are possible?" And once good states are created that are possible, what good states are sustainable? And that, in my view, is a still understudied and very poorly understood issue.
For some reason, this made me think about software development. Programming styles, static and dynamic typing, software development methodologies, ... So much that is written about these topics tells us what's the right the thing to do. "Do this, and you will be able to reliably make good software."
I know I've been partisan on some of these issues over the course of my time as a programmer, and I still have my personal preferences. But these days especially I find myself more interested in "what good states are sustainable?". What has worked for teams? What conditions seem to make those practices work well or not so well? How do teams adapt to changes in the domain or the business or the team's make-up?
This isn't too say that we can't draw conclusions about forces and context. For example, small teams seem to make it easier to adapt to changing conditions; to make it work with bigger teams, we need to design systems that encourage attention and feedback. But it does mean that we can help others succeed without always telling them what they must do. We can help them find a groove that syncs with them and the conditions under which they work.
Standing back and surveying the big picture, it seems that a lot of good states are sustainable, so long as we pay attention and adapt to the world around us. And that should be good news to us all.
Shriram Krishnamurthi, in Books as Software:
I have said that a book is a collection of components. I have concrete evidence that some of my users specifically excerpt sections that suit their purpose. ...
I forecast that one day, rich document formats like PDF will recognize this reality and permit precisely such specifications. Then, when a user selects a group of desired chapters to generate a thinner volume, the software will automatically evaluate constraints and include all dependencies. To enable this we will even need "program" analyses that help us find all the dependencies, using textual concordances as a starting point and the index as an auxiliary data structure.
I am one of the users Krishnamurthi speaks of, who has excerpted sections from his Programming Languages: Application and Interpretation to suit the purposes of my course. Though I've not written a book, I do post, use, adapt, and reuse detailed lecture notes for my courses, and as a result I have seen both sides of the divide he discusses. I occasionally change the order of topics in a course, or add a unit, or drop a unit. An unseen bit of work is to account for the dependencies among concepts, examples, problems, and code in the affected sections, but also in the new whole. My life is simpler than book writers who have to deal at least in part with rich document formats: I do everything in a small, old-style subset of HTML, which means I can use simple text-based tools for manipulating everything. But dependencies? Yeesh.
Maybe I need to write a big makefile for my course notes. Alas, that would not help me manage dependencies in the way I'd like, or in the way Krishnamurthi forecasts. As such, it would probably make things worse. I suppose that I could create the tool I need.
Last week I blogged about writing code that is easy to delete, drawing on some great lines from an old 'programming is terrible' post. Here's another passage from @tef's post that's worth thinking more about:
Step 1: Copy-paste code
Building reusable code is something that's easier to do in hindsight with a couple of examples of use in the code base, than foresight of ones you might want later. On the plus side, you're probably re-using a lot of code already just by using the file-system, why worry that much? A little redundancy is healthy.
It's good to copy-paste code a couple of times, rather than making a library function, just to get a handle on how it will be used. Once you make something a shared API, you make it harder to change.
There's not a great one-liner in there, but these paragraphs point to a really important lesson, one that we programmers sometimes have a hard time learning. We are told so often "don't repeat yourself" that we come to think that all repetition is the same. It's not.
One use of repetition is in avoiding what @tef calls, in another 'programming is terrible' post, "preemptive guessing". Consider the creation of a new framework. Oftentimes, designing a framework upfront doesn't work very well because we don't know the domain's abstractions yet. One of the best ways to figure out what they are is to write several applications first, and let framework fall out the applications. While doing this, repetition is our friend: it's most useful to know what things don't change from one application to another. This repetition is a hint on how to build the framework we need. I learned this technique from Ralph Johnson.
I use and teach a similar technique for programming in smaller settings, too. When we see two bits of code that resemble one another, it often helps to increase the duplication in order to eliminate it. (I learned this idea from Kent Beck.) In this case, the goal of the duplication is to find useful abstractions. Sometimes, though, code duplication is really a hint to think differently about a problem. Factoring out a function or class -- finding a new abstraction -- may be incidental to the learning that takes place.
For me, this line from from the second programming-is-terrible post captures this idea perfectly:
... duplicate to find the right abstraction first, then deduplicate to implement it.
My spell checker objects to the word "deduplicate", but I'll allow it.
All of these ideas taken together are the reason that I think copy-and-paste gets an undeservedly bad name. Used properly, it is a valuable programming technique -- essential, really. I've long wanted to write a Big Ball of Mud-style paper about copy-and-paste patterns. There are plenty of good reasons why we write repetitive code and, as @tef says in the two posts I link to above, sometimes leaving duplication in your code is the right thing to do.
One final tribute to repetition for now. While researching this blog post, I ran across a blog entry of mine from October 2016. Apparently, I had just read @tef's Write code that is easy to delete... post and felt an undeniable urge to quote and comment on it. If you read that 2016 post, you'll see that my Writing code that is easy to delete post from last week duplicates it in spirit and, in a few cases, even the details. I swear that I read @tef's post again last week and wrote the new blog entry from scratch, with no memory of the 2016 events. I am perfectly happy with this second act. Sometimes, ideas circle through our brains again, changing us in imperceptible ways. As @tef says, a little redundancy is healthy.
Alex Honnold is a rock climber who was the first person to "free solo" Yosemite's El Capitan rock wall. In an interview for a magazine, he was asked what it takes to reach ever higher goals. One bit of advice was to "aim for joy, not euphoria". When you prepare to achieve a goal, it may not feel like a big surprise when you achieve it because you prepared to succeed. Don't expect to be overwhelmed by powerful emotions when you accomplish something new; doing so sets too high a standard and can demotivate you.
This paragraph, though, is the one that spoke to me:
Someone recently said to me about running: "It never feels easier--you just go faster." A lot of sports always feel terrible. Climbing is always like that. You always feel weak and like you suck, but you can do harder and harder things.
As a one-time marathoner, never fast but always training to run a PR in my next race, I know what Honnold means. However, I also feel something similar as a programmer. Writing software often seems like a slog that I'm not very good at. I'm forever looking up language features in order to get my code to work, and then I end up with programs that are bulky and fight me at every turn. I refactor and rewrite and... find myself back in the slog. I don't feel terrible all that often, but I am usually a little on edge.
Yet if I compare the programs I write today with ones I wrote 5 or 10 or 30 years ago, I can see that I'm doing more interesting work. This is the natural order. Once I know how to do one thing, I seek tougher problems to solve.
In the article, the passage quoted above is labeled "Feeling awful is normal." I wonder if programming feels more accessible to people who are comfortable with a steady, low-grade intellectual discomfort punctuated by occasional bouts of head banging. Honnold's observation might reassure beginning programmers who don't already know that feeling uneasy is a natural part of pushing yourself to do more interesting work.
All that said, even when I was training for my next marathon, I was always able to run for fun. There was nothing quite like an easy run at sunrise to boost my mood. Fortunately, I am still able to program that way, too. Every once in a while, I like to write code to solve some simple problem I run across on Twitter or in a blog entry somewhere. I find that these interludes recharge me before I battling the next big problem I decide to tackle. I hope that my students can still programming in this way as they advance on to bigger challenges.
Last week someone tweeted a link to Write code that is easy to delete, not easy to extend. It contains a lot of great advice on how to create codebases that are easy to maintain and easy to change, the latter being an essential feature of almost any code that is the former. I liked this article so much that I wanted to share some of its advice here. What follows are a few of the many one- and two-liners that serve as useful slogans for building maintainable software, with light commentary.
... repeat yourself to avoid creating dependencies, but don't repeat yourself to manage them.
This line from the first page of the paper hooked me. I'm not sure I had ever had this thought, at least not so succinctly, but it captures a bit of understanding that I think I had. Reading this, I knew I wanted to read the rest of the article.
Make a util directory and keep different utilities in different files. A single util file will always grow until it is too big and yet too hard to split apart. Using a single util file is unhygienic.
This isn't the sort of witticism that I quote in the rest of this post, but its solid advice that I've come to live by over the years. I have this pattern.
Boiler plate is a lot like copy-pasting, but you change some of the code in a different place each time, rather than the same bit over and over.
I really like the author's distinction between boilerplate and copy-and-paste. Copy-and-paste has valuable uses (heresy, I know; more later), whereas boilerplate sucks the joy out of almost every programmer's life.
You are writing more lines of code, but you are writing those lines of code in the easy-to-delete parts.
Another neat distinction. Even when we understand that lines of code are an expense as much as (or instead of) an investment, we know that sometimes we have write more code. Just do it in units that are easy to delete.
A lesson in separating concerns, from Python libraries:
requests is about popular http adventures, urllib3 is about giving you the tools to choose your own adventure.
Layers! I have had users of both of these libraries suggest that the other should not exist, but they serve different audiences. They meet different needs in a way that that more than makes up for the cost of the supposed duplication.
Building a pleasant to use API and building an extensible API are often at odds with each other.
There's nothing earth-shattering in this observation, but I like to highlight different kinds of trade-off whenever I can. Every important decision we make writing programs is a trade-off.
Good APIs are designed with empathy for the programmers who will use it, and layering is realising we can't please everyone at once.
This advice elaborates on the quote earlier to repeat code in order not to create dependencies, but not to manage them. Creating a separate API is one way to avoid dependencies to code that are hard to delete.
Sometimes it's easier to delete one big mistake than try to delete 18 smaller interleaved mistakes.
Sometimes it really is best to write a big chunk of code precisely because it is easy to delete. An idea that is distributed throughout a bunch of functions or modules has to be disentangled before you can delete it.
Becoming a professional software developer is accumulating a back-catalogue of regrets and mistakes.
I'm going to use this line in my spring Programming Languages class. There are unforeseen advantages to all the practice we profs ask students to do. That's where experience comes from.
We are not building modules around being able to re-use them, but being able to change them.
This is another good bit of advice for my students, though I'll write this one more clearly. When students learn to program, textbooks often teach them that the main reason to write a function is that you can reuse it later, thus saving the effort of writing similar code again. That's certainly one benefit of writing a function, but experienced programmers know that there are other big wins in creating functions, classes, and modules, and that these wins are often even more valuable than reuse. In my courses, I try to help students appreciate the value of names in understanding and modifying code. Modularity also makes it easier to change and, yes, delete code. Unfortunately, students don't always get the right kind of experience in their courses to develop this deeper understanding.
Although the single responsibility principle suggests that "each module should only handle one hard problem", it is more important that "each hard problem is only handled by one module".
Lovely. The single module that handles a hard problem is a point of leverage. It can be deleted when the problem goes away. It can be rewritten from scratch when you understand the problem better or when the context around the problem changes.
This line is the heart of the article:
The strategies I've talked about -- layering, isolation, common interfaces, composition -- are not about writing good software, but how to build software that can change over time.
Good software is software that can you can change. One way to create software you can change is to write code that you can easily replace.
Good code isn't about getting it right the first time. Good code is just legacy code that doesn't get in the way.
A perfect aphorism to close to the article, and to perfect way to close this post: Good code is legacy code that doesn't get in the way.
I ran across two great examples of humility by Nobel Prize-winning economists in recent conversations with Tyler Cowen. When asked, "Should China and Japan move to romanized script?", Paul Romer said:
I basically don't know the answer to that question. But I'll use that as a way to talk about something else ...
Romer could have speculated or pontificated; instead, he acknowledged that he didn't know the answer and pivoted the conversation to a related topic he had thought about (reforming spelling in English, for which he offered an interesting computational solution). By shifting the topic, Romer added value to the conversation without pretending that any answer he could give to the original question would have more value than as speculation.
A couple of months ago, Cowen sat with Paul Krugman. When asked whether he would consider a "single land tax" as a way to encourage a more active and more equitable economy, Krugman responded:
I just haven't done my homework on that.
... and left it there. To his credit, Cowen did not press for an uninformed answer; he moved on to another question.
I love the attitude that Krugman and Romer adopt and really like Krugman's specific answer, which echoed his response to another question earlier in the conversation. We need more people answering questions this way, more often and in more circumstances.
Such restraint is probably even more important in the case of Nobel laureates. If Romer and Klugman choose to speculate on a topic, a lot of people will pay attention, even if it is a topic they know little about. We might learn something from their speculations, but we might also forget that they are only uninformed speculation.
I think what I like best about these answers is the example that Romer and Klugman set for the rest of us: It's okay to say, "I don't know." If you have not done the homework needed to offer an informed answer, it's often best to say so and move on to something you're better prepared to discuss.
This morning I read Tyler Cowen's conversation with Paul Romer. At one point, Romer talks about being introduced to C.S. Peirce, who had deep insights into "abstraction and how we use abstraction to communicate" (a topic Romer and Cowen discuss earlier in the interview). Romer is clearly enamored with Peirce's work, but he's also fascinated by the fact that, after a long career thinking about a set of topics, he could stumble upon a trove of ideas that he didn't even know existed:
... one of the joys of reading -- that's not a novel -- but one of the joys of reading, and to me slightly frightening thing, is that there's so much out there, and that a hundred years later, you can discover somebody who has so many things to say that can be helpful for somebody like me trying to understand, how do we use abstraction? How do we communicate clearly?
But the joy of scholarship -- I think it's a joy of maybe any life in the modern world -- that through reading, we can get access to the thoughts of another person, and then you can sample from the thoughts that are most relevant to you or that are the most powerful in some sense.
This process, he says, is the foundation for how we transmit knowledge within a culture and across time. It's how we grow and share our understanding of the world. This is a source of great joy for scholars and, really, for anyone who can read. It's why so many people love books.
Romer's interest in Peirce calls to mind my own fascination with his work. As Romer notes, Peirce had a "much more sophisticated sense about how science proceeds than the positivist sort of machine that people describe". I discovered Peirce through an epistemology course in graduate school. His pragmatic view of knowledge, along with William James's views, greatly influenced how I thought about knowledge. That, in turn, redefined the trajectory by which I approached my research in knowledge-based systems and AI. Peirce and James helped me make sense of how people use knowledge, and how computer programs might.
So I feel a great kinship with Romer in his discovery of Peirce, and the joy he finds in scholarship.
In Quality and Effort, Seth Godin reminds us that being careful can take us only so far toward the quality we seek. Humans make mistakes, so we need processes and systems in place to help us avoid them. Near the end of the post, he writes:
In school, we harangue kids to be more careful, and spend approximately zero time teaching them to build better systems instead. We ignore checklists and processes because we've been taught that they're beneath us.
This paragraph isolates one of the great powers we can teach our students, but also a glaring weakness in how most of us actually teach. I've been a professor for many years now, and before that I was a student for many years. I've seen a lot of students succeed and a few not do as well as they or I had hoped. Students who are methodical, patient, and disciplined in how they read, study, and program are much more likely to be in the successful group.
Rules and discipline sometimes get a bad rap these days. Creativity and passion are our catchwords. But dull, boring systems are often the keys that unlock the doors to getting things done and moving on to learn and do cooler things.
Students occasionally ask me why I slavishly follow the processes I teach them, whether it's a system as big as test-first development and refactoring or a process as simple as the technique for creating NFAs and converting them to DFAs. I tell them that I don't always trust myself but that I do trust the system: it almost always leads me to a working solution. Sure, in this moment or that I might be fine going off script, but... Good habits generate positive returns, while freewheeling it too often lets an error sneak in. (When I go off script in class, they too often get to see just that!)
We do our students a great favor when when we help them learn systems that work. Following a design recipe or something like it may be boring, but students who learn to follow it develop stronger programming skills, enjoy the success of getting projects done successfully, and graduate on to more interesting problems. As the system becomes ingrained as habit, they usually begin to appreciate it as an enabler of their success.
I agree with Godin: If it matters enough to be careful, then it matters enough to build (or learn) a system.
Richard Hamming -- he of Hamming codes, Hamming numbers, and Hamming distance -- wasn't a big fan of brainstorming. He preferred to talk about his ideas with another capable person, because the back-and-forth was more likely to help the idea reach "critical mass". But not every capable person is a good partner for such conversations:
There is also the idea I used to call 'sound absorbers'. When you get too many sound absorbers, you give out an idea and they merely say, "Yes, yes, yes." What you want to do is get that critical mass in action; "Yes, that reminds me of so and so," or, "Have you thought about that or this?" When you talk to other people, you want to get rid of those sound absorbers who are nice people but merely say, "Oh yes," and to find those who will stimulate you right back.
What a simple bit of advice: seek out idea amplifiers, not sound absorbers. Talk with people who help you expand and refine your ideas, by making connections to other work or by pushing you to consider implications or new angles. I think that talking to the right people can boost your work in another important way, too: they will feed your motivation, helping you to maintain the energy you need to stay excited and active.
This is one of the great benefits of blogs and Twitter, used well. We have so many more opportunities than ever before to converse with the right people. Unlike Hamming, I can't walk the halls of Bell Labs, or on any regular basis walk the halls of a big research university or one of the great computing companies. But I can read the blogs written by the researchers who work there, follow them on Twitter, and participate in amplifying conversations. Blogs and Twitter are great sources of both ideas and encouragement.
(The passage quoted above comes from the question-and-answer portion of Hamming's classic 1986 talk, You and Your Research. I re-read it this morning, as I occasionally do, because it's one of those papers that causes me to be more intentional in how I approach my work. Like Hamming, "I have a lot of faults", which means that there is considerable value to be found in judicious self-management. I need periodic booster shots.)
Over the last few days, there has been an interesting Twitter thread on the challenges of getting CS instructors to adopt the results of CS education research. Actually, it started as a thread and grew into a tree... Here's a reasonable jumping-in spot if you'd like to explore.
I imagine that it's hard to get research results into the classroom in a lot of disciplines. Research can be abstract and is usually out of context for any given school. But even when research groups implement curricula and train teachers, it can be hard to spread practices that we know work well.
The answer educators give for not adopting research results often boils down to, "It's complicated." Schools are different; students are different; the end goals are different. Indeed, the original Twitter thread spawned a student diversity conversation that picked up when the energy of the original topic waned. This tweet is a nice anchor for that conversation:
Majors vs. non-majors. Undergrads vs. high school vs. elementary school. Adult software developers vs end-user programmers vs conversational programmers. Different goals: authenticity, security, software engineering, algorithmic thinking, practice production. No one way.
All of these distinctions are real. I've seen several of them affect how we teach introductory programming at my school. And let's be honest: when we talk about research in CS education, we are often talking almost exclusively about teaching programming. How we teach intro programming can, though, have a big effect on student performance in later courses.
But even in the face of these different dimensions, I still wonder why more of us don't incorporate more research results into our teaching. I have a few ideas borne out of my years as a faculty member.
I think a big part of the effect that students have on whether faculty adopt new pedagogical approaches comes an angle not mentioned in that list: competitive pressure. Departments fear that if they adopt an unusual curriculum or an unusual first language, they will lose students. These fears manifest themselves if you propose to teach a relatively ordinary language such as Ada; they grow quickly if your approach uses an esoteric parentheses language (EPL). Parents of HS students will say, "Your competitors don't use EPL or your unusual pedagogical approach. They teach Good Old-Fashioned CS, the way it's always been, a way that's proven to get graduates jobs. Why shouldn't we send our daughters and sons to another school?"
HS students and their parents do ask questions such as these, and it takes a lot of perseverance to respond over and over. Only the most committed believers can stay the course. Unfortnately, it can take a few years for the benefits of a new approach to show up in upper division courses, let alone in the performance of graduates. New curricula rarely have that much time to succeed. If the department's faculty aren't all fully committed to an approach, then as soon as enrollments dip a bit, they join the chorus. There's a scapegoat ready at hand.
Even if we step back from the competitive disadvantage angle, we can see a similar phenomenon at play. The tweet quoted above list many different kinds of learners. These distinctions can serve as a handy reason for faculty not to change when faced with CS ed research that may only apply to one student population or one context. And resistance to change is a strong force.
Speaking as a long-time professor, I think the most powerful forces against change arise not on the student side of the equation but on the teachers'. Learning a bunch of new techniques, integrating them into a coherent curriculum, and then mastering them is hard. It takes time profs don't always have a lot of. When they do have some time, reading and applying CS ed research pulls them away from their own research or even away from the things they like to do in the classroom.
Personal confession time. This all reminds me of a conversation I had with Mike Clancy many years ago. He was part of the team that developed the idea of using case studies to teach programming. I've always admired the approach and thought the intro textbook he and Marcia Linn built around it was pretty cool. When I told Mike this, he asked, "Well then, why aren't you using them? Why aren't you writing case studies of your own?" These were great questions that took me aback. My answers sound like the excuses I've talked about in this post... My colleagues were skeptical of the approach and preferred other textbooks; convincing them to change would require a lot of work and political capital. Writing case studies is hard work, and at the time I was more excited to work on my own somewhat-related ideas. "Soon", I said. But soon never came.
I sit here still admiring case studies -- and worked examples, and Parsons problems, and the design recipe, and How to Design Programs. I've incorporated the idea of design recipes into my Programming Languages course, but I haven't fully committed to it yet. The others? They are still dreams. (I did try my first Parsons problem this semester: an assembly language program in my compilers course. I liked the result and think I'll try more next semester when I teach my students functional programming in Racket.)
Am I slow to adopt evidence-backed pedagogy? Lazy? Short on time? I don't know, but I suspect that there are many reasons. Change is hard. I do hope that I haven't done my students too much disservice in the meantime.
At one point in the Twitter conversation that launched this post, someone wrote something to the effect, "convincing researchers is easier than convincing practitioners". But that brings to mind another question: Why aren't all CS ed researchers advocating the use of worked examples? Design recipe and HTDP-style curricula? Parsons problems? Why aren't more CS ed researchers using them in their own classrooms? Maybe they are and I haven't been paying attention. If that's so, then I doubt that many other CS profs know about this widespread adoption of advances among CS ed researchers either.
Some disciplines, such as physics, seem to have developed more of a culture for incorporating education research into the classroom than computer science. Can we change that? If so, I think there is room for the CS ed research community to lead the way. But... it's complicated.
I like this passage from John Urschel Goes Pro, about the former NFL player who is pursuing a Ph.D. in math:
The world thinks mathematicians are people for whom math is easy. That's wrong. Sure, some kids, like Urschel, have little trouble with school math. But everyone who starts down the road to creating really new mathematics finds out what Urschel did: It's a struggle. A prickly, sometimes lonely struggle whose rewards are uncertain and a long time coming. Mathematicians are the people who love that struggle.
It's cliché to tell kids to "find their passion". That always seems to me like an awful lot of pressure to put on young adults, let alone teenagers. I meet with potential CS majors frequently, both college students and high school students. Most haven't found their passion yet, and as a result many wonder if there is something wrong with them. I do my my best to assure them that, no, there is nothing wrong with them. It's an unreasonable expectation placed on them by a world that, usually with good intentions, is trying to encourage them.
I don't think there is anything I'd rather be than a computer scientist, but I did not walk a straight path to being one. Some choices early on were easy: I like biology as a body of knowledge, but I never liked studying biology. That seemed a decent sign that maybe biology wasn't for me. (High-school me didn't understand that there might be a difference between school biology and being a biologist...) But other choices took time and a little self-awareness.
From the time I was eight years old or so, I wanted to be an architect. I read about architecture; I sent away for professional materials from the American Institute of Architects; I took courses in architectural drafting at my high school. (There was an unexpected benefit to taking those courses: I got to meet a lot of people were not part of my usual academic crowd.) Then I went off to college to study architecture... and found that, while I liked many things about the field, I didn't really like to do the grunt work that is part of the architecture student's life, and when the assigned projects got more challenging, I didn't really enjoy working on them.
But I had enjoyed working on the hard projects I'd encountered in my programing class back in high school. They were challenges I wanted to overcome. I changed my major and dove into college CS courses, which were full of hard problems -- but hard problems that I wanted to solve. I didn't mind being frustrated for an entire semester one year, working in assembly language and JCL, because I wanted to solve the puzzles.
Maybe this is what people mean when they tell us to "find our passion", but that phrase seems pretty abstract to me. Maybe instead we should encourage people to find the hard problems they like to work on. Which problems do you want to keep working on, even when they turn out to be harder than you expected? Which kinds of frustration do you enjoy, or at least are willing to endure while you figure things out? Answers to these very practical questions might help you find a place where you can build an interesting and rewarding life.
I realize that "Find your passion" makes for a more compelling motivational poster than "What hard problems do you enjoy working on?" (and even that's a lot better than "What kind of pain are you willing to endure?"), but it might give some people a more realistic way to approach finding their life's work.
Beds are useful. I enjoyed my bed at the hotel last night. But you won't find a pattern called bed in A Pattern Language, and that's because we don't have a problem with beds. Beds are solved. What we have a problem with is building with beds. And that's why, if you look in A Pattern Language, you'll find there are a number of patterns that involve beds.
Now, the analogy I want to make is, basically, the design patterns in the book Design Patterns are at the same level as the building blocks in the book A Pattern Language. So Bridge, which is one of the design patterns, is the same kind of thing as column. You can call it a pattern, but it's not really a pattern that gets at the root problem we're trying to solve. And if Alexander had written a book that had a pattern called Bed and another one called Chair, I imagine that that book would have failed and not inspired all of the people that the actual book did inspire. So that, I claim, is why patterns failed.
Those two nearly-sequential paragraphs are from Patterns Failed. Why? Should We Care?, a talk Brian Marick gave at Deconstruct 2017. It's a good talk. Marick's provocative claim that, as an idea, software patterns failed is various degrees of true and false depending on how you define 'patterns' and 'failed'. It's hard to declare the idea an absolute failure, because a lot of software developers and UI designers use patterns to good effect as a part of their work every day. But I agree with Brian that software patterns failed to live up to their promise or to the goals of the programmers who worked so hard to introduce Christopher Alexander's work to the software community. I agree, too, that the software pattern community's inability to document and share patterns at the granularity that made Alexander's patterns so irresistible is a big part of why.
I was a second- or third-generation member of the patterns community, joining in after Design Patterns had been published and, like Marick, I worked mostly on the periphery. Early on I wrote patterns that related to my knowledge-based systems work. Much of my pattern-writing, though, was at the level of elementary patterns, the patterns that novice programmers learn when they are first learning to program. Even at that level, the most useful patterns often were ones that operated up one level from the building blocks that novices knew.
Consider Guarded Linear Search, from the Loop Patterns paper that Owen Astrachan and I workshopped at PLoP 1998. It helps beginners learn how to arrange a loop and an if statement in a way that achieves a goal. Students in my beginning courses often commented how patterns like this one helped them write programs because, while they understood if statements and for statements and while statements, they didn't always know what to do with them when facing a programming task. At that most elementary level, the Guarded Linear Search pattern was a pleasing -- and useful -- whole.
That said, there aren't many "solved problems" for beginners, so we often wrote patterns that dropped down to the level of building blocks simply to help novices learn basic constructs. Some of the Loop Patterns paper does that, as does Joe Bergin's Patterns for Selection. But work in the elementary patterns community would have been much more valuable, and potentially had more effect, if we had thought harder about how to find and document patterns at the level of Alexander's patterns.
Perhaps the patterns sub-community in which I've worked in which best achieved its goals was the pedagogical patterns community. These are not software patterns but rather patterns of teaching techniques. They document solutions to problems that teachers face every day. I think I'd be willing to argue that the primary source of pedagogical patterns' effectiveness is that these solutions combine more primitive building blocks (delivery techniques, feedback techniques, interaction techniques) in a way that make learning and instruction both more effective and more fun. As a result, they captured a lot of teachers' interest.
I think that Marick's diagnosis also points out the error in a common criticism of software patterns. Over the years, we often heard folks say that software patterns existed only because people used horrible languages like C++ and Java. In a more powerful language, any purported pattern would be implemented as a language primitive or could be made a primitive by writing a macro. But this misses the point of Alexander's insight. The problem in software development isn't with inheritance and message passing and loops, just as the problem in architecture isn't with beds and windows and space. It's with finding ways to arrange these building blocks in a way that's comfortable and nice and, to use Alexander's term, "life-giving". That challenge exists in all programming languages.
Finally, you probably won't be surprised to learn that I agree with Marick that we should all care that software patterns failed to live up to their promise. Making software is fun, but it's also challenging. Alexander's idea of a pattern language is one way that we might help programmers do their jobs better, enjoy their jobs more, and produce software that is both functional and habitable. The first pass was a worthy effort, and a second pass, informed by the lessons of the first, might get us closer to the goal.
Thanks to Brian for giving this talk and to the folks at Deconstruct for posting it online. Go watch it.
![]() |
The opening keynote this year was by Simon Peyton Jones of Microsoft Research, well known in the programming languages for Haskell and many other things. But his talk was about something considerably less academic: "Shaping Our Children's Education in Computing", a ten-year project to reform the teaching of computing in the UK primary and secondary schools. It was a wonderful talk, full of history, practical advice, lessons learned, and philosophy of computing. Rather than try to summarize everything Peyton Jones said, I will let you watch the video when it is posted (which will be as early as next week, I think).
I would, though, like to highlight one particular part of the talk, the way he describes computer science to a non-CS audience. This is an essential skill for anyone who wants to introduce CS to folks in education, government, and the wider community who often see CS as either hopelessly arcane or as nothing more than a technology or a set of tools.
Peyton Jones characterized computing as being about information, computation, and communication. For each, he shared one or two ways to discuss the idea with an educated but non-technical audience. For example:
In all three cases, it helps greatly to use examples from many disciplines and to ask questions that encourage the audience to ask their own questions, form their own hypotheses, and create their own experiments. The best examples and questions actually enable people to engage with computing through their own curiosity and inquisitiveness. We are fascinated by computing; other people can be, too.
There is a huge push in the US these days for everyone to learn how to program. This creates a tension among many of us computer scientists, who know that programming isn't everything that we do and that its details can obscure CS as much as they illuminate it. I thought that Peyton Jones used a very nice analogy to express the relationship between programming and CS more broadly: Programming is to computer science as lab work is to physics. Yes, you could probably take lab work out of physics and still have physics, but doing so would eviscerate the discipline. It would also take away a lot of what draws people to the discipline. So it is with programming and computer science. But we have to walk a thin line, because programming is seductive and can ultimately distract us from the ideas that make programming so valuable in the first place.
Finally, I liked Peyton Jones's simple summary of the reasons that everyone should learn a little computer science:
Oh, and yes, a few people will get jobs that use programming skills and computing knowledge. People in government and business love to hear that part.
Regular readers of this blog know that I am a sucker for aphorisms. Peyton Jones dropped a few on us, most earnestly when encouraging his audience to participate in the arduous task of introducing and reforming the teaching CS in the schools:
It's easy to admire great researchers who have invested so much time and energy into solving real-world problems, especially in our schools. As long as this post is, it covers only a few minutes from the middle of the talk. My selection and bare-bones outline don't do justice to Peyton Jones's presentation or his message. Go watch the talk when the video goes up. It was a great way to start Strange Loop.
This fall I am again teaching our course in compiler development. Working in teams of two or three, students will implement from scratch a complete compiler for a simple functional language that consists of little more than integers, booleans, an if statement, and recursive functions. Such a language isn't suitable for much, but it works great for writing programs that do simple arithmetic and number theory. In the past, I likened it to an integer assembly language. This semester, my students are compiling a Pascal-like language of this sort that call Flair.
If you've read my blog much in the falls over the last decade or so, you may recall that I love to write code in the languages for which my students write their compilers. It makes the language seem more real to them and to me, gives us all more opportunities to master the language, and gives us interesting test cases for their scanners, parsers, type checkers, and code generators. In recent years I've blogged about some of my explorations in these languages, including programs to compute Farey numbers and excellent numbers, as well as trying to solve one of my daughter's AP calculus problems.
When I run into a problem, I usually get an itch to write a program, and in the fall I want to write it in my students' language.
Yesterday, I began writing my first new Flair program of the semester. I ran across this tweet from James Tanton, which starts:
N is "special" if, in binary, N has a 1s and b 0s and a & b are each factors of N (so non-zero).
So, 10 is special because:
9 is not special because its binary rep also contains two 1s and two 0s, but two is not a factor of 9. 3 is not special because its binary rep has no 0s at all.
My first thought upon seeing this tweet was, "I can write a Flair program to determine if a number is special." And that is what I started to do.
Flair doesn't have loops, so I usually start every new program by mapping out the functions I will need simply to implement the definition. This makes sure that I don't spend much time implementing loops that I don't need. I ended up writing headers and default bodies for three utility functions:
With these helpers, I was ready to apply the definition of specialness:
return divides(count(1, to_binary(n)), n) and divides(count(0, to_binary(n)), n)
Calling to_binary on the same argument is wasteful, but Flair doesn't have local variables, either. So I added one more helper to implement the design pattern "Function Call as Variable Assignment", apply_definition:
function apply_definition(binary_n : integer, n : integer) : booleanand called it from the program's main:
return apply_definition(to_binary(n), n)
This is only the beginning. I still have a lot of work to do to implement to_binary, count and divides, using recursive function calls to simulate loops. This is another essential design pattern in Flair-like languages.
As I prepared to discuss my new program in class today, I found bug: My divides test was checking for factors of binary_n, not the decimal n. I also renamed a function and one of its parameters. Explaining my programs to students, a generalization of rubber duck debugging, often helps me see ways to make a program better. That's one of the reasons I like to teach.
Today I asked my students to please write me a Flair compiler so that I can run my program. The course is officially underway.
Or: How to Learn Physics, Professional Golfer Edition
Bryson DeChambeau is a professional golfer, in the news recently for consecutive wins in the FedExCup playoff series. But he can also claim an unusual distinction as a student of physics:
In high school, he rewrote his physics textbook.
DeChambeau borrowed the textbook from the library and wrote down everything from the 180-page book into a three-ring binder. He explains: "My parents could have bought one for me, but they had done so much for me in golf that I didn't want to bother them in asking for a $200 book. ... By writing it down myself I was able to understand things on a whole comprehensive level.
I imagine that copying texts word-for-word was a more common learning strategy back when books were harder to come by, and perhaps it will become more common again as textbook prices rise and rise. There is certainly something to be said for it. Writing by hand takes time, and all the while our brains can absorb terms, make connections among concepts, and process the material into long-term memory. Zed Shaw argues for this as a great way to learn computer programming, implementing it as a pedagogical strategy in his "Learn <x> the Hard Way" series of books. (See Learn Python the Hard Way as an example.)
I don't think I've ever copied a textbook word-for-word, and I never copied computer programs from "Byte" magazine, but I do have similar experiences in note taking. I took elaborate notes all through high school, college, and grad school. In grad school, I usually rewrote all of my class notes -- by hand; no home PC -- as I reviewed them in the day or two after class. My clean, rewritten notes had other benefits, too. In a graduate graph algorithms course, they drew the attention of a classmate who became one of my best friends and were part of what attracted the attention of the course's professor, who asked me to consider joining his research group. (I was tempted... Graph algorithms was one of my favorite courses and research areas!)
I'm not sure many students these days benefit from this low-tech strategy. Most students who take detailed notes in my course seem to type rather than write which, if what I've read is correct, has fewer cognitive advantages. But at least those students are engaging with the material consciously. So few students seem to take detailed notes at all these days, and that's a shame. Without notes, it is harder to review ideas, to remember what they found challenging or puzzling in the moment, and to rehearse what they encounter in class into their long-term memories. Then again, maybe I'm just having a "kids these days" moment.
Anyway, I applaud DeChambeau for saving his parents a few dollars and for the achievement of copying an entire physics text. He even realized, perhaps after the fact, that it was an excellent learning strategy.
(The above passage is from The 11 Most Unusual Things About Bryson DeChambeau. He sounds like an interesting guy.)
If you don't sit facing the window, you could be in any town.
I read that line this morning in Maybe the Cumberland Gap just swallows you whole, where it is a bittersweet observation of the similarities among so many dying towns across Appalachia. It's a really good read, mostly sad but a little hopeful, that applies beyond one region or even one country.
My mind is self-centered, though, and immediately reframed the sentence in a way that cast light on my good fortune.
I just downloaded a couple of papers on return-oriented programming so that I can begin working with an undergraduate on an ambitious research project. I have a homework assignment to grade sitting in my class folder, the first of the semester. This weekend, I'll begin to revise a couple of lectures for my compiler course, on NFAs and DFAs and scanning text. As always, there is a pile of department work to do on my desk and in my mind.
I live in Cedar Falls, Iowa, but if I don't sit facing the window, I could be in Ames or Iowa City, East Lansing or Durham, Boston or Berkeley. And I like the view out of my office window very much, thank you, so I don't even want to trade.
Heading into a three-day weekend, I realize again how fortunate I am. Do I put my good fortune to good enough use?
As I a way to get into the right frame of mind for the new semester and the next iteration of my compiler course, I read Michael Hicks's Software Security is a Programming Languages Issue this morning. Hicks incorporates software security into his courses on the principles of programming languages, with two lectures on security before having students study and use Rust. The article has links to lecture slides and supporting material, which makes it a post worth bookmarking.
I started thinking about adding LangSec to my course late in the spring semester, as I brainstormed topics that might spice the rest of the course up for both me and my students. However, time was short, so I stuck with a couple of standalone sessions on topics outside the main outline: optimization and concatenative languages. They worked fine but left me with an itch for something new.
I think I'll use the course Hicks and his colleagues teach as a starting point for figuring out how I might add to next spring's course. Students are interested in security, it's undoubtedly an essential issue for today's grads, and it is a great way to demonstrate how the design of programming languages is more than just the syntax of a loop or the lambda calculus.
Hicks's discussion of Rust also connects with my fall course. Two years ago, an advanced undergrad used Rust as the implementation language for his compiler. He didn't know the language but wanted to pair it with Haskell in his toolbox. The first few weeks of the project were a struggle as he wrestled with mastering ownership and figuring out some new programming patterns. Eventually he hit a nice groove and produced a working compiler with only a couple of small holes.
I was surprised how easy it was for me install the tools I needed to compile, test, and explore his code. That experience increased my interest in learning the language, too. Adding it to my spring course would give me the last big push I need to buckle down.
This summer has been a blur of administrative stuff, expected and unexpected. The fall semester brings the respite of work I really enjoy: teaching compilers and writing some code. Hurray!
I just read on the old Agile/XP mailing list that Jerry Weinberg passed away on Tuesday, August 7. The message hailed Weinberg as "one of the finest thinkers on computer software development". I, like many, was a big fan of work.
My first encounter with Weinberg came in the mid-1990s when someone recommended The Psychology of Computer Programming to me. It was already over twenty years old, but it captivated me. It augmented years of experience in the trenches developing computer software with a deep understanding of psychology and anthropology and the firm but gentle mindset of a gifted teacher. I still refer back to it after all these years. Whenever I open it up to a random page, I learn something new again. If you've never read it, check it out now. You can buy the ebook -- along with many of Weinberg's books -- online through LeanPub.
After the first book, I was hooked. I never had the opportunity to attend one of Weinberg's workshops, but colleagues lavished them with praise. I should have made more of an effort to attend one. My memory is foggy now, but I do think I exchanged email messages with him once back in the late 1990s. I'll have to see if I can dig them up in one of my mail archives.
Fifteen years ago or so, I picked up a copy of Introduction to General Systems Thinking tossed out by a retiring colleague, and it became the first in a small collection of Weinberg books now on my shelf. As older colleagues retire in the coming years, I would be happy to salvage more titles and extend my collection. It won't be worth much on the open market, but perhaps I'll be able to share my love of Weinberg's work with students and younger colleagues. Books make great gifts, and more so a book by Gerald Weinberg.
Perhaps I'll share them with my non-CS friends and family, too. A couple of summers back, my wife saw a copy of Are Your Lights On?, a book Weinberg co-wrote with Donald Gause, sitting on the floor of my study at home. She read it and liked it a lot. "You get to read books like that for your work?" Yes.
I just read Weinberg's final blog entry earlier this week. He wasn't a prolific blogger, but he wrote a post every week or ten days, usually about consulting, managing, and career development. His final post touched on something that we professors experience at least occasionally: students sometimes solve the problems we et before them better than we expected, or better than we ourselves can do. He reminded people not to be defensive, even if it's hard, and to see the situation as an opportunity to learn:
When I was a little boy, my father challenged me to learn something new every day before allowing myself to go to bed. Learning new things all the time is perhaps the most important behavior in my life. It's certainly the most important behavior in our profession.
Weinberg was teaching us to the end, with grace and gratitude. I will miss him.
Oh, and one last personal note: I didn't know until after he passed that we shared the same birthday, a few years apart. A meaningless coincidence, of course, but it made me smile.
I spent a couple of hours this morning at a roundtable discussion listening to area tech employers talk about their work and their companies' needs. It was pretty enjoyable (well, except perhaps for the CEO who too frequently prefaced his remarks with "What the education system needs to understand is ..."). To a company, they all place a lot of value on the projects that job candidates have done. Their comments reminded me of an old MAA blog post in which a recent grad said:
During the fall of my junior year, I applied for an internship at Red Ventures, a data analytics and technology company just outside Charlotte. Throughout the rigorous interview process, it wasn't my GPA that stood out. I stood out among the applicants, in part, because I was able to discuss multiple projects I had taken ownership of and was extremely passionate about.
I encourage this mentality in my students, though I think "passionate about" is too strong a condition (not to mention cliché). Students should have a few projects that they are interested in, or proud of, or maybe just completed.
Most of the students taking my compiler course this fall won't be applying for a compiler job when they graduate, but they will have written a compiler as part of a team. They will have met a spec, collaborated on code, and delivered a working product. That is evidence of skill, to be sure, but also of hard work and persistence. It's a significant accomplishment.
The students who take our intelligent systems course or our real-time embedded systems will be able to say the same thing. Some students will also be able to point to code they wrote for a club or personal projects. They key is to build things, care about them, and "deliver", whatever that means in the context of that particular project.
I made note of one new piece of advice to give our students, offered by a former student I mentioned in a blog post many years ago who is now head of a local development team for mobile game developer Jam City: Keep all the code you write. It can be a GitHub repo, as many people now recommend, but it doesn't have to be. A simple zip file organized by courses and projects can be enough. Such a portfolio can show prospective employers what you've done, how you've grown, and how much you care about the things you make. It can say a lot.
You might even want to keep all that code for Future You. I'm old enough that it was difficult to keep digital copies of all the code I wrote in college. I have a few programs from my undergrad days and a few more from grad school, which have migrated across storage media as time passed, but I missing much of my class work as a young undergrad and all of the code I wrote in high school. I sometimes wish I could look back at some of that code...
In Getting Critiqued, Adam Morse reflects on his evolution from art student to web designer, and how that changed his relationship with users and critiques. Artists create things in which they are, at some level, invested. Their process matters. As a result, critiques, however well-intentioned, feel personal. The work isn't about a user; it's about you. But...
... design is different. As a designer, I don't matter. My work doesn't matter. Nothing I make matters in the context of my process. It's all about the people you are building for. You're just trying to solve problems for people. Once you realize this, it's the most liberating thing.
Now, criticism isn't really about you as artist. It's about how well the design meets the needs of the user. With that in mind, the artist can put some distance between himself or herself and think about the users. That's probably what the users are paying for anyway.
I've never been a designer, but I was fortunate to learn how better to separate myself from my work by participating in the software patterns community and its writers' workshop format. From the workshops, I came to appreciate the value of providing positive and constructive feedback in a supportive way. But I also learned to let critiques from others be about my writing and not about me. The ethos of writers' workshops is one of shared commitment to growth and so creates as supportive framework as possible in which to deliver suggestions. Now, even when I'm not in such an conspicuously supportive environment, I am better able to detach myself from my work. It's never easy, but it's easier. This mindset can wear off a bit over time, so I find an occasional inoculation via PLoP or another supportive setting to be useful.
Morse offers another source of reminder: the designs we create for the web -- and for most software, too-- are not likely to last forever. So...
Don't fall in love with borders, gradients, a shade of blue, text on blurred photos, fancy animations, a certain typeface, flash, or music that autoplays. Just get attached to solving problems for people.
That last sentence is pretty good advice for programmers and designers alike. If we detach ourselves from our specific work output a bit and instead attach ourselves to solving problems for other people, we'll be able to handle their critiques more calmly. As a result, we are also likely to do better work.
Some thoughts on studying computer science from Gian-Carlo Rota:
A large fraction of MIT undergraduates major in computer science or at least acquire extensive computer skills that are applicable in other fields. In their second year, they catch on to the fact that their required courses in computer science do not provide the whole story. Not because of deficiencies in the syllabus; quite the opposite. The undergraduate curriculum in computer science at MIT is probably the most progressive and advanced such curriculum anywhere. Rather, the students learn that side by side with required courses there is another, hidden curriculum consisting of new ideas just coming into use, new techniques and that spread like wildfire, opening up unsuspected applications that will eventually be adopted into the official curriculum.
Keeping up with this hidden curriculum is what will enable a computer scientist to stay ahead in the field. Those who do not become computer scientists to the second degree risk turning into programmers who will only implement the ideas of others.
MIT is, of course, an exceptional school, but I think Rota's comments apply to computer science at most schools. So much learning of CS happens in the spaces between courses: in the lab, in the student lounge, at meetings of student clubs, at part-time jobs, .... That can sometimes be a challenge for students who don't have much curiosity, or develop one as they are exposed to new topics.
As profs, we encourage students to be aware of all that is going on in computer science beyond the classroom and to take part in the ambient curriculum to the extent they are able. Students who become computer scientists only to the first degree can certainly find good jobs and professional success, but there are more opportunities open at the second degree. CS can also be a lot more fun there.
... from my morning reading.
First, a sentence from Bryan Caplan, about one of his influences, philosopher Michael Huemer:
I think what's great about this book, and really all of Mike's work, is he always tries to start off with premises that make sense to people who don't already agree, and then try to get somewhere.
I value people who take the time to construct arguments in this way. It's surprisingly rare in academic discourse and public discourse. Teachers usually learn pretty quickly, though, that the most effective way to teach is start where your students are: recognize the state of their knowledge and respect their current beliefs. I try to remind myself of this principle regularly during a course, or I'm likely to go off track.
Second, the closing exchange from a 1987 interview with Stanley Kubrick. Kubrick has been talking about how the critics' views of his films tend to evolve over time. The interviewer wrapped up the conversation with:
Well, you don't make it easy on viewers or critics. You create strong feelings, but you won't give us any easy answers.
That's because I don't have any easy answers.
That seems like a pretty good aspiration to have for teaching, that people can say it creates strong feelings but doesn't give any easy answers. Much of teaching is simpler than this, of course, especially in a field such as computer science. A closure is something that we can understand as it is, as is, say, an algorithm for parsing a stream of tokens. But after you learn a few concepts and start trying to build or understand a complex system, easy answers are much harder to come by. Even so, I do hope that students leave my courses with strong feelings about their craft. Those feelings may not match my own, and they'll surely still be evolving, but they will be a product of the student engaging with some big ideas and trying them out on challenging problems.
Maybe if I keep reading interested articles on the exercise the bike and making connections to my craft, I can get this computer science thing down better.
This post isn't really about chess, though it might seem at first to be.
In The Reviled Art, chess grandmaster Stuart Rachels says that most grandmasters don't like composed chess problems because they are too difficult. It's easy to imagine why average chessplayers find problems too difficult: they aren't all that great chess. But why grandmasters? Rachels contends that problems are hard for tournament players because they are counterintuitive: the solutions contradict the intuitions developed by players whose chess skill is developed and sharpened over the board.
Rachels then says:
Most problems stump me too, so I conceive of the time I spend looking at them as time spent preparing to appreciate their solutions -- not as time spent trying to solve them.
I love this attitude. If I view time spent banging my head against a puzzle or a hard problem as "trying to solve the problem", then not solving the problem might feel like failure. If I view that time as "preparing to appreciate the solution", then I can feel as if my time was well spent even if I don't solve it -- as long as I can appreciate the beauty or depth or originality of the solution.
This attitude is helpful outside of chess. Maybe I'm trying to solve a hard programming problem or trying to understand a challenging area of programming language theory that is new to me. I don't always solve the problem on my own or completely understand the new area without outside help or lots of time reading and thinking. But I often do appreciate the solution once I see it. All the time I spent working on the problem prepared me for that moment.
I often wish that more of my students would adopt Rachels's attitude. I frequently pose a problem for them to work on for a few minutes before we look at a solution, or several candidates, as a group. All too often some students look at the problem, think it's too difficult, and then just sit there waiting for me to show them the answer. This approach often results in them feeling two kinds of failure: they didn't solve the problem, and they don't even appreciate the solution when they see it. They haven't put in the work thinking about it that prepares their minds to really get the solution. Maybe I can do more to help students realize that the work is worth worth the effort even if they don't think they can solve the problem. Send me your suggestions!
Rachels's point about the counterintuitiveness of composed chess problems indicates another way in which trying to solve unorthodox problems can be worthwhile. Sometimes our intuitions let us down because they are too narrow, or even wrong. Trying to solve an unorthodox problem can help us broaden our thinking. My experience with chess compositions is that most of the ideas I need to solve them will not be helpful in over-the-board play; those kinds of positions simply don't occur in real games. But a few themes do apply, and practicing with them helps me learn how to play better in game situations. If nothing else, working on unorthodox problems reminds me to look outside the constraints of my intuitions sometimes when a problem in real life seems too hard.
Sidney Lumet, in his book Making Movies, writes:
Arthur Miller's first, and I think, only novel, Focus, was, in my opinion, every bit as good as his first produced play, All My Sons. I once asked him why, if he was equally talented in both forms, he chose to write plays. Why would he give up the total control of the creative process that a novel provides to write instead for communal control, where a play would first go into the hands of a director and then pass into the hands of a cast, set designer, producer, and so forth? His answer was touching. He loved seeing what his work evoked in others. The result could contain revelations, feelings, and ideas that he never knew existed when he wrote the play. That's what he hoped for.
Writing software for people to use is something quite different from writing a play for audiences to watch, but this paragraph brought to mind experiences I had as a grad student and new faculty member. As a part of my doctoral work, I implemented a expert system shells for a couple of problem-solving styles. Experts and grad students in domains such as chemical engineering, civil engineering, education, manufacturing, and tax accounting used these shells to build expert systems in their domains. I often found myself in the lab with these folks as they used my tools. I learned a lot by watching them and discussing with them the languages implemented in the tools. Their comments and ideas sometimes changed how I thought about the languages and tools, and I was able to fold some of these changes back into the systems.
Software design can be communal, too. This is, of course, one of the cornerstones of agile software development. Giving up control can help us write better software, but it can also be a source of the kind of pleasure I imagine Miller got from working to bring his plays to life on stage.
In an old blog post promoting his book on timing, Daniel Pink writes:
... Connie Gersick's research has shown that group projects rarely progress in a steady, linear way. Instead, at the beginning of a project, groups do very little. Then at a certain moment, they experience a sudden burst of activity and finally get going. When is that moment? The temporal midpoint. Give a team 34 days, they get started in earnest on day 17. Give a team 11 days, they get really get going on day 6. In addition, there’s other research showing that being behind at the midpoint--in NBA games and in experimental settings--can boost performance in the second half.
So we need to recognize midpoints and try to use them as a spark rather than a slump.
I wonder if this research suggests that we should favor shorter projects over longer ones. If most of us start going full force only at the middle of our projects, perhaps we should make the middle of our projects come sooner.
I'll admit that I have a fondness for short over long: short iterations over long iterations in software development, quarters over semesters in educational settings, short books (especially non-fiction) over long books. Shorter cycles seem to lead to higher productivity, because I spend more time working and less time ramping up and winding down. That seems to be true for my students and faculty colleagues, too.
In the paragraph that follows the quoted passage, Pink points inadvertently to another feature of short projects that I appreciate: more frequent beginnings and endings. He talks about the poignancy of endings, which adds meaning to the experience. On the other end of the cycle are beginnings, which create a sense of newness and energy. I always look forward to the beginning of a new semester or a new project for the energy it brings me.
Agile software developers know that, on top of these reasons, short projects offer another potent advantage: more opportunities to take stock of what we have learned and feed that learning back into what we do.
I spent big chunks of the last two days grading final projects and final exams for my Programming Languages course. Grading exams is important but not a lot of fun, so I released a little tension on Twitter as thoughts popped into my head:
I love final exam answers that appear to be randomly generated from a list of terms learned in the course.
Also fun: code that appears to be randomly generated from a list of functions learned in the course.
... and then a student surprises me with a creative, efficient solution. I like those answers just as much.
Answer of the day so far: "Local variables help us be more ambiguous solving many problems at once."
I don't know what that means, but I love it.
I hope folks did not think I was "punching down". I know how stressful final exams are for most students, and I know how challenging a comprehensive final exam in this course is. Students have to write code, explain ideas, and connect ideas to code. It's tough. The tweets were just me having a little fun during what is otherwise a slow, laborious process.
This exam ended differently than any of the previous finals in this course. The final question, worth 5% of the exam grade, was this:
Identify one topic from the course that is not covered by an exam question but about which you learned something valuable. Write two to three sentences about what you learned.
I used to include a more wide-ranging version of this question to end my AI final exams many years ago, in which students had a chance to write a summary of the important ideas they had learned in the course. I'm not sure what reminded of the idea (perhaps one of James Tanton's essays), but this seemed like a nice way to give students a chance to boost their grades without a curve. I figured I would be generous and give them some latitude with their own experiences. My hope was that students could end the exam on a good note, feeling positive about something they learned and appreciated rather than solving yet another problem I fished out of fifteen weeks of material. There was a small risk -- what if a bunch of them panicked at the unusual question and couldn't think of anything to say? But that risk exists for almost any question I ask.
The question seems to have gone over quite well. Some students talked about a specific topic from the course, among them variable arity functions, currying, higher-order procedures, and syntactic abstraction. That's mostly what I had in mind when I wrote the question, even if some of their answers were covered in part somewhere else on the exam. Others answered more generally than I expected. A couple talked about how the course gave them a deeper appreciation for data abstraction; a couple of others wrote about the experience of writing an interpreter and having to live with their design decisions as the code as it grew over three weeks. All but one student wrote an answer substantive and reflective enough that I didn't even have to think about how many points to award. I was happy to read them.
I really shouldn't have been surprised. Most students care more about their learning, and get more out of a class, than exam answers and classroom participation might indicate. They face a lot of pressures, and as a result have a limited amount of time and energy to express about any one course day in and day out. But making software matters to most of them; sometimes even big ideas matter. This question let them express some of what made the class work for them.
This problem had unexpected effect... I ended the exam on a good note. I put down my grading pen feeling good about the course, knowing that each student learned something that stood out to them, that they appreciated enough to write a few sentences about. I got a small glimpse of how they changed as a result of the course. I put the question on the exam for the students' sake, but it affected me as much as it affected them.
That's not a bad way to end the semester.
![]() |
In today's edition of "Sounds great, but...", I point you to Learn calculus like Huygens, a blog entry that relates some interesting history about the relationship between Gottfried Leibniz and Christiaan Huygens, "the greatest mathematician in the generation before Newton and Leibniz". It's a cool story: a genius of the old generation learns a paradigm-shifting new discipline via correspondence with a genius of the new generation who is in the process of mapping the discipline.
The "Sounds great, but..." part comes near the end of the article, when the author extrapolates from Huygens's attitude to what is wrong with math education these days. It seems that Huygens wanted to see connections between the crazy new operations he was learning, including second derivatives, and the real world. Without seeing these connections, he wasn't as motivated to put in the effort to learn them.
The author then asserts:
The point is not that mathematics needs to be applied. It is that it needs to be motivated. We don't study nature because we refuse to admit value in abstract mathematics. We study nature because she has repeatedly proven herself to have excellent mathematical taste, which is more than can be said for the run-of-the-mill mathematicians who have to invent technical pseudo-problems because they can't solve any real ones.
Yikes, that got ugly fast. And it gets uglier, with the author eventually worrying that we alienate present-day Huygenses with a mass of boring problems that are disconnected from reality.
I actually love the heart of that paragraph: We don't study nature because we refuse to admit value in abstract mathematics. We study nature because she has repeatedly proven herself to have excellent mathematical taste.... This is a reasonable claim, and almost poetic. But the idea that pseudo-problems invented by run-of-the-mill mathematicians are the reason students today aren't motivated to learn calculus or other advanced mathematics seems like a massive overreach.
I'm sympathetic to the author's position. I watched my daughters slog through AP Calculus, solving many abstract problems and many applied problems that had only a thin veneer of reality wrapped around them. As someone who enjoyed puzzles for puzzles' sake, I had enjoyed all of my calculus courses, but it seemed as if my daughters and many of their classmates never felt the sort of motivation that Huygens craved and Leibniz delivered.
I also see many computer science students slog through courses in which they learn to program, apply computational theory to problems, and study the intricate workings of software and hardware systems. Abstract problems are a fine way to learn how to program, but they don't always motivate students to put in a lot of work on challenging material. However, real problems can be too unruly for many settings, though, so simplified, abstract problems are common.
But it's not quite as easy to fix this problem by saying "learn calculus like Huygens: solve real problems!". There are a number of impediments to this being a straightforward solution in practice.
One is the need for domain knowledge. Few, if any, of the students sitting in today's calculus classes have much in common with Huygens, a brilliant natural scientist and inventor who had spent his life investigating hard problems. He brought a wealth of knowledge to his study of mathematics. I'm guessing that Leibniz didn't have to search long to find applications with which Huygens was already familiar and whose solutions he cared about.
Maybe in the old days all math students were learning a lot of science at the same time as they learned math, but that is not always so now. In order to motivate students with real problems, you need real problems from many domains, in hopes of hitting all students' backgrounds and interests. Even then, you may not cover them all. And, even if you do, you need lots of problems for them to practice on.
I think about these problems every day from the perspective of a computer science prof, and I think there are a lot of parallels between motivating math students and motivating CS students. How do I give my students problems from domains they both know something about and are curious enough to learn more about? How do I do that in a room with thirty-five students with as many different backgrounds? How do I do that in the amount of time I have to develop and extend my course?
Switching to a computer science perspective brings to mind a second impediment to the "solve real problems" mantra. CS education research offers some evidence that using context-laden problems, even from familiar contexts, can make it more difficult for students to solve programming problems. The authors of the linked paper say:
Our results suggest that any advantage conveyed by a familiar context is dominated by other factors, such as the complexity of terminology used in the description, the length of the problem description, and the availability of examples. This suggests that educators should focus on simplicity of language and the development of examples, rather than seeking contexts that may aid in understanding problems.
Using familiar problems to learn new techniques may help motivate students initially, but that may come at other costs. Complexity and confusion can be demotivating.
So, "learn calculus like Huygens" sounds great, but it's not quite so easy to implement in practice. After many years designing and teaching courses, I have a lot of sympathy for the writers of calculus and intro programming textbooks. I also don't think it gets much easier as students advance through the curriculum. Some students are motivated no matter what the instructor does; others need help. The tension between motivation and the hard work needed to master new techniques is always there. Claims that the tension is easy to resolve are usually too glib to be helpful.
The Huygens-Leibniz tale really is a cool story, though. You might enjoy it.
(The image above is a sketch of Christiaan Huygens's first pendulum clock, from 1657. Source: Wikipedia.)
I've been reading my way through Frank Chimero's talks online and ran across a great bit on maps and interaction design in What Screens Want. One of the paragraphs made me think about the abstractions that show up in CS courses:
When I realized that, a little light went off in my head: a map's biases do service to one need, but distort everything else. Meaning, they misinform and confuse those with different needs.
CS courses are full of abstractions and models of complex systems. We use examples, often simplified, to expose or emphasize a single facet a system, as a way to help students cut through the complexity. For example, compilers and full-strength interpreters are complicated programs, so we start with simple interpreters operating over simple languages. Students get their feet wet without drowning in detail.
In the service of trying not to overwhelm students, though, we run the risk of distorting how they think about the parts we left out. Worse, we sometimes distort even their thinking about the part we're focusing on, because they don't see its connections to the more complete picture. There is an art to identifying abstractions, creating examples, and sequencing instruction. Done well, we can minimize the distortions and help students come to understand the whole with small steps and incremental increases in size and complexity.
At least that's what I think on my good days. There are days and even entire semesters when things don't seem to progress as smoothly as I hope or as smoothly as past experience has led me to expect. Those days, I feel like I'm doing violence to an idea when I create an abstraction or adopt a simplifying assumption. Students don't seem to be grokking the terrain, so change the map. We try different problems or work through more examples. It's hard to find the balance sometimes between adding enough to help and not adding so much as to overwhelm.
The best teachers I've encountered know how to approach this challenge. More importantly, they seem to enjoy the challenge. I'm guessing that teachers who don't enjoy it must be frustrated a lot. I enjoy it, and even so there are times when this challenge frustrates me.
Greg Wilson wrote a short blog post recently about why JavaScript isn't suitable for teaching Data Carpentry-style workshops. In closing, he suggests an unusual way to design programming languages:
What I do know is that the world would be a better place if language designers adopted tutorial-driven design: write the lessons that introduce newcomers to the language, then implement the features those tutorials require.
That's a different sort of TDD than I'm used to...
This is the sort of idea that causes me to do a double or triple take. At first, it has an appealing ring to it, when considering how difficult it is to teach most programming languages to novices. Then I think a bit and decide that it sounds crazy because, really, are we going to hamstring our languages by focusing on the struggles of beginners? But then it sits in my mind for a while and I start to wonder if we couldn't grow a decent language this way. It's almost like using the old TDD to implement new the TDD.
The PLT Scheme folks have designed a set of teaching languages that enable beginners to grow into an industry-strength language. That design project seems to have worked from the outsides in, with a target language in mind while designing the teaching languages. Maybe Wilson's idea of starting at the beginning isn't so crazy after all.
It's been a tough semester. On top of the usual business, there have been a couple of extra stresses. First, I've been preparing for the departure of a very good friend, who is leaving the university and the area for family and personal reasons. Second, a good friend and department colleague took an unexpected leave that turned into a resignation. Both departures cast a distant pall over my workdays. This week, though, has offered a few positive notes to offset the sadness.
Everyone seems to complain about email these days, and I certainly have been receiving and sending more than usual this semester, as our students and I adjust to the change in our faculty. But sometimes an email message makes my day better. Exhibit 1, a message from a student dealing with a specific issue:
Thank you for your quick and helpful response!
Things don't look so complicated or hopeless now.
Exhibit 2, a message from a student who has been taming the bureaucracy that arises whenever two university systems collide:
I would like to thank you dearly for your prompt and thorough responses to my numerous emails. Every time I come to you with a question, I feel as though I am receiving the amount of respect and attention that I wish to be given.
Compliments like these make it a lot easier to muster the energy to deal with the next batch of email coming in.
There has also been good news on the student front. I received email from a rep at a company in Madison, Wisconsin, where one of our alumni works. They are looking for developers to work in a functional programming environment and are having a hard time filling the positions locally, despite the presence of a large and excellent university in town. Our alum is doing well enough that the company would like to hire more from our department, which is doing a pretty good job, too.
Finally, today I spoke in person with two students who had great news about their futures. One has accepted an offer to join the Northwestern U. doctoral program and work in the lab of Kenneth Forbus. I studied Forbus's work on qualitative reasoning and analogical reasoning as a part of my own Ph.D. work and learned a lot from him. This is a fantastic opportunity. The other student has accepted an internship to work at PlayStation this summer, working on the team that develops the compilers for its game engines. He told me, "I talked a lot about the project I did in your course last semester during my interview, and I assume that's part of the reason I got an offer." I have to admit, that made me smile.
I had both of these students in my intro class a few years back. They would have succeeded no matter who taught their intro course, or the compiler course, for that matter, so I can't take any credit for their success. But they are outstanding young men, and I have had the pleasure of getting to know over the last four years. News of the next steps in their careers makes me feel good, too.
I think I have enough energy to make it to the end of the semester now.
Yesterday morning I read The Good Room, a talk Frank Chimero gave last month. Early on in the talk, Chimero says:
Let me start by stating something obvious: in the last decade, technology has transformed from a tool that we use to a place where we live.
This sentence jumped off the page both for the content of the assertion and for the decade time frame with which he bounds it. In the fall of 2003, I taught a capstone course for non-majors that is part of my university's liberal arts core. The course, titled "Environment, Technology, and Society", brings students from all majors on campus together in a course near the end of their studies, to apply their general education and various disciplinary expertises to problems of some currency in the world. As you might guess from the title, the course focuses on problems at the intersection of the natural environment, technology, and people.
My offering of the course put on a twist on the usual course content. We focused on the man-made environment we all live in, which even by 2003 had begun to include spaces carved out on the internet and web. The only textbook for the course was Donald Norman's The Design of Everyday Things, which I think every university graduate should have read. The topics for the course, though, had a decided IT flavor: the effect of the Internet on everyday life, e-commerce, spam, intellectual property, software warranties, sociable robots, AI in law and medicine, privacy, and free software. We closed with a discussion of what an educated citizen of the 21st century ought to know about the online world in which they would live in order to prosper as individuals and as a society.
The change in topic didn't excite everyone. A few came to the course looking forward to a comfortable "save the environment" vibe and were resistant to considering technology they didn't understand. But most were taking the course with no intellectual investment at all, as a required general education course they didn't care about and just needed to check off the list. In a strange way, their resignation enabled them to engage with the new ideas and actually ask some interesting questions about their future.
Looking back now after fifteen years , the course design looks pretty good. I should probably offer to teach it again, updated appropriately, of course, and see where young people of 2018 see themselves in the technological world. As Chimero argues in his talk, we need to do a better job building the places we want to live in -- and that we want our children to live in. Privacy, online peer pressure, and bullying all turned out differently than I expected in 2003. Our young people are worse off for those differences, though I think most have learned ways to live online in spite of the bad neighborhoods. Maybe they can help us build better places to live.
Chimero's talk is educational, entertaining, and quotable throughout. I tweeted one quote: "How does a city wish to be? Look to the library. A library is the gift a city gives to itself." There were many other lines I marked for myself, including:
In Material as Metaphor, the artist Anni Albers talks about how she came to choose media in which she worked:
How do we choose our specific material, our means of communication? "Accidentally". Something speaks to us, a sound, a touch, hardness or softness, it catches us and asks us to be formed. We are finding our language, and as we go along we learn to obey their rules and their limits. We have to obey, and adjust to those demands. Ideas flow from it to us and though we feel to be the creator we are involved in a dialogue with our medium. The more subtly we are tuned to our medium, the more inventive our actions will become. Not listening to it ends in failure.
This expresses much the way I feel about different programming languages and styles. I can like them all, and sometimes do! I go through phases when one style speaks to me more than another, or when one language seems to be in sync with how I am thinking. When that happens, I find myself wanting to learn its rules, to conform so that I can reach a point where I feel creative enough to solve interesting problems in the language.
If I find myself not liking a language, it's usually because I'm not listening to it; I'm fighting back. When I first tried to learn Haskell, I refused to bend to its style of functional programming. I had worked hard to grok FP in Scheme, and I was so proud of my hard-won understanding that I wanted to impose it on the new language. Eventually, I retreated for a while, returned more humbly, and finally came to appreciate Haskell, if not master it deeply.
My experience with Smalltalk went differently. One summer I listened to what it was telling me, slowly and patiently, throwing code away and starting over several times on an application I was trying to build. This didn't feel like a struggle so much as a several-month tutoring session. By the end, I felt ideas flowing through me. I think that's the kind of dialogue Albers is referring to.
If I want to master a new programming language, I have to be willing to obey its limits and to learn how to use its strengths as leverage. This can be a conscious choice. It's frustrating when that doesn't seem to be enough.
I wish I could always will myself into the right frame of mind to learn a new way of thinking. Albers reminds us that often a language speaks to us first. Sometimes, I just have to walk away and wait until the time is right.
While discussing the effective use of discontinuities in film, both motion within a context versus change of context, Walter Murch tells a story about... bees:
A beehive can apparently be moved two inches each night without disorienting the bees the next morning. Surprisingly, if it is moved two miles, the bees also have no problem: They are forced by the total displacement of their environment to re-orient their sense of direction, which they can do easily enough. But if the hive is moved two yards, the bees become fatally confused. The environment does not seem different to them, so they do not re-orient themselves, and as a result, they will not recognize their own hive when they return from foraging, hovering instead in the empty space where the hive used to be, while the hive itself sits just two yards away.
This is fascinating, as well being a really cool analogy for the choices movies editors face when telling a story on film. Either change so little that viewers recognize the motion as natural, or change enough that they re-orient their perspective. Don't stop in the middle.
What is even cooler to me is that this story appears in a footnote.
One of the things I've been loving about In the Blink of an Eye is how Murch uses footnotes to teach. In many books, footnotes contain minutia or references to literature I'll never read, so I skip them. But Murch uses them to tell stories that elaborate on or deepen his main point but which would, if included in the text, interrupt the flow of the story he has constructed. They add to the narrative without being essential.
I've already learned a couple of cool things from his footnotes, and I'm not even a quarter of the way into the book. (I've been taking time to mull over what I read...) Another example: while discussing the value of discontinuity as a story-telling device, Murch adds a footnote that connects this practice to the visual discontinuity found ancient Egyptian painting. I never knew before why the perspective in those drawings was so unusual. Now I do!
My fondness for Murch's footnotes may stem from something more than their informative nature. When writing up lecture notes for my students, I like to include asides, digressions, and links to optional readings that expand on the main arc of the story. I'd like for them to realize that what they are learning is part of a world bigger than our course, that the ideas are often deeper and have wider implications than they might realize. And sometimes I just like to entertain with a connection. Not all students care about this material, but for the ones who do, I hope they get something out of them. Students who don't care can do what I do in other books: skip 'em.
This book gives me a higher goal to shoot for when including such asides in my notes: elaborate without being essential; entice without disrupting.
Walter Murch, in In the Blink of an Eye:
A vast amount of preparation, really, to arrive at the innocuously brief moment of decisive act: the cut -- the moment of transition from one shot to the next -- something that, appropriately enough, should look almost self-evidently simple and effortless, if it is even noticed at all.
This can apply to software development, I think, but I haven't thought about that yet. I read the passage at the beginning of a new semester, when my mind is filled with another offering of my programming languages course. So I've been thinking about how this quote works in the context of my course: the overall structure of the course as well as the structure of individual class sessions. The course consists of three major units, connected by a thread, with the third having its own substructure. Each session is a more cohesive story with its own microstructure: the flow of a lecture, the sequence of exercises students do, the sequence of examples that students see. Moments of transition are everywhere, at multiple scales.
When a session goes badly, or not as well as I'd hoped, I am quite aware of the cuts that did not work. They seem awkward, or ill-motivated, or jarring. The students notice some of these, too, but they don't always let me know of their disorientation right away. That's one benefit of building frequent exercises and review questions into a class session: at least I have a chance of finding out sooner when something isn't working the way I'd planned.
Reading Murch has given me a new vocabulary for thinking about transitions visually. In particular, I've been thinking about two basic types of transition:
![]() |
For example, I've been trying to think more often about how one kind of cut can be mistaken for the other and how that might affect students. What happens when what I intend as a small move within a context seems so disconnected for students that they think I've changed contexts? What happens when what I intend as a big shift to a new topic sounds to students like the WAH-WAH-WAH of Charlie Brown's teacher? I can always erect massive signposts to signal transitions of various kinds, but that can be just as jarring to readers or listeners as unannounced cuts. It is also inelegant, because it fails to respect their ability to construct their own understanding of what they are learning.
Trying on Murch's perspective has not been a panacea. The first session of the course, the one with a new opening story, went well, though it needs a few more iterations to become good. My second session went less well. I tried to rearrange a session that already worked well, and my thinking about transitions was too self-conscious. The result was a synthesis of two threads that didn't quite work, leaving me feeling a bit jumbled myself by connections that were incomplete and jumps that seemed abrupt. Fortunately, I think I managed to recognize this soon enough in class that I was able to tell a more coherent story than my outline prepared me to tell. The outline needs a lot more work.
In the longer run, though, thinking about transitions more carefully should help me do a better job leading students in a fruitful direction. I'll keep at it.
This was posted on the Racket mailing list recently:
"The Little Schemer" starts slow for people who have programmed before, but seeing that I am only half-way through and already gained some interesting knowledge from it, one should not underestimate the acceleration in this book.
The Little Schemer is the only textbook I assign in my Programming Languages course. These students usually have only a little experience: often three semesters, two in Python and one in Java; sometimes just the two in Python. A few of the students who work in the way the authors intend have an A-ha! experience while reading it. Or maybe they are just lucky... Other students have only a WTF? experience.
Still, I assign the book, with hope. It's relatively inexpensive and so worth a chance that a few students can use it to grok recursion, along with a way of thinking about writing functions that they haven't seen in courses or textbooks before. The book accelerates from the most basic ideas of programming to "interesting" knowledge in a relatively short number of pages. Students who buy in to the premise, hang on for the ride, and practice the ideas in their own code soon find that they, too, have accelerated as programmers.
This morning, I read the first few pages of In the Blink of an Eye, an essay on film editing by Walter Murch. He starts by talking about his work on Apocalypse Now, which took well over a year in large part because of the massive amount of film Coppola shot: 1,250,000 linear feet, enough for 230 hours of running time. The movie ended up being about two hours and twenty-five minutes, so Murch and his colleagues culled 95 minutes of footage for every minute that made it into the final product. A more typical project, Murch says, has a ratio of 20:1.
Even at 20:1, Murch's story puts into clearer light the amount of raw material I create when designing a typical session for one of my courses. The typical session mixes exposition, examples, student exercises, and (less than I'd like to admit) discussion. Now, whenever I feel like a session comes up short of my goal, I will think back on Murch's 20:1 ratio and realize how much harder I might work to produce enough material to assemble a good session. If I want one of my sessions to be an Apocalypse Now, maybe I'll need to shoot higher.
This motivation comes at a favorable time. Yesterday I had a burst of what felt like inspiration for a new first day to my Programming Languages course. At the end of the brainstorm came what is now the working version of my opening line in the course: "In the beginning, there was assembly language.". Let's see if I have enough inspiration -- and make enough time -- to turn the idea into what I hope it can be: a session that fuels my students' imagination for a semester's journey through Racket, functional programming, and examining language ideas with interpreters.
I do hope, though, that the journey itself does not bring to mind Apocalypse Now.
I received a Change of Terms message yesterday from one of my mutual fund companies, which included this unexpected note:
Direction to buy or sell Vanguard funds must be placed online or verbally and may no longer be submitted in writing.
I haven't mailed Vanguard or any other financial services company a paper form or a paper check in years, but still. When I was growing up, I never would have imagined that I would see the day when you could not mail a letter to a company in order to conduct financial business. Busy, busy, busy.
In the academic world, this is the time for another type change of terms, as we prepare to launch our spring semester semester on Monday. The temperatures in my part of the country the last two weeks make the name of the semester a cruel joke, but the hope of spring lives.
For me, the transition is from my compiler course to my programming languages course. Compilers went as well this fall as it has gone in a long time; I really wish I had blogged about it more. I can only hope that Programming Languages goes as well. I've been reading about some ways I might improve the course pedagogically. That will require me to change some old habits, but trying to do so is part of the fun of teaching. I intend to blog about my experiences with the new ideas. As I said, the hope of spring lives.
In any case, I get to write Racket code all semester, so at least I have that going for me, which is nice.
In this interview with Adam Grant, Walter Jacobson talks about some of the things he learned while writing biographies of Benjamin Franklin, Albert Einstein, Steve Jobs, and Leonardo da Vinci. A common theme is that all four were curious and interested in a wide range of topics. Toward the end of the interview, Jacobson says:
We of humanities backgrounds are always doing the lecture, like, "We need to put the 'A' in 'STEM', and you've got to learn the arts and the humanities." And you get big applause when you talk about the importance of that.
But we also have to meet halfway and learn the beauty of math. Because people tell me, "I can't believe somebody doesn't know the difference between Mozart and Haydn, or the difference between Lear and Macbeth." And I say, "Yeah, but do you know the difference between a resistor and a transistor? Do you know the difference between an integral and a differential equation?" They go, "Oh no, I don't do math, I don't do science." I say, "Yeah, but you know what, an integral equation is just as beautiful as a brush stroke on the Mona Lisa." You've got to learn that they're all beautiful.
Appreciating that beauty made Leonardo a better artist and Jobs a better technologist. I would like for the students who graduate from our CS program to know some literature, history, and art and appreciate their beauty. I'd also like for the students who graduate from our university with degrees in literature, history, art, and especially education to have some knowledge of calculus, the Turing machine, and recombinant DNA, and appreciate their beauty.
This morning I read an interview with Steven Soderbergh, which is mostly about his latest project, the app/series, "Mosaic". A few weeks ago, "Mosaic" was released as an app in advance of its debut on HBO. Actually, that's not quite right. It is an app, which will also be released later in series form, and then only because Soderbergh needed money to finish the app version of the film. In several places, he talks about being a director in ways that made me think of being a university professor these days.
One was in terms of technology. There are moments in the "Mosaic" app when it offers the viewer an opportunity to digress and read a document, to flash back, or to flash forward. The interviewer is intrigued by the notion that a filmmaker would be willing to distract the viewer in this way, sending texts and pushing notifications that might disrupt the experience. Soderbergh responded:
My attitude was, "Look, we've gotten used to watching TV now with three scrolling lines of information at the bottom of the screen all the time. People do not view that stuff the same way that they would have viewed it 20 years ago."
... To not acknowledge that when you're watching something on your phone or iPad that there are other things going on around you is to be in denial. My attitude is, "Well, if they're going to be distracted by something, let it be me!"
I'm beginning to wonder if this wouldn't be a healthier attitude for us to have as university instructors. Maybe I should create an app that is my course and let students experience the material using what is, for them, a native mode of interaction? Eventually they'll have to sit down and do the hard work of solving problems and writing code, but they could come to that work in a different way. There is a lot of value in our traditional modes of teaching and learning, but maybe flowing into our students' daily experience with push requests and teaser posts would reach them in a different way.
Alas, I doubt that HBO will front me any money to make my app, so I'll have to seem other sources of financing.
On a more personal plane, I was struck by something that Soderbergh said about the power directors have over the people they work with:
What's also interesting, given the environment we're in right now, is that I typically spend the last quarter of whatever talk I'm giving [to future fillmakers] discussing personal character, how to behave, and why there should be some accepted standard of behavior when you interact with people and how you treat people. Particularly when you're in a position like that of a director, which is an incredibly powerful situation to be in, pregnant with all kinds of opportunity to be abusive.
... if you're in a position of power, you can look at somebody sideways and destroy their week, you know? You need to be sensitive to the kind of power that a director has on a set.
It took me years as a teacher to realize the effect that an offhand remark could have on a student. I could be lecturing in class, or chatting with someone in my office, and say something about the course, or about how I work, or about how students work or think. This sentence, a small part of a larger story, might not mean all that much to me, and yet I would learn later that it affected how the student felt about himself or herself for a week, or for the rest of the course, or even longer. This effect can be positive or negative, of course, depending on the nature of the remark. As Soderbergh says, it's worth thinking about how you behave when you interact with people, especially when you're in a position of relative authority, in particular as a teacher working with young people.
This applies to our time as a parent and a spouse, too. Some of my most regrettable memories over the years are of moments in which I made an offhand remark, thoughtlessly careless, that cut deep into the heart of my wife or one of my daughters. Years later, they rarely remember the moment or the remark, but I'm sad for the pain I caused in that moment and for any lingering effect it may have. The memory is hard for me to shake. I have to hope that the good things I have said and done during our time together meant as much. I can also try to do better now. The same holds true for my time working with students.
While grading one last project for the semester, I ran across this gem:
if checkInputParameters() == False: [...]
This code was not written by a beginning programmer, but a senior who likely will graduate in May. Sigh.
I inherited this course late in the semester, so it would be nice if I could absolve myself of responsibility for allowing a student to develop such a bad habit. But this isn't the first time I've seen this programming pattern. I've seen it in my courses and in other faculty's courses, in first-year courses and fourth-year courses. In fact, I was so certain that I blogged about the pattern before that I spent several minutes trolling the archive. Alas, I never found the entry.
Don't let anyone tell you that Twitter can't be helpful. Within minutes of tweeting my dismay, two colleagues suggested new strategies for dealing with this anti-pattern.
Sylvain Leroux invoked Star Trek:
James T. Kirk "Kobayashi Maru"-style solution: Hack CPython to properly parse that new syntax:
unless checkInputParameters(): ...
Maybe I'll turn this into a programming exercise for the next student in my compiler class who wanders down the path of temptation. Deep in my heart, though, I know that enterprising programmers will use the new type of statement to write this:
unless checkInputParameters() == True: [...]
Agile Hulk suggested a fix:
if (checkInputParameters() == False) == True:
This might actually be a productive pedagogical tack to follow: light-hearted and spot on, highlighting the anti-pattern's flaw by applying it to its own output. ("Anti-pattern eats its own dog food.") With any luck, some students will be enlightened, Zen-like. Others will furrow their brow and say "What?"
... and even if we look past the boolean crime in our featured code snippet, we really ought to take on that function name. 'Tis the season, though, and the semester is over. I'll save that rant for another time.
We are deep into fall semester. The three teams in my compilers course are making steady progress toward a working compiler, and I'm getting so excited by that prospect that I've written a few new programs for them to compile. The latest two work with Kaprekar numbers.
Yet I've also found myself thinking already quite a bit about my spring programming languages course.
I have not made significant changes to this course (which introduces students to Racket, functional programming, recursive programming over algebraic data types, and a few principles of programming languages) in several years. I don't know if I'm headed for a major re-design yet, but I do know that several new ideas are commingling in my mind and encouraging me to think about improvements to the course.
The first trigger was reading How to Switch from the Imperative Mindset, which approaches learning functional style explicitly as a matter establishing new habits. My students come to the course having learned an imperative style in Python, perhaps with some OO in Java thrown in. Most of them are not yet 100% secure in their programming skills, and the thought of learning a new style is daunting. They don't come to the course asking for a new set of habits.
One way to develop a new set of habits is to recognize the cues that trigger an old habit, learn a new response, and then rehearse that response until it becomes a new habit. The How to Switch... post echoes a style that I have found effective when teaching OOP to programmers with experience in a procedural language, and I'm thinking about how to re-tool part of my course to use this style more explicitly when teaching FP.
My idea right now is something like this. Start with simple examples from the students' experience processing arrays and lists of data. Then work through solutions in sequence, such as:
We can also do this with built-in functions, perhaps to start, which eliminates the need to write a user-defined function.
In effect, this refactors code that the students are already comfortable with toward common functional patterns. I can use the same sequence of steps for mapping, folding, and reducing, which will reinforce the thinking habits students need to begin writing FP code from the original cues. I'm only just beginning to think about this approach, but I'm quite comfortable using a "refactoring to patterns" style in class.
Going in this direction will help me achieve another goal I have in mind for next semester: making class sessions more active. This was triggered by my post-mortem of the last course offering. Some early parts of the course consist of too much lecture. I want to get students writing small bits of code sooner, but with more support for taking small, reliable steps.
Paired this change to what happens in class are changes to what happens before students come to class. Rather than me talking about so many things in class, I hope to have
This change will require me to package my notes differently and also to create triggers and scaffolding for the students' experimentation before coming to class. I'm thinking of this as something like a flipped classroom, but with "watching videos" replaced by "playing with code".
Finally, this blog post triggered a latent desire to make the course more effective for all students, wherever they are on the learning curve. Many students come to the course at roughly same level of experience and comfort, but a few come in struggling from their previous courses, and a few come in ready to take on bigger challenges. Even those broad categories are only approximate equivalence classes; each student is at a particular point in the development we hope for them. I'd like to create experiences that can help students all of these students learn something valuable for them.
I've only begun to think about the ideas in that post. Right now, I'm contemplating two of ideas from the section on getting to know my students better: gathering baseline data early on that I can use to anchor the course, and viewing grading as planning. Anything that can turn the drudgery of grading into a productive part of the course for me is likely to improve my experience in the course, and that is likely to improved my students' experience, too.
I have more questions than answers at this point. That's part of the fun of re-designing a course. I expect that things will take better shape over the next six weeks or so. If you have any suggestions, email me or tweet to me at @wallingf.
The opening talk of the CS Education Summit this week considered the challenges facing CS education in a time of surging enrollments and continued concerns about the diversity of the CS student population. In the session that followed, Eric Roberts and Jodi Tims presented data that puts the current enrollment surge into perspective, in advance of a report from the National Academy of Science.
In terms of immediate takeaway, Eric Roberts's comments were gold. Eric opened with Stein's Law: If something is unsustainable, it will stop. Stein was an economist whose eponymous law expresses one of those obvious truths we all seem to forget about in periods of rapid change: If something cannot go on forever, it won't. You don't have to create a program to make it stop. A natural corollary is: If it can't go on for long, you don't need a program to deal with it. It will pass soon.
Why is that relevant to the summit? Even without continued growth, current enrollments in CS majors is unsustainable for many schools. If the past is any guide, we know that many schools will deal with unsustainable growth by limiting the number of students who start or remain in their major.
Roberts has studied the history of CS boom-and-bust cycles over the last thirty years, and he's identified a few common patterns:
So the challenge of booming enrollments exacerbates the challenge to increase diversity. The boom might decrease diversity, but when it ends -- and it will, if we limit enrollments -- our diversity rarely recovers. That's the story of the last three booms.
In order to grow capacity, the most immediate solution is to hire more professors. I hope to write more about that soon, but for now I'll mention only that the problem of hiring enough faculty to teach all of our students has at east two facets. The first is that many schools simply don't have the money to hire more faculty right now. The second is that there aren't enough CS PhDs to go around. Roberts reported that, of last year's PhD grads, 83% took positions at R1 schools. That leaves 17% for the rest of us. "Non-R1 schools can expect to hire a CS PhD every 27 years." Everyone laughed, but I could see anxiety on more than a few faces.
The value of knowing this history is that, when we go to our deans and provosts, we can do more than argue for more resources. We can show the effect of not providing the resources needed to teach all the students coming our way. We won't just be putting the brakes on local growth; we may be helping to create the next enrollment crash. At a school like mine, if we teach the people of our state that we can't handle their CS students, then the people of our state will send their students elsewhere.
The problem for any one university, of course, is that it can act only based on its own resources and under local constraints. My dean and provost might care a lot about the global issues of demand for CS grads and need for greater diversity among CS students. But their job is to address local issues with their own (small) pool of money.
I'll have to re-read the papers Roberts has written about this topic. His remarks certainly gave us plenty to think about, and he was as engaging as ever.
Today and tomorrow, I am at a CS Education Summit in Pittsburgh. I've only been to Pittsburgh once before, for ICFP 2002 (the International Conference on Functional Programming) and am glad to be back. It's a neat city.
The welcome address for the summit was given by Dr. Farnam Jahanian, the interim president at Carnegie Mellon University. Jahanian is a computer scientist, with a background in distributed computing and network security. His resume includes a stint as chair of the CS department at the University of Michigan and a stint at the NSF.
Welcome addresses for conferences and workshops vary in quality. Jahanian gave quite a good talk, putting the work of the summit into historical and cultural context. The current boom in CS enrollments is happening at a time when computing, broadly defined, is having an effect in seemingly all disciplines and all sectors of the economy. What does that mean for how we respond to the growth? Will we see that the current boom presages a change to the historical cycle of enrollments in coming years?
Jahanian made three statements in particular that for me capture the challenge facing CS departments everywhere and serve as a backdrop for the summit:
Thus the idea of a CS education summit. I'm glad to be here.
(*) In my experience, it is much more likely to find a person with a CS or math PhD and significant educational background in the humanities than to find a person with a humanities PhD and significant educational background in CS or math (or any other science, for that matter). One of my hopes for the current trend of increasing interest in CS among non-CS majors is that we an close this gap. All of the departments on our campuses, and thus all of our university graduates, will be better for it.
Yesterday, I mentioned rewriting the rules for computing FIRST and FOLLOW sets using only "plain English". As I was refactoring my descriptions, I realized that one of the reasons students have difficulty with many textbook treatments of the algorithms is that the books give complete and correct definitions of the sets upfront. The presence of X := ε rules complicates the construction of both sets, but they are unnecessary to understanding the commonsense ideas that motivate the sets. Trying to deal with ε too soon can interfere with the students learning what they need to learn in order to eventually understand ε!
When I left the ε rules out of my descriptions, I ended up with what I thought were an approachable set of rules:
These rules are incomplete, but they have offsetting benefits. Each of these cases is easy to grok with a simple example or two. They also account for a big chunk of the work students need to do in constructing the sets for a typical grammar. As a result, they can get some practice building sets before diving into the gnarlier details ε, which affects both of the main rules above in a couple of ways.
These seems like a two-fold application of the Concrete, Then Abstract pattern. The first is the standard form: we get to see and work with accessible concrete examples before formalizing the rules in mathematical notation. The second involves the nature of the problem itself. The rules above are the concrete manifestation of FIRST and FOLLOW sets; students can master them before considering the more abstract ε cases. The abstract cases are the ones that benefit most from using formal notation.
I think this is an example of another pattern that works well when teaching. We might call it "Learn Exceptions Later", "Handle Exceptions Later", "Save Exceptions For Later", or even "Treat Exceptions as Exceptions". (Naming things is hard.) It is often possible to learn a substantial portion of an idea without considering exceptions at all, and doing so prepares students for learning the exceptions anyway.
I guess I now have at least one idea for my next PLoP paper.
Ironically, writing this post brings to mind a programming pattern that puts exceptions up top, which I learned during the summer Smalltalk taught me OOP. Instead of writing code like this:
if normal_case(x) then // a bunch // of lines // of code // processing x else throw_an_erroryou can write:
if abnormal_case(x) then throw_an_errorThis idiom brings the exceptional case to the top of the function and dispatches with it immediately. On the other hand, it also makes the normal case the main focus of the function, unindented and clear to the eye. It may look like this idiom violates the "Save Exceptions For Later" pattern, but code of this sort can be a natural outgrowth of following the pattern. First, we implement the function to do its normal business and makes sure that it handles all of the usual cases. Only then do we concern ourselves with the exceptional case, and we build it into the function with minimal disruption to the code.// a bunch // of lines // of code // processing x
This pattern has served me well over the years, far beyond Smalltalk.
I've been meaning to blog about my compilers course for more than a month, but life -- including my compilers course -- have kept me busy. Here are three quick notes to prime the pump.
In her post, Kuper said that her first compiler course was "a lot of hard work" but "the most fun I'd ever had writing code". I always tell my students that this course will be just like that for them. They are more likely to believe the first claim than the second. Diving in, I'm remembering those feelings firsthand. I think my students will be glad that I dove in. I'm reliving some of the challenges of doing everything that I ask them to do. This is already generating a new source of empathy for my students, which will probably be good for them come grading time.
This graph illustrates one of the problems that afflicts me as a writer. Too often, I don't have the confidence (or gumption) to start writing until I reach the X. By that time in the learning cycle, downhill momentum is significant. It's easier not to write, either because I figure what I have to say is old news or because my mind has moved on to another topic.
I am thankful that other people share their learning at the top of the curve.
~~~~
Sarah Perry. created the above image for one of her many fine essays. I came upon it in David Chapman's Ignorant, Irrelevant, and Inscrutable. The blue X is mine.
I have finally started reading Mindstorms. I hope to write short reflections as I complete every few pages or anytime I come across something I feel compelled to write about in the moment. This is the first entry in the imagined series.
In the introduction, Papert says:
We shall see again and again that the consequences of mathophobia go far beyond obstructing the learning of mathematics and science. The interact with other endemic "cultural toxins", for example, with popular theories of aptitudes, to contaminate peoples' images of themselves as learners. Difficulty with school math is often the first step of an invasive intellectual process that leads us all to define ourselves as bundles of aptitudes and ineptitudes, as being "mathematical" or "not mathematical", "artistic" or "not artistic", "musical" or "not musical", "profound" or "superficial", "intelligent" or "dumb". Thus deficiency becomes identity, and learning is transformed from the early child's free exploration of the world to a chore beset by insecurities and self-imposed restrictions.
This invasive intellectual process has often deeply affected potential computer science students long before they reach the university. I would love to see Papert's dream made real early enough that young people can imagine being a computer scientist earlier. It's hard to throw of the shackles after they take hold.
The thing that sticks out as I read the first few pages of Mindstorms is its focus on the power of affect in learning. I don't recall conscious attention to my affect having much of a role in my education; it seems I was in a continual state of "cool, I get to learn something". I didn't realize at the time just what good fortune it was to have that as a default orientation.
I'm also struck by Papert's focus on the role of physicality in learning, how we often learn best when the knowledge has a concrete manifestation in our world. I'll have to think about this more... Looking back now, abstraction always seemed natural to me.
Papert's talk of love -- falling in love with the thing we learn about, but also with the thing we use to learn it -- doesn't surprise me. I know these feelings well, even from the earliest experiences I had in kindergarten.
An outside connection that I will revisit: Frank Oppenheimer's exploratorium, an aspiration I learned about from Alan Kay. What would a computational exploratorium look like?
This bullet point from @jessitron's Hyperproductive Development really connected with me:
As the host symbiont who lives and breathes the system: strike the words "just", "easy", "obvious", "simple", and "straightforward" from your vocabulary. These words are contextual, and no other human shares your context.
My first experience coming to grips with my use of these words was not in software development, but in the classroom. "Obvious" has never been a big part of my vocabulary, but I started to notice a few years ago how often I said "just", "easy", and "simple" in class and wrote them in my lecture notes. Since then, I have worked hard to cut back sharply on my uses of these minimizers in both spoken and written interactions with my students. I am not always successful, of course, but I am more aware now and frequently catch myself before speaking, or in the act of writing.
I find that I still use "straightforward" quite often these days. Often, I use it to express contrast explicitly, something to the effect, "This won't necessarily be easy, but at least it's straightforward." By this I mean that some problem or task may require hard work, but at least the steps they need to perform should be clear. I wonder now, though, whether students always take it this way, even when expressed explicitly. Maybe they hear me minimizing the task head, not putting the challenge they face into context.
Used habitually, even with good intentions, a word like "straightforward" can become a crutch, a substitute minimizer. It lets me to be lazy when I try to summarize a process or to encourage students when things get difficult. I'm going to try this fall to be more sensitive to my use of "straightforward" and see if I can't find a better way in most situations.
As for the blog post that prompted this reflection, Hyperproductive Development summarizes as effectively as anything I've read the truth behind the idea that some programmers are so much more effective than others: "it isn't the developers, so much as the situation". It's a good piece, well worth a quick read.
In his conversation with Tyler Cowen, Ben Sasse talks a bit about how students learn in our schools of public policy, business, and law:
We haven't figured out in most professional schools how to create apprenticeship models where you cycle through different aspects of what doing this kind of work will actually look like. There are ways that there are tighter feedback loops at a med school than there are going to be at a policy school. There are things that I don't think we've thought nearly enough about ways that professional school models should diverge from traditional, theoretical, academic disciplines or humanities, for example.
We see a similar continuum in what works best, and what is needed, for learning computer science and learning software engineering. Computer science education can benefit from the tighter feedback loops and such that apprenticeship provides, but it also has a substantial theoretical component that is suitable for classroom instruction. Learning to be a software engineer requires a shift to the other end of the continuum: we can learn important things, in the classroom, but much of the important the learning happens in the trenches, making things and getting feedback.
A few universities have made big moves in how they structure software engineering instruction, but most have taken only halting steps. They are often held back by a institutional loyalty to the traditional academic model, or out of sheer curricular habit.
The one place you see apprenticeship models in CS is, of course, graduate school. Students who enter research work in the lab under the mentorship of faculty advisors and more senior grad students. It took me a year or so in graduate school to figure out that I needed to begin to place more focus on my research ideas than on my classes. (I hadn't committed to a lab or an advisor yet.)
In lieu of a changed academic model, internships of the sort I mentioned recently can be really helpful for undergrad CS students looking to go into software development. Internships create a weird tension for faculty... Most students come back from the workplace with a new appreciation for the academic knowledge they learn in the classroom, which is good, but they also back to wonder why more of their schoolwork can't have the character of learning in the trenches. They know to want more!
Project-based courses are a way for us to bring the value of apprenticeship to the undergraduate classroom. I am looking forward to building compilers with ten hardy students this fall.
In Leadership as a Performing Art, Ed Batista discusses, among other things, a "naturalness bias" that humans have when evaluating one another. Naturalness is "a preference for abilities and talents that we perceive as innate over those that appear to derive from effort and experience". Even when people express a preference for hard work and experience, they tend to judge more positively people who seem to be operating on natural skill and talent. As Batista notes, this bias affects not only how we evaluate others but also how we evaluate ourselves.
As I read this article, I could not help but think about how students who are new to programming and to computer science often react to their own struggles in an introductory CS course. These thoughts reached a crescendo when I came to these words:
One commonly-held perspective is that our authentic self is something that exists fully formed within us, and we discover its nature through experiences that feel more (or less) natural to us. We equate authenticity with comfort, and so if something makes us feel uncomfortable or self-conscious, then it is de facto inauthentic, which means we need not persist at it (or are relieved of our responsibility to try). But an alternative view is that our authentic self is something that we create over time, and we play an active role in its development through experiences that may feel uncomfortable or unnatural, particularly at first. As INSEAD professor of organizational behavior Herminia Ibarra wrote in The Authenticity Paradox in 2015,
Because going against our natural inclinations can make us feel like impostors, we tend to latch on to authenticity as an excuse for sticking with what's comfortable... By viewing ourselves as works-in-progress and evolving our professional identities through trial and error, we can develop a personal style that feels right to us and suits our organizations' changing needs. That takes courage, because learning, by definition, starts with unnatural and often superficial behaviors that can make us feel calculating instead of genuine and spontaneous. But the only way to avoid being pigeonholed and ultimately become better leaders is to do the things that a rigidly authentic sense of self would keep us from doing.
So many CS students and even computing professionals report suffering from impostor syndrome, sometimes precisely because they compare their internal struggles to learn with what appears to be the natural ability of their colleagues. But, as Ibarra says, learning, by definition, starts with the unnatural. To be uncomfortable is, in one sense, to be in a position to learn.
How might we teachers of computer science help our students overcome the naturalness bias they unwittingly apply when evaluating their own work and progress? We need strategies to help students see that CS is something we do, not something we are. You can feel uncomfortable and still be authentic.
This distinction is at the foundation of Batista's advice to leaders and, I think, at the foundation of good advice to students. When students can distinguish between their behavior and their identity, they are able to manage more effectively the expectations they have of their own work.
I hope to put what I learned in this article to good use both for my students and myself. It might help me be more honest -- and generous -- to myself when evaluating my performance as a teacher and an administrator, and more deliberate in how I try to get better.
Last Thursday, I spent a day visiting a major IT employer in our state. Their summer interns, at least three of whom are students in my department, were presenting projects they had developed during a three-day codejam. The company invited educators from local universities to come in for the presentations, preceded by a tour of the corporate campus, a meeting with devs who had gone through the internship program in recent years, and a conversation about how the schools and company might collaborate more effectively. Here are a few of my impressions from the visit.
I saw and heard the word "agile" everywhere. The biggest effects of the agility company-wide seemed to be in setting priorities and in providing transparency. The vocabulary consisted mostly of terms from Scrum and kanban. I started to wonder how much the programming practices of XP or other agile methodologies had affected software development practices there. Eventually I heard about the importance of pair programming and unit testing and was happy to know that the developers hadn't been forgotten in the move to agile methods.
Several ideas came to mind during the visit of things we might incorporate into our programs or emphasize more. We do a pretty good job right now, I think. We currently introduce students to agile development extensively in our software engineering course, and we have a dedicated course on software verification and validation. I have even taught a dedicated course on agile software development several times before, most recently in 2014 and 2010. Things we might do better include:
Over the course of the day, I heard about many of the attributes this company likes to see in candidates for internships and full-time positions, among them:
The codejam presentations themselves were quite impressive. Teams of three to six college students can do some amazing things in three days when they are engaged and when they have the right tools available to them. One theme of the codejam was "platform as a service", and students used a slew of platforms, tools, and libraries to build their apps. Ones that stood out because they were new to me included IBM BlueMix (a l´ AWS and Azure), Twilio ("a cloud platform for building SMS, voice and messaging apps"), and Flask ("a micro web framework written in Python"). I also saw a lot of node.js and lots and lots of NoSQL. There was perhaps a bias toward NoSQL in the tools that the interns wanted to learn, but I wonder if students are losing appreciation for relational DBs and their value.
Each team gave itself a name. This was probably my favorite:
int erns;I am a programmer.
All in tools, the interns used too many different tools for me to take note of. That was an important reminder from the day for me. There are so many technologies to learn and know how to use effectively. Our courses can't possibly include them all. We need to help students learn how to approach a new library or framework and become effective users as quickly as possible. And we need to have them using source control all the time, as ingrained habit.
One last note, if only because it made me smile. Our conversation with some of the company's developers was really interesting. At the end of the session, one of the devs handed out his business card, in case we ever wanted to ask him questions after leaving. I looked down at the card and saw...
... Alan Kay. Who knew that Alan was moonlighting as an application developer for a major financial services company in the Midwest? I'm not sure whether sharing a name with a titan of computer science is a blessing or a curse, but for the first time in a long while I enjoyed tucking a business card into my pocket.
This morning, I tweeted a quote from Sherry Turkle's Remembering Seymour Papert that struck a chord with a few people: "Seymour Papert saw that the computer would make it easier for thinking itself to become an object of thought." Here is another passage that struck a chord with me:
At the time of the juggling lesson, Seymour was deep in his experiments into what he called 'loud thinking'. It was what he was asking my grandfather to do. What are you trying? What are you feeling? What does it remind you of? If you want to think about thinking and the real process of learning, try to catch yourself in the act of learning. Say what comes to mind. And don't censor yourself. If this sounds like free association in psychoanalysis, it is. (When I met Seymour, he was in analysis with Greta Bibring.) And if it sounds like it could you get you into personal, uncharted, maybe scary terrain, it could. But anxiety and ambivalence are part of learning as well. If not voiced, they block learning.
It occurred to me that I blog as a form of "loud thinking". I don't write many formal essays or finished pieces for my blog these days. Mostly I share thoughts as they happen and think out loud about them in writing. Usually, it's just me trying to make sense of ideas that cross my path and see where they fit in with the other things I'm learning. I find that helpful, and readers sometimes help me by sharing their own thoughts and ideas.
When I first read the phrase "loud thinking", it felt awkward, but it's already growing on me. Maybe I'll try to get my compiler students to do some loud thinking this fall.
By the way, Turkle's entire piece is touching and insightful. I really liked the way she evoked Papert's belief that we "love the objects we think with" and "think with the objects we love". (And not just because I'm an old Smalltalk programmer!) I'll let you read the rest of the piece yourself to appreciate both the notion and Turkle's storytelling.
Now, for a closing confession: I have never read Mindstorms. I've read so much about Papert and his ideas over the years, but the book has never made it to the top of my stack. I pledge to correct this egregious personal shortcoming and read it as soon as I finish the novel on my nightstand. Maybe I'll think out loud about it here soon.
I just finished reading Tyler Cowen's recent interview with historian Jill Lepore. When Cowen asks Lepore about E.B. White's classic Stuart Little, Lepore launches into a story that illustrates quite nicely what it's like to be a scholar.
First, she notes that she was writing a review of a history of children's literature and kept coming across throwaway lines of the sort "Stuart Little, published in 1945, was of course banned." This triggered the scholar's impulse:
And there's no footnote, no explanation, no nothing.
At the time, one of my kids was six, and he was reading Stuart Little, we were reading at night together, and I was like, "Wait, the story about the mouse who drives the little car and rides a sailboat across the pond in Central Park, that was a banned book? What do I not know about 1945 or this book? What am I missing?"
These last two sentences embody the scholar's orientation. "What don't I know about these two things I think I know well?"
And I was shocked. I really was shocked. And I was staggered that these histories of children's literature couldn't even identify the story. I got really interested in that question, and I did what I do when I get a little too curious about something, is I become obsessive about finding out everything that could possibly be found out.
Next comes obsession. Lepore then tells a short version of the story that became her investigative article for The New Yorker, which she wrote because sometimes I "just fall into a hole in the ground, and I can't get out until I have gotten to the very, very bottom of it."
Finally, three transcript pages later, Lepore says:
It was one of the most fun research benders I've ever been on.
It ends in fun.
You may be a scholar if you have this pattern. To me, one of the biggest downsides of becoming department head is having less time to fall down some unexpected hole and follow its questions until I reach the bottom. I miss that freedom.
![]() |
Over the last couple of weeks, I have spent a few minutes most afternoons writing a little code. It's been the best part of my work day. The project was a little compiler.
One of the first things we do in my compiler course is to study a small compiler for a couple of the days. This is a nice way to introduce the stages of a compiler and to raise some of the questions that we'll be answering over the course of the semester. It also gives students a chance to see the insides of a working compiler before they write their own. I hope that this demystifies the process a little: "Hey, a compiler really is just a program. Maybe I can write one myself."
For the last decade or so, I have used a compiler called acdc for this demo, based on Chapter 2 of Crafting A Compiler by Fischer, Cytron, and LeBlanc. ac is a small arithmetic language with two types of numbers, sequences of assignment statements, and print statements. dc is a stack-based desk calculator that comes as part of many Unix installations. I whipped up a acdc compiler in Java about a decade ago and have used it ever since. Both languages have enough features to be useful as a demo but not enough to overwhelm. My hacked-up compiler is also open to improvements as we learn techniques throughout the course, giving us a chance to use them in the small before students applied them to their own project.
I've been growing dissatisfied with this demo for a while now. My Java program feels heavy, with too many small pieces to be simple enough for a quick read. It requires two full class sessions to really understand it well, and I've been hoping to shorten the intro to my course. ac is good, but it doesn't have any flow control other than sequencing, which means that it does not give us a way to look at assembly language generation with jumps and backpatching. On top of all that, I was bored with acdc; ten years is a long time to spend with one program.
This spring I stumbled on a possible replacement in The Fastest FizzBuzz in the West. It defines a simple source language for writing FizzBuzz programs declaratively. For example:
1...150 fizz=3 buzz=5 woof=7produces the output of a much larger program in other languages. Imagine being able to pull this language during your next interview for a software dev position!
This language is really simple, which means that a compiler for it can be written in relatively few lines of code. However, it also requires generating code with a loop and if-statements, which requires thinking about branching patterns in assembly language.
The "Fastest FizzBuzz" article uses a Python parser generator to create its compiler. For my course, I want something that my students can read with only their knowledge coming into the course, and I want the program to be transparent enough so that they can see directly how each stage works and how it interacts with other parts of the compiler.
I was also itching to write a program, so I did.
I wrote my compiler in Python. It performs a simple scan of the source program, taking as much advantage of the small set of simple tokens as possible. The parser works by recursive descent, which also takes advantage of the language's simple structure. The type checker makes sure the numbers all make sense and that the words are unique. Finally, to make things even simpler, the code generator produces an executable Python 3.4 program.
I'm quite hopeful about this compiler's use as a class demo. It is simple enough to read in one sitting, even by students who enter the course with weaker programming skills. Even so, the language can also be used to demonstrate the more sophisticated techniques we learn throughout the course. Consider:
We'll see how well this plays in class in a couple of months. In any case, I had great fun ending my days the last two weeks by firing up emacs or IDLE and writing code. As a bonus, I used this exercise to improve my git skills, taking them beyond the small set of commands I have used on my academic projects in the past. (git rebase -i is almost my friend now.) I also wrote more pyunit tests than I have written in a long, long time, which reminded me of some of the challenges students face when trying to test their code. That should serve me well in the fall, too.
I do like writing code.
In The Sweet Bees [paywalled], Sue Hubbell writes:
Beekeepers are an opinionated lot, each sure that his methods, and his methods alone, are the proper ones. When I first began keeping bees, the diversity of passionately held opinion bewildered me, but now that I have hives in locations scattered over a thousand-square-mile area I think I understand it.... Frosts come earlier in some places than in others. Spring comes later. Rainfall is not the same. The soils, and the flowering plants they support, are unlike. Through the years, I have learned that as a result of all these variations I must keep the bees variously. Most people who keep bees have only a few hives, and have them all in one place. They find it difficult to understand why practices that have proved successful for them do not work for others. But I have learned that I must treat the bees in one yard quite differently from the way I do those even thirty miles away. The thing to do, I have discovered, is to learn from the bees themselves.
Even though I've taught at only two universities, I've learned this lesson over the years in many ways that don't require physical distance. Teaching novices in an intro course is different from teaching seniors. Teaching a programming course is different from teaching discrete structures or theory of computation. Teaching AI is different from teaching operating systems. I have learned that I must teach differently in different kinds of courses.
In an instructional setting, even more important are the bees themselves. I've been teaching Programming Languages every spring for the last years, and each group of students has been a little different. The course goes better when I have -- and take -- the time to make adjustments according to what I learn over the course of the semester about the particular set of students I have. This spring, I did not recognize the need to adapt quickly enough, and I feel like I let some of the students down.
You sometimes hear faculty talk about students "back in the old days". One thing is certain: the students we had then probably were different from the students we have now. But they were also different from the students that came before them. Each group is new, made up of individuals with their own backgrounds and their own goals.
It's nice when students are similar enough to what we expect that we can take advantage of what worked well last time. We just can't count on that happening all that often. Our job is to teach the students in class right now.
(I first encountered Hubbell's article in To Teach, by William Ayers. I gave a short review of it in yesterday's post.)
Math phobia is a common affliction of [K-12] teachers, and something that must be resisted if strong and able students are our goal.
Our own confusion about math can be an important aid in our teaching, if we take it seriously.
That second sentence applies to more than math, and more than K-12.
These come from To Teach, by William Ayers. This book is about teaching in K-12 schools, especially younger grades, with no particular focus on math or any other subject. These sentences come from a chapter on "liberating the curriculum", in which Ayers talks about specific issues in teaching reading, math, science, and social studies. Otherwise, the book is about ways of thinking about learning that respect the individual and break down artificial boundaries between areas of knowledge.
Teaching at a university is different, of course, because we are working with older, more mature students. They are more capable of directing their own learning and are, at least in my discipline, usually taking courses as part of a major they themselves have chosen. You would think that engagement and motivation would take on much different forms in the college setting.
However, I think that most of what Ayers says applies quite well to teaching college. This is especially true when students come to us hamstrung by a K-12 education such that they cannot, or at least do not as a matter of habit, take control of their learning. But I think his advice is true of good teaching anywhere, at least in spirit:
One of my favorite lines from the book is You can learn everything from anything. Start with any topic in the CS curriculum, or any project that someone wants to build, and you will eventually touch every part of the field. I think we could do some interesting things with the CS curriculum by focusing courses on projects instead of topic areas. I love how Ayers suggests bringing this mindset even to kindergarten students.
Ayers's book is a thin volume, the sort I like, with good stories and thought-provoking claims about how we approach students and schools. Eugene sez: two thumbs up.
Earlier this month, James Iry tweeted:
Every CS degree covers fancy data structures. But what trips up more programmers? Times. Dates. Floats. Non-English text. Currencies.
I would like to add names to Iry's list. As a person who goes by his middle name and whose full legal name includes a suffix, I've seen my name mangled over the years in ways you might not imagine -- even by a couple of computing-related organizations that shall remain nameless. (Ha!) And my name presents only a scant few of the challenges available when we consider all the different naming conventions around the world.
This topic would make a great course for undergrads. We could call it "Humble Data Types" or "Mundane Data Types". My friends who program for a living know that these are practical data types, the ones that show up in almost all software and which consume an inordinate amount of time. That's why we see pages on the web about "falsehoods programmers believe" about time, names, and addresses -- another one for our list!
It might be hard to sell this course to faculty. They are notoriously reluctant to add new courses to the curriculum. (What would it displace?) Such conservatism is well-founded in a discipline that moves quickly through ideas, but this is a topic that has been vexing programmers for decades.
It would also be hard to sell the course to students, because it looks a little, well, mundane. I do recall a May term class a few years ago in which a couple of programmers spent days fighting with dates and times in Ruby while building a small accounting system. That certainly created an itch, but I'm not sure most students have enough experience with such practical problems before they graduate.
Maybe we could offer the course as continuing education for programmers out in the field. They are the ones who would appreciate it the most.
In his commonplace book A Certain World, W.H. Auden quotes C.S. Lewis on the controversial nature of tramslation:
[T]ranslation, by its very nature, is a continuous implicit commentary. It can become less tendentious only by becoming less of a translation.
Lewis was merely acknowledging a truth about language: Translators must have a point of view, and often that point of view will be controversial.
I once saw Kurt Vonnegut speak with a foreign language class here many years ago. One of the students asked him what he thought about the quality of the translations done for his book. Vonnegut laughed and said that his books were so peculiar and so steeped in Americana that translating one was akin to writing a new book. He said that his translators deserved all the royalties from the books they created by translating him. They had to write brand new works.
These memories came to mind again recently while I was reading Tyler Cowen's conversation with Jhumpa Lahiri, especially when Lahiri said this:
At one point I was talking about this idea, in antiquity: in Latin, the word for "translator" is "interpreter". I teach translation now, and I talk a lot to my students about translation being the most intimate form of reading and how there was the time when translating and interpreting and analyzing were all one thing.
As my mind usually does, it began to think about computer programs.
Like many programmers, I often find myself porting a program from one language to another. This is clearly translation but, as Vonnegut and and Lahiri tell us, it is also a form of interpretation. To port a piece of code, I have to understand its meaning and express that meaning in a new language. That language has its own constructs, idioms, patterns, and set of community practices and expectations. To port a program, one must have a point of view, so the process can be, to use Lewis's word, tendentious.
I often refactor code, too, both my own programs and programs written by others. This, too, is a form of translation, even though it leaves the new code written in the same language as the original. Refactoring is necessarily an opinionated act, and thus tendentious.
Occasionally, I refactor a program in order to learn what it does and how it does it. In those cases, I'm not judging the original code as anything but ill-suited to my current state of knowledge. Even so, when I get done, I usually like my version better, if only a little bit. It expresses what I learned in the process of rewriting the code.
It has always been hard for me to port a program without refactoring it, and now I understand why. Both activities are a kind of translation, and translation is by its nature an activity that requires a point of view.
This fall, I will again teach our "Translation of Programming Languages" course. Writing a compiler requires one to become intimate not only with specific programs, the behavior of which the compiler must preserve, but also the language itself. At the end of the project, my students know the grammar, syntax, and semantics of our source language in a close, almost personal way. The target language, too. I don't mind if my students develop a strong point of view, even a controversial one, along the way. (I'm actually disappointed if the stronger students do not!) That's a part of writing new software, too.
A few years ago, someone asked MathOverflow, "Why do so many textbooks have so much technical detail and so little enlightenment?" Some CS textbooks suffer from this problem, too, though these days the more likely case is that the book tries to provide too much context. They try to motivate so much that they drown the reader in a lot of unhelpful words.
The top answer on MathOverflow (via Brian Marick) points out that the real problem does not usually lie in the lack of motivation or context provided by textbooks. The real goal is to learn how to do math, not "know" it. That is even more true of software developent. A textbook can't really teach to write programs; most of that is learned through doing itself. Perhaps the best purpose that the text can serve, says the answer, is to show the reader what he or she needs to learn. From there, the reader must go off and practice.
How does learning occur from there?
Based on my own experience as both a student and a teacher, I have come to the conclusion that the best way to learn is through "guided struggle". You have to do the work yourself, but you need someone else there to either help you over obstacles you can't get around despite a lot of effort or provide you with some critical knowledge (usually the right perspective but sometimes a clever trick) you are missing. Without the prior effort by the student, the knowledge supplied by a teacher has much less impact.
Some college CS students seem to understand this, or perhaps they simply get lucky because they are motivated to program for some other reason. They go off and try to do something using the knowledge they have. Then they come to class, or to the prof's office hours, to ask questions about what does not go smoothly. Students who skip the practice and hope that lectures will soak into them like a magic balm generally find that they don't know much when they attempt problems after class.
The MathOverflow answer matches up pretty well with my experience. Teachers and written material can have strong positive effect on learning, but they are most effective once the student has engaged with the material by trying to solve problems or write code. The teacher's job then has two parts: First, create conditions in which students can work productively. Second, pay close attention to what students are doing, diagnose misconceptions and mistakes, and help students get back onto a productive path by pointing out missing practices or bits of knowledge.
All of this reminds me of some of mymore effective class sessions teaching novice programmers, using design patterns. A typical session looks something like this:
This is a guided struggle in the small. Students then go off to write a larger program that lets them struggle a bit more, and we discuss whatever gets in their way.
A final note... One of the comments on the answer points out that a good lecture can "do" math (or CS), rather than "tell", and that such lectures can be quite effective. I agree, but in my experience this is one of the hardest skills for a professor to develop. Once I have solved a problem, it is quite difficult for me to make it look to my students as if I am solving it anew in class. The more ingrained the skill, the harder it is for me to lecture about it in a way that is more helpful than telling a story. Such stories are an essential tool in the teacher's toolbox, but their lies more in motivating students than in teaching them how to write programs. Students still have to go off and do the hard work themselves. The teacher's job is to guide them through their struggles.
In an essay in The Guardian, writer George Saunders reflects on having written his first novel after many years writing shorter fiction. To a first approximation, he found the two experiences to be quite similar. In particular,
What does an artist do, mostly? She tweaks that which she's already done.
I read this on a day when I had just graded thirty-plus programming assignments from my junior/senior-level Programming Languages courses, and this made me think of student programmers. My first thought was snarky, and only partly tongue-in-cheek: Students write and then submit. Who has the time or interest to tweak?
My conscience quickly got the better of me, and I admitted that this was unfair. In a weak moment at the end of a long day, it's easy to be dismissive and not think about students as people who face all sorts of pressures both in and out of the classroom. Never forget Hanlon's Razor, my favorite formulation of which is:
Never attribute to malice or stupidity that which can be explained by moderately rational individuals following incentives in a complex system of interactions.
Even allowing the snark, my first thought was inaccurate. The code students submit is often the end result of laborious tweaking. The thing is, most students tweak only while the code gives incorrect answers. In the worst case, some students tweak and tweak, employing an age-old but highly inefficient software development methodology: Make another change and see if it works.
This realization brought to mind Kent Beck's Rules of Simple Design:
Most students are under time pressures that make artistry a luxury good; they are happy to find time to make their code work at all. If the code passes the tests, it's probably good enough for now.
But there is more to the student's willingness to stop tinkering so soon than just external pressures. It takes a lot of programming experience and a fair amount of time to come to even appreciate Rules 2 through 4. Why does it matter if code reveals the programmer's intention, in terms of either art or engineering? What's the big deal about a little duplication? The fewest elements? -- making that happen takes time that could be spent on something much more interesting.
I am coming to think of Kent's rules as a sort of Bloom's taxonomy for the development of programming expertise. Students start at Level 1, happy to write code that achieves its stated purpose. As they grow, programmers move through the rules, mastering deeper levels of understanding of design, simplicity, and, yes, artistry. They don't move through the stages in a purely linear fashion, but they do tend to master the rules in roughly the order listed above.
Today is a day of empathy for my novice programmers. As I write this, they are writing the final exam in my course. I hope that in a few weeks, after the blur of a busy semester settles in their minds, they reflect a bit and see that they have made progress as programmers -- and that they can now ask better questions about the programming languages they use than they could at the beginning of the course.
The last sentence of each of these passages reminds me of some of the students over the years.
First this, from Paul Callaghan's The Word Chain Kata:
One common way to split problems is via the "generate and test" pattern, where one component suggests results and the other one discards invalid ones. (In my experience, undergrad programmers are experts in this pattern, although they tend to use it as a [software development] methodology--but that's another story.)
When some students learn to program for the first time, they start out by producing code that looks like something they have seen before, trying it out, and then tinkering with it until it works or until they become exasperated. (I always hope that they act on their exasperation by asking me for help, rather than by giving up.) These students usually understand little bits of the code locally, but they don't really understand the program or function as a whole. Yet, somehow, they find a way to make it work.
It's surprising how far some students can get in a course of study by programming this way. (That's why Callaghan calling the approach a methodology made me smile.) It's unfortunate, too, because eventually the approach hits a wall when problems and domains become more challenging. Or when they run into a course where they program in Racket, in which one misplaced parenthesis can cause an otherwise perfect piece of code to implode. Lisp-like languages do not provide a supportive environment for this kind of "programming by wandering around".
And then there's this, from Andy Hertzfeld's fun little story about the writing of the classic manual Inside Macintosh:
Pretty soon, I figured out that if Caroline had trouble understanding something, it probably meant that the design was flawed. On a number of occasions, I told her to come back tomorrow after she asked a penetrating question, and revised the API to fix the flaw that she had pointed out. I began to imagine her questions when I was coding something new, which made me work harder to get things clearer before I went over them with her.
In this story, Caroline is not a student, but a young new writer assigned to the Mac documentation team. Still, she reminds me of students who are as delightful to work with as generate-and-test programmers can be frustrating. These students pay attention. They ask good questions, ones that often challenge the unstated assumptions underlying what we have explained before. At first, this can seem frustrating to us teachers, because we have to formulate answers for things that should be obvious. But that's the point: they aren't obvious, at least not to everyone, and us thinking they are obvious is inhibiting our teaching.
Last semester, I had one team of students in my compilers class that embodied this attitude. They asked questions no one had ever bothered to ask me before. At first, I thought, "How can these guys not understand such basic material?" Like Hertzfeld, though, pretty soon I figured out that their questions were exposing holes or weaknesses in my lectures, examples, and sample code. I began to anticipate their questions as I prepared for class. Their questions helped me see ways to make my course better.
As along so many other dimensions, part of the challenge in teaching CS is the wide variation in the way students study, learn, and approach their courses. It is also a part of the great fun of teaching, especially when you encounter the Carolines and Averys who push me to get better.
"This weekend I enjoyed Peter Hessler's interview of McPhee in The Paris Review, John McPhee, The Art of Nonfiction No. 3."
That's a direct quote from this blog. Don't remember it? I don't blame you; neither do I. I do remember blogging about McPhee back when, but as I read the same Paris Review piece again last Sunday and this, I had no recollection of reading it before, no sense of déjà vu at all.
Sometimes having a memory like mine is a blessing: I occasionally get to read something for the first time again. If you read my blog, then you get to read my first impressions for a second time.
I like this story that McPhee told about Bob Bingham, his editor at The New Yorker:
Bingham had been a writer-reporter at The Reporter magazine. So he comes to work at The New Yorker, to be a fact editor. Within the first two years there, he goes out to lunch with his old high-school friend Gore Vidal. And Gore says, What are you doing as an editor, Bobby? What happened to Bob Bingham the writer? And Bingham says, Well, I decided that I would rather be a first-rate editor than a second-rate writer. And Gore Vidal draws himself up and says, And what is wrong with a second-rate writer?
I can just hear the faux indignation in Vidal's voice.
McPhee talked a bit about his struggle over several years to write a series of books on geology, which had grown out of an idea for a one-shot "Talk of the Town" entry. The interviewer asked him if he ever thought about abandoning the topic and moving on to something he might enjoy more. McPhee said:
The funny thing is that you get to a certain point and you can't quit. Because I always worried: if you quit, you'll quit again. The only way out was to go forward, to learn your way and write your way out of it.
I know that feeling. Sometimes, I really do need to quit something and move on, but I always wonder whether quitting this time will make it easier to do next time. Because sometimes, I need to stick it out and, as McPhee says, learn my way out of the difficulty. I have no easy answers for knowing when quitting is the right thing to do.
Toward the end of the interview, the conversation turned to the course McPhee teaches at Princeton, once called "the literature of fact". The university first asked him to teach on short notice, over the Christmas break in 1974, and he accepted immediately. Not everyone thought it was a good idea:
One of my dear friends, an English teacher at Deerfield, told me: Do not do this. He said, Teachers are a dime a dozen -- writers aren't. But my guess is that I've been more productive as a writer since I started teaching than I would have been if I hadn't taught. In the overall crop rotation, it's a complementary job: I'm looking at other people's writing, and the pressure's not on me to do it myself. But then I go back quite fresh.
I know a lot of academics who feel this way. Then again, it's a lot easier to stay fresh in one's creative work if one has McPhee's teaching schedule, rather than a full load of courses:
My schedule is that I teach six months out of thirty-six, and good Lord, that leaves a lot of time for writing, right?
Indeed it does. Indeed it does.
On this reading of the interview, I marked only two passages that I wrote about last time. One came soon after the above response, on how interacting with students is its own reward. The other was a great line about the difference between mastering technique and having something to say: You demonstrated you know how to saddle a horse. Now go find the horse.
That said, I unconsciously channeled this line from McPhee just yesterday:
Writing teaches writing.
We had a recruitment event on campus, and I was meeting with a dozen or so prospective students and their entourages. We were talking about our curriculum, and I said a few words about our senior project courses. Students generally like these courses, even though they find them difficult. The students have never had to write a big program over the course of several months, and it's harder than it looks. The people who hire our graduates like these courses, too, because they know that these courses are places where students really begin to learn to program.
In the course of my remarks, I said something to the effect, "You can learn a lot about programming in classes where you study languages and techniques and theory, but ultimately you learn to write software by writing software. That's what the project courses are all about." There were a couple of experienced programmers in the audience, and they were all nodding their heads. They know McPhee is right.
This week, I have been enjoying Eli Bendersky's two-article series "Adventures in JIT Compilation":
Next I'll follow his suggestion and read the shorter How to JIT - An Introduction.Bendersky is a good teacher, at least in the written form, and I am picking up a lot of ideas for my courses in programming languages and compilers. I recommend his articles and his code highly.
In Part 2, Bendersky says something that made me think of my students:
One of my guiding principles through the field of programming is that before diving into the possible solutions for a problem (for example, some library for doing X) it's worth working through the problem manually first (doing X by hand, without libraries). Grinding your teeth over issues for a while is the best way to appreciate what the shrinkwrapped solution/library does for you.
The presence or absence of this attitude is one of the crucial separators among CS students. Some students come into the program with this mindset already in place, and they are often the ones who advance most quickly in the early courses. Other students don't have this mindset, either by interest or by temperament. They prefer to solve problems quickly using canned libraries and simple patterns. These students are often quite productive, but they sometimes soon hit a wall in their learning. When a student rides along the surface of what they are told in class, never digging deeper, they tend to have a shallow knowledge of how things work in their own programs. Again, this can lead to a high level of productivity, but it also produces brittle knowledge. When something changes, or the material gets more difficult, they begin to struggle. A few of the students eventually develop new habits and move nicely into the group of students who likes to grind. The ones who don't make the transition continue to struggle and begin to enjoy their courses less.
There is a rather wide variation among undergrad CS students, both in their goals and in their preferred styles or working and learning. This variation is one of the challenges facing profs who hope to reaching the full spectrum of students in their classes. And helping students to develop new attitudes toward learning and doing is always a challenge.
This comes from Laura Miller, a book reviewers and essayist for Slate, in a Poets & Writers interview:
I also believe that reading is a profoundly creative act, that every act of reading is a collaboration between author and reader. I don't understand why more people aren't interested in this alchemy. It's such an act of grace to give someone else ten or fifteen hours out of your own irreplaceable life, and allow their voice, thoughts, and imaginings into your head.
I think this is true of all reading, whether fiction or nonfiction, literary or technical. I often hear CS profs tell their students to read "actively" by trying code out in an interpreter, asking continually what the author means, and otherwise engaging with the material. Students who do have a chance to experience what Miller describes: turning over a few hours of their irreplaceable lives to someone who understands a topic well, allow their voice, thoughts, and imaginings into their heads, and coming out on the other end of the experience with new thoughts -- and maybe even a new mind.
Yesterday afternoon I listened to a story told by someone who had recently been a passenger in a small plane flown by a colleague. As they climbed to their cruising altitude, which was clear and beautiful, the plane passed through a couple thousand feet of heavy clouds. The pilot flew confidently, having both training and experience in instrument flight.
The passenger telling the story, however, was disoriented and a little scared by being in the clouds and not knowing where they were heading. The pilot kept him calm by explaining the process of "flying by instruments" and how he had learned it. Sometimes, learning something new can give us confidence. Other times, just listening to a story can distract us enough to get us through a period of fear.
This story reminded me of a session early in my programming languages course, when students are learning Racket and functional programming style. Racket is quite different from any other language they have learned. "Don't dip your toes in the water," I tell them. "Get wet."
For students who prefer their learning analogies not to involve potential drowning -- that is, after all, the sensation many of them report feeling as they learn to cope with all of Racket's parentheses for the first time -- I relate an Alan Kay story that talks about learning to fly an airplanes after already knowing how to drive a car. Imagine what the world be like if everyone refused to learn how to fly a plane because driving was so much more comfortable and didn't force them to bend their minds a bit? Sure, cars are great and serve us well, but planes completely change the world by bringing us all closer together.
I have lost track of where I had heard or read Kay telling that story, so when I wrote up the class notes, I went looking for a URL to cite. I never found one, but while searching I ran across a different use of airplanes in an analogy that I have since worked into my class. Here's the paragraph I use in my class notes, the paragraph I thought of while listening to the flying-by-instruments story yesterday:
The truth is that bicycles and motorcycles operate quite differently than wheeled vehicles that keep three or more wheels on the ground. For one thing, you steer by leaning, not with the handlebars or steering wheel. Learning to fly an airplane gives even stronger examples of having to learn that your instincts are wrong, and that you have to train yourself to "instinctively" know not only that you turn by banking rather than with the rudder, but that you control altitude primarily with the throttle, not the elevators, speed primarily with the elevators not the throttle, and so forth.
Learning to program in a new style, whether object-oriented, functional, or concatenative, usually requires us to overcome deeply-ingrained design instincts and to develop new instincts that feel uncomfortable while we are learning. Developing new instincts takes some getting used to, but it's worth the effort, even if we choose not to program much in the new style after we learn it.
Now I find myself thinking about what it means to "fly by instruments" when we program. Is our unit testing framework one of the instruments we come to rely on? What about a code smell detector such as Reek? If you have thoughts on this idea, or pointers to what others have already written, I would love to hear from you.
Postscript. I originally found the passage quoted above in a long-ish article about Ruby and Smalltalk, but that link has been dead for a while. I see that the article was reposted in a Ruby Buzz Forum message called On Ceremony and Training Wheels.
Oh, and if you know where I can find the Alan Kay story I went looking for online, I will greatly appreciate any pointers you can offer!
Over the last couple of days, a thread on the SIGCSE mailing list has been revisiting the well-tilled ground of comments in code. As I told my students at the beginning of class this semester, some of my colleagues consider me a heretic for not preaching the Gospel of Comments in Code. Every context seems to have its own need for, and expectations of, comments. Wherever students end up working, both while in school and after graduation, their employers will set a standard and expect them to follow. They will figure it out.
In most of my courses, I define a few minimal standard, try to set a good example in the code I give my students, and otherwise don't worry much about comments. Part of my example is that different files I give them are commented differently, depending on the context. A demo of an idea, a library to be reused in the course, and an application are different enough that they call for different kinds of comments. In a course such as compiler development, I require more documentation, both in and out of the code. Students live with that code for a semester and come to value some of their own comments many weeks later.
Anyway, the SIGCSE thread included two ideas that I liked, though they came from competing sides of the argument. One asserted that comments are harmful, because:
They're something the human reader can see but the computer can't, and therefore are a source of misunderstanding.
I love the idea of thinking in terms of misunderstandings between humans and the computer.
The other responded to another poster's suggestion that students be encouraged to write comments with themselves in mind: What would you like to know if you open this code six months from now? The respondent pointed out that this is unreasonable: Answering that question requires...
... a skill that is at least on par with good programming skills. Certainly new CS students are unable to make this kind of decision.
The thread has been a lot of fun to read. I remain mostly of the view that:
This comment at the close of a recent Dan Meyer post struck close to home:
I haven't found a way to generate these kinds of insights about math without surrounding myself with people learning math for the first time.
I've learned a lot about programming from teaching college students. Insights can come at all levels, from working with seniors who are writing compilers as their first big project, through midstream students who are learning OOP or functional programming as a second or third style, right down to beginners who are seeing variables and loops and functions for the first time.
Sometimes an insight comes when a student asks a new question, or an old question at the right time. I had a couple of students in my compiler course last fall who occasionally asked the most basic questions, especially about code generation. Listening to their questions and creating new examples to illustrate my answers helped me think differently about the run-time system.
Other times, they come while listening to students talk among themselves. One student's answer to another student's question can trigger an entirely new way for me to think about a concept I think I understand pretty well. I don't have any recent personal examples in mind, but this sort of experience seems to be part of what triggered Meyer's post.
People are always telling us to "be the least experienced person in the room", to surround ourselves with "smarter" or more experienced people and learn from them. But there is a lot to be learned from programmers who are just starting out. College profs have that opportunity all the time, if they are willing to listen and learn.
A few quick notes on my previous post about the effect of ubiquitous information on knowing and doing.
~~~~
The post reminded a reader of something that Guy Steele said at DanFest, a 2004 festschrift in honor of Daniel Friedman's 60th birthday. As part of his keynote address, Steele read from an email message he wrote in 1978:
Sussman did me a very big favor yesterday -- he let me flounder around trying to build a certain LISP interpreter, and when I had made and fixed a critical bug he then told me that he had made (and fixed) the same mistake in Conniver. I learned a lot from that bug.
Isn't that marvelous? "I learned a lot from that bug."
Thanks to this reader for pointing me to a video of Steele's DanFest talk. You can watch this specific passage at the 12:08 mark, but really: You now have a link to an hour-long talk by Guy Steele that is titled "Dan Friedman--Cool Ideas". Watch the entire thing!
~~~~
If all you care about is doing -- getting something done -- then ubiquitous information is an amazing asset. I use Google and StackOverflow answers quite a bit myself, mostly to navigate the edges of languages that I don't use all the time. Without these resources, I would be less productive.
~~~~
Long-time readers may have read the story about how I almost named this blog something else. ("The Euphio Question" still sets my heart aflutter.) Ultimately I chose a title that emphasized the two sides of what I do as both a programmer and a teacher. The intersection of knowing and doing is where learning takes place. Separating knowing from doing creates problems.
In a post late last year, I riffed on some ideas I had as I read Learn by Painting, a New Yorker article about an experiment in university education in which everyone made art as a part of their studies.
That article included a line that expressed an interesting take on my blog's title: "Knowing and doing are two sides of the same activity, which is adapting to our environment."
That's cool thought, but a rather pedestrian sentence. The article includes another, more poetic line that fits in nicely with the theme of the last couple of days:
Knowing is better than not knowing, but knowing without doing is as good as not knowing.
If I ever adopt a new tagline for my blog, it may well be this sentence. It is not strictly true, at least in a universal sense, but it's solid advice nonetheless.
I was recently reading an old bit-player entry on computing number factoids when I ran across a paragraph that expresses an all-too-common phenomenon of the modern world:
If I had persisted in my wrestling match, would I have ultimately prevailed? I'll never know, because in this era of Google and MathOverflow and StackExchange, a spoiler lurks around every cybercorner. Before I could make any further progress, I stumbled upon pointers to the work of Ira Gessel of Brandeis, who neatly settled the matter ... more than 40 years ago, when he was an undergraduate at Harvard.
The matter in this case was recognizing whether an arbitrary n is a Fibonacci number or not, but it could be have been just about anything. If you need an answer to almost any question these days, it's already out there, right a your fingertips.
Google and StackExchange and MathOverflow are a boon for knowing, but not so much for doing. Unfortunately, doing often leads to a better kind of knowing. Jumping directly to the solution can rob us of some important learning. As Hayes reminds us in his articles, it also can also deprive us of a lot of fun.
You can still learn by doing and have a lot of fun doing it today -- if you can resist the temptation to search. After you struggle for a while and need some help, then having answers at our fingertips becomes a truly magnificent resource and can help us get over humps we could never have gotten over so quickly in even the not-the-so-distant past.
The new world puts a premium on curiosity, the desire to find answers for ourselves. It also values self-denial, the ability to delay gratification while working hard to find answer that we might be able to look up. I fear that this creates a new gap for us to worry about in our education systems. Students who are curious and capable of self-denial are a new kind of "haves". They have always had a leg up in schools, but ubiquitous information magnifies the gap.
Being curious, asking questions, and wanting to create (not just look up) answers have never been more important to learning.
I found a great story from Lubomir Kavalek in his recent column, Chess Champions and Their Queens. Many years ago, Kavalek was talking with Berry Withuis, a Dutch journalist, about Rashid Nezhmedtinov, who had played two brilliant queen sacrifices in the span of five years. The conversation reminded Withuis of a question he once asked of grandmaster David Bronstein:
"Is the Queen stronger than two light pieces?"
(The bishop and knight are minor, or "light", pieces.)
The former challenger for the world title took the question seriously. "I don't know," he said. "But I will tell you later."
That evening Bronstein played a simultaneous exhibition in Amsterdam and whenever he could, he sacrificed his Queen for two minor pieces. "Now I know," he told Withuis afterwards. "The Queen is stronger."
How is that for an empirical mind? Most chessplayers would have immediately answered "yes" to Withuis's question. But Bronstein -- one of the greatest players never to be world champion and author of perhaps the best book of tournament analysis in history -- didn't know for sure. So he ran an experiment!
We should all be so curious. And humble.
I wondered for a while if Bronstein could have improved his experiment by channeling Kent Beck's Three Bears pattern. (I'm a big fan of this learning technique and mention it occasionally here, most recently last summer.) This would require him to play many games from the other side of the sacrifice as well, with a queen against his opponents' two minor pieces. Then I realized that he would have a hard time convincing any of his opponents to sacrifice their queens so readily! This may be the sort of experiment that you can only conduct from one side, though in the era of chess computers we could perhaps find, or configure, willing collaborators.
As I settle into a new semester of teaching students functional programming and programming languages, I find myself again in the role of grader of, and commenter, on code. This passage from Tobias Wolff in Paris Review interview serves as a guide for me:
Now, did [Pound] teach Eliot to write? No. But he did help him see that there were more notes to be played he was playing. That is the kind of thing I hope to do. And to counsel patience -- the beauty of patience, which is not a virtue of the young.
Students often think that learning to program is all about the correctness of their code. Correctness matters, but there's a lot more. Knowing what is possible and learning to be patient as they learn often matter more than mere correctness. For some students, it seems, those lessons must begin before more technical habits can take hold.
On the racket-users mailing list yesterday, Matthias Felleisen issued "a research challenge that is common in the Racket world":
If you are here and you see the blueprints for paradise over there, don't just build paradise. Also build the bridge from here to there.
This is one of the things I love about Racket. And I don't use even 1% of the goodness that is Racket and its ecosystem.
Over the last couple of years, I have been migrating my Programming Languages course from a Scheme subset of Racket to Racket itself. Sometimes, this is simply a matter of talking about Racket, not Scheme. Others, it means using some of the data structures, functions, and tools Racket provides rather than doing without or building our own. Occasionally, this shift requires changing something I do in class, because Racket is fussier than Scheme in some regards. That's usually a good thing, because the change makes Racket a better language for engineering big programs. In general, though, the shift goes smoothly.
Occasionally, the only challenge is a personal one. For example, I decided to use first and rest this semester when working with lists, instead of car and cdr. This should make some students' lives better. Learning a new language and a new style and new IDE all at once can be tough for students with only a couple of semesters' programming experience, and using words that mean what they say eliminates one unnecessary barrier. But, as I tweeted, I don't feel whole or entirely clean when I do so. As my college humanities prof taught me through Greek tragedies, old habits die hard, if at all.
One of my goals for the course this semester is to have the course serve as a better introduction to Racket for students who might be inclined to take advantage of its utility and power in later courses, or who simply want to enjoy working in a beautiful language. I always seem to have a few who do, but it might be nice if even more left the course thinking of Racket as a real alternative for their project work. We'll see how it goes.
Coming into the semester, I didn't have any students doing their undergraduate research under my supervision. That frees up some time each week, which is nice, but leaves my semester a bit poorer. Working with students one-on-one is one of the best parts of this job, even more so in relief against administrative duties. Working on these projects makes my weeks better, even when I don't have as much time to devote to them as I'd like.
Yesterday, a student walked in with a project that makes my semester a little busier -- and much more interesting. Last summer, he implemented some ideas on extensible effects in Haskell and has some ideas for ways to make the system more efficient.
This student knows a lot more about extensible effects and Haskell than I do, so I have some work to do just to get ready to help. I'll start with Extensible Effects: An Alternative to Monad Transformers, the paper by Oleg Kiselyov and his colleagues that introduced the idea to the wider computing community. This paper builds on work by Cartwright and Felleisen, published over twenty years ago, which I'll probably look at, too. The student has a couple of other things for me to read, which will appear in his more formal proposal this week. I expect that these papers will make my brain hurt, in the good way, and am looking forward to diving in.
In the big picture, most undergrad projects in my department are pretty minor as research goes. They are typically more D than R, with students developing something that goes beyond what they learn in any course and doing a little empirical analysis. The extensible effects project is much more ambitious. It builds on serious academic research. It works on a significant problem and proposes something new. That makes the project much more exciting for me as the supervisor.
I hope to report more later, as the semester goes on.
In the Paris Review's The Art of Fiction No. 183, the interviewer asks Tobias Wolff how he balances writing with university teaching. Wolff figures that teaching is a pretty good deal:
When I think about the kinds of jobs I've had and the ways I've lived, and still managed to get work done--my God, teaching in a university looks like easy street. I like talking about books, and I like encountering other smart, passionate readers, and feeling the friction of their thoughts against mine. Teaching forces me to articulate what otherwise would remain inchoate in my thoughts about what I read. I find that valuable, to bring things to a boil.
That reflects how I feel, too, as someone who loves to do computer science and write programs. As a teacher, I get to talk about cool ideas every day with my students, to share what I learn as I write software, and to learn from them as they ask the questions I've stopped asking myself. And they pay me. It's a great deal, perhaps the optimal point in the sort of balance that Derek Sivers recommends.
Wolff immediately followed those sentences with a caution that also strikes close to home:
But if I teach too much it begins to weigh on me--I lose my work. I can't afford to do that anymore, so I keep a fairly light teaching schedule.
One has to balance creative work with the other parts of life that feed the work. Professors at research universities, such as Wolff at Stanford, have different points of equilibrium available to them than profs at teaching universities, where course loads are heavier and usually harder to reduce.
I only teach one course a semester, which really does help me to focus creative energies around a smaller set of ideas than a heavier load does. Of course, I also have the administrative duties of a department head. They suffocate time and energy in a much less productive way than teaching does. (That's the subject of another post.)
Why can't Wolff afford to teach too many courses anymore? I suspect the answer is time. When you reach a certain age, you realize that time is no longer an ally. There are only so many years left, and Wolff probably feels the need to write more urgently. This sensation has been seeping into my mind lately, too, though I fear perhaps a bit too slowly.
~~~~
(I previously quoted Wolff from the same interview in a recent entry about writers who give advice that reminds us that there is no right way to write all programs. A lot of readers seemed to like that one.)
The CS faculty has decided to create common syllabi for all courses in the department. The motivation to do this came from several directions, including a need to meet university requirements regarding "outcomes assessment" and a desire to make sections taught by different instructors more consistent within and across semesters. But it will also help instructors improve their courses. Faculty teaching upper-division courses will have a more better sense of what students coming into their courses should already know, and faculty teaching lower-division courses will have a better sense of what students their students need to be able to do in their upcoming courses.
Our first pass at this is for each faculty member to use a common format to describe one of his or her courses. The common format requires us to define in some detail the purpose, goals, outcomes, and content of the course. Of course, we all have syllabi for our courses that cover some or all of these things, but now we are expected to make all the elements concrete enough that the syllabus can be used by other faculty to teach the course.
For my first attempt, I decided to write an extended syllabus for my Programming Languages and Paradigm course, which I am teaching again in the spring. I have been teaching this course for years, have detailed lecture notes for every session (plus many more), and already give students a syllabus that tries to explain in a useful way the purpose, goals, outcomes, and content of the course. That should give me a running start, right?
I've been working on putting my current syllabus's content into the extended syllabus format for several hours now. At this point, I have concluded that the process is three things: instructive, likely to be very helpful in the long run, and very hard to do.
Defining detailed goals and outcomes is instructive because it causes me to think about the course both at the highest level of detail (the goal of the course both in our curriculum and in our students' CS education) and at the lowest (what we want students our students to know and be able to do to when they leave the course). After teaching the course for many years, I tend to think big-picture thoughts about the course only at the beginning of the semester and only in an effort make general modifications to the direction it takes. Then I think about finer details on a unit-by-unit and session-by-session basis, slowly evolving the course content in reaction to specific stimuli. Looking at the course across multiple levels of abstraction at the same time is teaching me a lot about what my course is and does in a way that I don't usually see when I'm planning only at the high level or executing only at the day-to-day level.
One specific lesson I've learned is really a stark reminder of something I've always known: some of my sessions are chock-full of stuff: ideas, techniques, code, examples, .... That is great for exposing students to a wide swath of the programming languages world, but it is also a recipe for cognitive overload.
This process is helpful because it causes me think about concrete ways I can make the course better. I am finding holes in my coverage of certain topics and leaps from one concept to another that are intuitive in my mind but not documented anywhere.
I've been taking notes as I go long detailing specific changes I can make this spring:
But my most salient conclusion at this moment is that this is hard. It is difficult to explain the course in enough detail that faculty outside the area can grok the course as a part of our curriculum. It's difficult to explain the course in enough detail that other faculty could, at least in principle, teach it as designed. It's difficult to design a course carefully enough to be justifiably confident that it meets your goals for the course. That sounds a little like programming.
But I'm glad I'm doing it. It's worth the effort to design a course this carefully, and to re-visit the design periodically. That sounds a little like programming, too.
The latest edition of my compiler course has wrapped, with grades submitted and now a few days distance between us and the work. The course was successful in many ways, even though not all of the teams were all able to implement the entire compiler. That mutes the students' sense of accomplishment sometimes, but it's not unusual for at least some of the teams to have trouble implementing a complete code generator. A compiler is a big project. Fifteen weeks is not a lot of time. In that time, students learn a lot about compilers, and also about how to work as a team to build a big program using some of the tools of modern software development. In general, I was quite proud of the students' efforts and progress. I hope they were proud of themselves.
One of the meta-lessons students tend to learn in this course is one of the big lessons of any project-centered course:
... making something is a different learning experience from remembering something.
I think that a course like this one also helps most of them learn something else even more personal:
... the discipline in art-making is exercised from within rather than without. You quickly realize that it's your own laziness, ignorance, and sloppiness, not somebody else's bad advice, that are getting in your way. No one can write your [program] for you. You have to figure out a way to write it yourself. You have to make a something where there was a nothing.
"Laziness", "ignorance", and "sloppiness" seem like harsh words, but really they aren't. They are simply labels for weaknesses that almost all of us face when we first learn to create things on our own. Anyone who has written a big program has probably encountered them in some form.
I learned these lessons as a senior, too, in my university's two-term project course. It's never fun to come up short of our hopes or expectations. But most of us do it occasionally, and never more reliably than we are first learning how to make something significant. It is good for us to realize early on our own responsibility for how we work and what we make. It empowers us to take charge of our behavior.
![]() |
The quoted passages are, with the exception of the word "program", taken from Learn by Painting, a New Yorker article about "Leap Before You Look: Black Mountain College, 1933-1957", an exhibit at the Institute of Contemporary Art in Boston. Black Mountain was a liberal arts college with a curriculum built on top of an unusual foundation: making art. Though the college lasted less than a quarter century, its effects were felt across most of art disciplines in the twentieth century. But its mission was bigger: to educate citizens, not artists, through the making art. Making something is a different learning experience from remembering something, and BMC wanted all of its graduates to have this experience.
The article was a good read throughout. It closes with a comment on Black Mountain's vision that touches on computer science and reflects my own thinking about programming. This final paragraph begins with a slight indignity to us in CS but turns quickly into an admiration:
People who teach in the traditional liberal-arts fields today are sometimes aghast at the avidity with which undergraduates flock to courses in tech fields, like computer science. Maybe those students see dollar signs in coding. Why shouldn't they? Right now, tech is where value is being created, as they say. But maybe students are also excited to take courses in which knowing and making are part of the same learning process. Those tech courses are hands-on, collaborative, materials-based (well, virtual materials), and experimental -- a digital Black Mountain curriculum.
When I meet with prospective students and their parents, I stress that, while computer science is technical, it is not vocational. It's more. Many high school students sense this already. What attracts them to the major is a desire to make things: games and apps and websites and .... Earning potential appeals to some of them, of course, but students and parents alike seem more interested in something else that CS offers them: the ability to make things that matter in the modern world. They want to create.
The good news suggested in "Learn by Painting", drawing on the Black Mountain College experiment, is that learning by making things is more than just that. It is a different and, in most ways, more meaningful way to learn about the world. It also teaches you a lot about yourself.
I hope that at least a few of my students got that out of their project course with me, in addition to whatever they learned about compilers.
~~~~
IMAGE. The main building of the former Black Mountain College, on the grounds of Camp Rockmont, a summer camp for boys. Courtesy of Wikipedia. Public domain.
A friend of mine recently shared a link to Radio Garden on a mailing list (remember those?), and in the ensuing conversation, another friend wrote:
I remember when I was a kid playing with my Dad's shortwave radio and just being flabbergasted when late one night I tuned in a station from Peru. Today you can get on your computer and communicate instantly with any spot on the globe, and that engenders no sense of wonder at all.
Such is the nature of advancing technology. Everyone becomes acclimated to amazing new things, and pretty soon they aren't even things any more.
Teachers face a particularly troublesome version of this phenomenon. Teach a subject for a few years, and pretty soon it loses its magic for you. It's all new to your students, though, and if you can let them help you see it through their eyes, you can stay fresh. The danger, though, is that it starts to look pretty ordinary to you, even boring, and you have a hard time helping them feel the magic.
If you read this blog much, you know that I'm pretty easy to amuse and pretty easy to make happy. Even so, I have to guard against taking life and computer science for granted.
Earlier this week, I was reading one reading one of the newer tutorials in Matthew Butterick's Beautiful Racket, Imagine a language: wires. In it, he builds a DSL to solve one of the problems in the 2015 edition of Advent of Code, Some Assembly Required. The problem is fun, specifying a circuit in terms of a small set of operations for wires and gates. Butterick's approach to solving it is fun, too: creating a DSL that treats the specification of a circuit as a program to interpret.
This is no big deal to a jaded old computer scientist, but remember -- or imagine -- what this solution must seem like to a non-computer scientist or to a CS student encountering the study of programming languages for the first time. With a suitable interpreter, every dataset is a program. If that isn't amazing enough, some wires datasets introduce sequencing problems, because the inputs to a gate are defined in the program after the gate. Butterick uses a simple little trick: define wires and gates as functions, not data. This simple little trick is really a big idea in disguise: Functions defer computation. Now circuit programs can be written in any order and executed on demand.
Even after all these years, computing's most mundane ideas can still astonish me sometimes. I am trying to keep my sense of wonder high and to renew it whenever it starts to flag. This is good for me, and good for my students.
~~~~
P.S. As always, I recommend Beautiful Racket, and Matthew Butterick's work more generally, quite highly. He has a nice way of teaching useful ideas in a way that appreciates their beauty.
P.P.S. The working title of this entry was "Paging Louis C.K., Paging Louis C.K." That reference may be a bit dated by now, but still it made me smile.
Earlier this week, Rands tweeted:
Tinkering is a deceptively high value activity.
... to which I followed up:
Which is why a language that enables tinkering is a deceptively high value tool.
I thought about these ideas a couple of days later when I read The Running Conversation in Your Head and came across this paragraph:
The idea is not that you need language for thinking but that when language comes along, it sure is useful. It changes the way you think, it allows you to operate in different ways because you can use the words as tools.
This is how I think about programming in general and about new, and better, programming languages in particular. A programmer can think quite well in just about any language. Many of us cut our teeth in BASIC, and simply learning how to think computationally allowed us to think differently than we did before. But then we learn a radically different or more powerful language, and suddenly we are able to think new thoughts, thoughts we didn't even conceive of in quite the same way before.
It's not that we need the new language in order to think, but when it comes along, it allows us to operate in different ways. New concepts become new tools.
I am looking forward to introducing Racket and functional programming to a new group of students this spring semester. First-class functions and higher-order functions can change how students think about the most basic computations such as loops and about higher-level techniques such as OOP. I hope to do a better job this time around helping them see the ways in which it really is different.
To echo the Running Conversation article again, when we learn a new programming style or language, "Something really special is created. And the thing that is created might well be unique in the universe."
I'm reminded of a student I met with once who told me that he planned to go to law school, and then a few minutes later, when going over a draft of a lab report, said "Yeah... Grammar isn't really my thing." Explaining why I busted up laughing took a while.
When I ask prospective students why they decided not to pursue a CS degree, they often say things to the effect of "Computer science seemed cool, but I heard getting a degree in CS was a lot of work." or "A buddy of mine told me that programming is tedious." Sometimes, I meet these students as they return to the university to get a second degree -- in computer science. Their reasons for returning vary from the economic (a desire for better career opportunities) to personal (a desire to do something that they have always wanted to do, or to pursue a newfound creative interest).
After you've been in the working world a while, a little hard work and some occasional tedium don't seem like deal breakers any more.
Such conversations were on my mind as I read physicist Chad Orzel's recent Science Is Not THAT Special. In this article, Orzel responds to the conventional wisdom that becoming a scientist and doing science involve a lot of hard work that is unlike the exciting stuff that draws kids to science in the first place. Then, when kids encounter the drudgery and hard work, they turn away from science as a potential career.
Orzel's takedown of this idea is spot on. (The quoted passage above is one of the article's lighter moments in confronting the stereotype.) Sure, doing science involves a lot of tedium, but this problem is not unique to science. Getting good at anything requires a lot of hard work and tedious attention to detail. Every job, every area of expertise, has its moments of drudgery. Even the rare few who become professional athletes and artists, with careers generally thought of as dreams that enable people to earn a living doing the thing they love, spend endless hours engaged in the drudgery of practicing technique and automatizing physical actions that become their professional vocabulary.
Why do we act as if science is any different, or should be?
Computer science gets this rap, too. What could be worse than fighting with a compiler to accept a program while you are learning to code? Or plowing threw reams of poorly documented API descriptions to plug your code into someone's e-commerce system?
Personally, I can think of lots of things that are worse. I am under no illusion, however, that other professionals are somehow shielded from such negative experiences. I just prefer my pains to theirs.
Maybe some people don't like certain kinds of drudgery. That's fair. Sometimes we gravitate toward the things whose drudgery we don't mind, and sometimes we come to accept the drudgery of the things we love to do. I'm not sure which explains my fascination with programming. I certainly enjoy the drudgery of computer science more than that of most other activities -- or at least I suffer it more gladly.
I'm with Orzel. Let's be honest with ourselves and our students that getting good at anything takes a lot of hard work and, once you master something, you'll occasionally face some tedium in the trenches. Science, and computer science in particular, are not that much different from anything else.
I've been wanting to write a blog entry or two lately about my compiler course and about papers I've read recently, but I've not managed to free up much time as semester winds down. That's one of the problems with having Big Ideas to write about: they seem daunting and, at the very least, take time to work through.
So instead here are two brief notes about articles that crossed my newsfeed recently and planted themselves in my mind. Perhaps you will enjoy them even without much commentary from me.
• A Student's Unusual Proof Might Be A Better Proof
I asked a student to show that between any two rationals is a rational.
She did the following: if x < y are rational then take δ << y-x and rational and use x+δ.
I love the student's two proofs in article! Student programmers are similarly creative. Their unusual solutions often expose biases in my thinking and give me way to think about a problem. If nothing else, they help to understand better how students think about ideas that I take for granted.
Some girls entered a school art competition. Fewer boys than girls entered the competition.
She projected her screen and asked, "What math do you see in this problem?"
Pregnant pause.
"There isn't any math. There aren't any numbers."
I am fascinated by the possibility of adapting this idea to teaching students to think like a programmer. In an intro course, for example, students struggle with computational ideas such as loops and functions even though they have a lot of experience with these ideas embodied in their daily lives. Perhaps the language we use gets in the way of them developing their own algorithmic skills. Maybe I could use computationless word problems to get them started?
I'm giving serious thought to ways I might use this approach to help students learn functional programming in my Programming Languages course this spring. The authors describes how to write numberless word problems, and I'm wondering how I might bring the philosophy to computer science. If you have any ideas, please share!
Khoi Vinh wrote a short blog entry called The Underestimated Merits of Copying Someone Else's Work that reminds us how valuable copying others' work, a standard practice in the arts, can be. At the lowest level there is copying at the textual level. Sometimes, the value is mental or mechanical:
Hunter S. Thompson famously re-typed, word for word, F. Scott Fitzgerald's "The Great Gatsby" just to learn how it was done.
This made me think back to the days when people typed up code they found in Byte magazine and other periodicals. Of course, typing a program gave you more than practice typing or a sense of what it was like to type that much; it also gave you a working program that you could use and tinker with. I don't know if anyone would ever copy a short story or novel by hand so that they could morph it into something new, but we can do that meaningfully with code.
I missed the "copy code from Byte" phase of computing. My family never had a home computer, and by the time I got to college and changed my major to CS, I had plenty of new programs to write. I pulled ideas about chess-playing programs and other programs I wanted to write from books and magazines, but I never typed up an entire program's source code. (I mention one of my first personal projects in an old OOPSLA workshop report.)
I don't hear much these days about people copying code keystroke for keystroke. Zed Shaw has championed this idea in a series of introductory programming books such as Learn Python The Hard Way. There is probably something to be learned by copying code Hunter Thompson-style, feeling the rhythm of syntax and format by repetition, and soaking up semantics along the way.
Vinh has a more interesting sort of copying in mind, though: copying the interface of a software product:
It's odd then to realize that copying product interfaces is such an uncommon learning technique in design. ... it's even easier to re-create designs than it is to re-create other forms of art. With a painting or sculpture, it's often difficult to get access to the historically accurate tools and materials that were used to create the original. With today's product design, the tools are readily available; most of us already own the exact same software employed to create any of the most prominent product designs you could name.
This idea generalizes beyond interfaces to any program for which we don't have source code. We often talk about reverse engineering a program, but in my experience this usually refers to creating a program that behaves "just like" the original. Copying an interface pixel by pixel, like copying a program or novel character by character, requires the artist to attend to the smallest details -- to create an exact replica, not a similar work.
We cannot reverse engineer a program and arrive at identical source code, of course, but we can try to replicate behavior and interface exactly. Doing so might help a person appreciate the details of code more. Such a practice might even help a programmer learn the craft of programming in a different way.
In My Writing Education: A Time Line, George Saunders recounts stories of his interactions with writing teachers over the years, first in the creative writing program at Syracuse and later as a writer and teacher himself. Along the way, he shows us some of the ways that our best teachers move us.
Here, the teacher gets a bad review:
Doug gets an unkind review. We are worried. Will one of us dopily bring it up in workshop? We don't. Doug does. Right off the bat. He wants to talk about it, because he feels there might be something in it for us. The talk he gives us is beautiful, honest, courageous, totally generous. He shows us where the reviewer was wrong -- but also where the reviewer might have gotten it right. Doug talks about the importance of being able to extract the useful bits from even a hurtful review: this is important, because it will make the next book better. He talks about the fact that it was hard for him to get up this morning after that review and write, but that he did it anyway. He's in it for the long haul, we can see.
I know some faculty who basically ignore student assessments of their teaching. They paid attention for a while at the beginning of their careers, but it hurt too much, so they stopped. Most of the good teachers I know, though, approach their student assessments the way that Doug approaches his bad review: they look for the truths in the reviews, take those truths seriously, and use them to get better. Yes, a bad set of assessments hurts. But if you are in it for the long haul, you get back to work.
Here, the teacher gives a bad review:
What Doug does for me in this meeting is respect me, by declining to hyperbolize my crap thesis. I don't remember what he said about it, but what he did not say was, you know: "Amazing, you did a great job, this is publishable, you rocked our world with this! Loved the elephant." There's this theory that self-esteem has to do with getting confirmation from the outside world that our perceptions are fundamentally accurate. What Doug does at this meeting is increase my self-esteem by confirming that my perception of the work I'd been doing is fundamentally accurate. The work I've been doing is bad. Or, worse: it's blah. This is uplifting -- liberating, even -- to have my unspoken opinion of my work confirmed. I don't have to pretend bad is good. This frees me to leave it behind and move on and try to do something better. The main thing I feel: respected.
Sometimes, students make their best effort but come up short. They deserve the respect of an honest review. Honest doesn't have to be harsh; there is a difference between being honest and being a jerk. Sometimes, students don't make their best effort, and they deserve the respect of an honest review, too. Again, being honest doesn't mean being harsh. In my experience, most students appreciate an honest, objective review of their work. They almost always know when they are coming up short, or when they aren't working hard enough. When a teacher confirms that knowledge, they are freed -- or motivated in a new way -- to move forward.
Here, the teacher reads student work:
I am teaching at Syracuse myself now. Toby, Arthur Flowers, and I are reading that year's admissions materials. Toby reads every page of every story in every application, even the ones we are almost certainly rejecting, and never fails to find a nice moment, even when it occurs on the last page of the last story of a doomed application. "Remember that beautiful description of a sailboat on around page 29 of the third piece?" he'll say. And Arthur and I will say: "Uh, yeah ... that was ... a really cool sailboat." Toby has a kind of photographic memory re stories, and such a love for the form that goodness, no matter where it's found or what it's surrounded by, seems to excite his enthusiasm. Again, that same lesson: good teaching is grounded in generosity of spirit.
It has taken me a long time as a teacher to learn to have Toby's mindset when reading student work, and I'm still learning. Over the last few years, I've noticed myself trying more deliberately to find the nice moments in students' programs, even the bad ones, and to tell students about them. That doesn't mean being dishonest about the quality of the overall program. But nice moments are worth celebrating, wherever they are found. Sometimes, those are precisely the elements students need to hear about, because they are the building blocks for getting better.
Finally, here is the teacher talking about his own craft:
During the Q&A someone asks what Toby would do if he couldn't be a writer.
A long, perplexed pause.
"I would be very sad", he finally says.
I like teaching computer science, but what has enabled me to stay in the classroom for so many years and given me the stamina to get better at teaching is that I like doing computer science. I like to program. I like to solve problems. I like to find abstractions and look for ways to solve other problems. There are many things I could do if I were not a computer scientist, but knowing what I know now, I would be a little sad.
![]() |
Last week, I read a blog entry by Ben Thompson that said Influence lives at intersections. Thompson was echoing a comment about Daniel Kahneman's career: "Intellectual influence is the ability to dissolve disciplinary boundaries." These were timely references for my week.
On Friday night, I had the pleasure of attending the Heritage Honours Awards, an annual awards dinner hosted by my university's alumni association. One of our alumni, Wade Arnold, received the Young Alumni Award for demonstrated success early in a career. I mentioned Wade in a blog entry several years ago, when he and I spoke together at a seminar on interactive digital technologies. That day, Wade talked about intersections:
It is difficult to be the best at any one thing, but if you are very good at two or three or five, then you can be the best in a particular market niche. The power of the intersection.
Wade built his company, Banno, by becoming very good at several things, including functional programming, computing infrastructure, web development, mobile development, and financial technology. He was foresightful and lucky enough to develop this combination of strengths before most other people did. Most important, though, he worked really hard to build his company: a company that people wanted to work with, and a company that people wanted to work for. As a result, he was able to grow a successful start-up in a small university town in the middle of the country.
It's been a delight for me to know Wade all these years and watch him do his thing. I'll bet he has some interesting ideas in store for the future.
The dinner also provided me with some unexpected feelings. Several times over the course of the evening, someone said, "Dr. Wallingford -- I feel like I know you." I had the pleasure of meeting Wade's parents, who said kind things about my influence on their son. Even his nine-year-old son said, "My dad was talking about you in the car on the drive over." No one was confused about whom we were there to honor Friday night, about who had done the considerable work to build himself into an admirable man and founder. That was all Wade. But my experience that night is a small reminder to all you teachers out there: you do have an effect on people. It was certainly a welcome reminder for me at the end of a trying semester.
I enjoyed this interview with Atul Gawande by Ezra Klein. When talking about making mistakes, Gawande notes that humans have enough knowledge to cut way down on errors in many disciplines, but we do not always use that knowledge effectively. Mistakes come naturally from the environments in which we work:
We're all set up for failure under the conditions of complexity.
Mistakes are often more a matter of discipline and attention to detail than a matter of knowledge or understanding. Klein captures the essence of Gawande's lesson in one of his questions:
We have this idea that competence is not making mistakes and getting everything right. [But really...] Competence is knowing you will make mistakes and setting up a context that will help reduce the possibility of error but also help deal with the aftermath of error.
In my experience, this is a hard lesson for computer science students to grok. It's okay to make mistakes, but create conditions where you make as few as possible and in which you can recognize and deal with the mistakes as quickly as possible. High-discipline practices such as test-first and pair programming, version control, and automated builds make a lot more sense when you see them from this perspective.
There is an open thread on the SIGCSE mailing list called "Forget the language wars, try the math wars". Faculty are discussing how to justify math requirements on a CS major, especially for students who "just want to be programmers". Some people argue that math requirements are a barrier to recruiting students who can succeed in computer science, in particular calculus.
Somewhere along the line, Cay Horstmann wrote a couple of things I agree with. First, he said that he didn't want to defend the calculus requirement because most calculus courses do not teach students how to think "but how to follow recipes". I have long had this complaint about calculus, especially as it's taught in most US high schools and universities. Then he wrote something more positive:
What I would like to see is teaching math alongside with programming. None of my students were able to tell me what sine and cosine were good for, but when they had to program a dial [in a user interface], they said "oh".
Couldn't that "oh" have come earlier in their lives? Why don't students do programming in middle school math? I am not talking large programs--just a few lines, so that they can build models and intuition.
I agree wholeheartedly. And even if students do not have such experiences in their K-12 math classes, the least we could do help them have that "oh" experience earlier in their university studies.
My colleagues and I have been discussing our Discrete Structures course now for a few weeks, including expected outcomes, its role as a prerequisite to other courses, and how we teach it. I have suggested that one of the best ways to learn discrete math is to connect it with programs. At our university, students have taken at least one semester of programming (currently, in Python) before they take Discrete. We should use that to our advantage!
A program can help make an abstract idea concrete. When learning about set operations, why do only paper-and-pencil exercises when you can use simple Python expressions in the REPL? Yes, adding programming to the mix creates new issues to deal with, but if designed well, such instruction could both improve students' understanding of discrete structures -- as Horstmann says, helping them build models and intuition -- and give students more practice writing simple programs. An ancillary benefit might be to help students see that computer scientists can use computation to help them learn new things, thus preparing for habits that can extend to wider settings.
Unfortunately, the most popular Discrete Structures textbooks don't help much. They do try to use CS-centric examples, but they don't seem otherwise to use the fact that students are CS majors. I don't really blame them. They are writing for a market in which students study many different languages in CS 1, so they can't (and shouldn't) assume any particular programming language background. Even worse, the Discrete Structures course appears at different places throughout the CS curriculum, which means that textbooks can't assume even any particular non-language CS experience.
Returning to Horstmann's suggestion to augment math instruction with programming in K-12, there is, of course, a strong movement nationally to teach computer science in high school. My state has been disappointingly slow to get on board, but we are finally seeing action. But most of the focus in this nationwide movement is on teaching CS qua CS, with less interest in emphasis on integrating CS into math and other core courses.
For this reason, let us again take a moment to thank the people behind the Bootstrap project for leading the charge in this regard, helping teachers use programming in Racket to teach algebra and other core topics. They are even evaluating the efficacy of the work and showing that the curriculum works. This may not surprise us in CS, but empirical evidence of success is essential if we hope to get teacher prep programs and state boards of education to take the idea seriously.
W.H. Auden, in A Certain World, on the idea of The Two Cultures:
Of course, there is only one. Of course, the natural sciences are just as "humane" as letters. There are, however, two languages, the spoken verbal language of literature, and the written sign language of mathematics, which is the language of science. This puts the scientist at a great advantage, for, since like all of us he has learned to read and write, he can understand a poem or a novel, whereas there are very few men of letters who can understand a scientific paper once they come to the mathematical parts.
When I was a boy, we were taught the literary languages, like Latin and Greek, extremely well, but mathematics atrociously badly. Beginning with the multiplication table, we learned a series of operations by rote which, if remembered correctly, gave the "right" answer, but about any basic principles, like the concept of number, we were told nothing. Typical of the teaching methods then in vogue is the mnemonic which I had to learn.Minus times Minus equals Plus:
The reason for this we need not discuss.
Sadly, we still teach young people that it's okay if math and science are too hard to master. They grow into adults who feel a chasm between "arts and letters" and "math and science". But as Auden notes rightly, there is no chasm; there is mostly just another language to learn and appreciate.
(It may be some consolation to Auden that we've reached a point where most scientists have to work to understand papers written by scientists in other disciplines. They are written in highly specialized languages.)
In my experience, it is more acceptable for a humanities person to say "I'm not a science person" or "I don't like math" than for a scientist to say something similar about literature, art, or music. The latter person is thought, silently, to be a Philistine; the former, an educated person with a specialty.
I've often wondered if this experience suffers from observation bias or association bias. It may well. I certainly know artists and writers who have mastered both languages and who remain intensely curious about questions that span the supposed chasm between their specialties and mine. I'm interested in those questions, too.
Even with this asymmetry, the presumed chasm between cultures creates low expectations for us scientists. Whenever my friends in the humanities find out that I've read all of Kafka's novels and short stories; that Rosencrantz and Guildenstern Are Dead is my favorite play, or that I even have a favorite play; that I really enjoyed the work of choreographer Merce Cunningham; that my office bookshelf includes the complete works of William Shakespeare and a volume of William Blake's poetry -- I love the romantics! -- most seem genuinely surprised. "You're a computer scientist, right?" (Yes, I like Asimov, Heinlein, Clarke, and Bradbury, too.)
Auden attributes his illiteracy in the language of mathematics and science to bad education. The good news is that we can reduce, if not eliminate, the language gap by teaching both languages well. This is a challenge for both parents and schools and will take time. Change is hard, especially when it involves the ways we talk about the world.
![]() |
I agree with W.H. Auden:
Who on earth invented the silly convention that it is boring or impolite to talk shop? Nothing is more interesting to listen to, especially if the shop is not one's own.
My wife went on a forty-mile bike ride this morning, a fundraiser for the Cedar Valley Bicycle Collective, which visited three local farms. At those stops, I had the great fortune to listen to folks on all three farms talk shop. We learned about making ice cream and woodcarving at Three Pines Farm. We learned about selecting, growing, and picking apples -- and the damage hail and bugs can do -- at Blueridge Orchard. And the owner of the Fitkin Popcorn Farm talked about the popcorn business. He showed us the machines they use to sort the corn out of the field, first by size and then by density. He also talked about planting fields, harvesting the corn, and selling the product nationally. I even learned that we can pop the corn while it's still on the ears! (This will happen in my house very soon.)
I love to listen to people talk shop. In unguarded moments, they speak honestly about something they love and know deeply. They let us in on what it is like for them to work in their corner of the world. However big I try to make my world, there is so much more out there to learn.
The Auden passage is from his book A Certain World, a collage of poems, quotes, and short pieces from other writers with occasional comments of his own. Auden would have been an eclectic blogger! This book feels like a Tumblr blog, without all the pictures and 'likes'. Some of the passages are out of date, but they let us peak in on the mind of an accomplished poet. A little like good shop talk.
On the first day of my compiler class this fall, I had my students fill out a short survey to help me set the stage for the course. After I asked them to list the the three or four programming languages they know best, I asked them:
While they were completing the survey, one student raised his hand and asked, "When you easy or hard to compile, do you mean for the programmer or the compiler?" I laughed almost immediately.
Fortunately, I've had all these students in class before, and they know that I'm not a mean-spirited person. Even so, I just as quickly apologized for laughing and let him know that I wasn't laughing at the question so much as laughing at my own surprise: It had never occurred to me that someone might interpret the question in that way!
I realized, though, that from a student's perspective, getting a Python program to the point of runnability is a very different thing from getting, say, a Java or Ada program to the point of runnability. For a beginner, to get his or her first few Ada programs to compile is indeed a chore. I remember feeling the same way when I learned Haskell as a relatively experienced professor and programmer, many years after I had last been a student in a classroom.
This story came to mind as I read Required Reading for Math Teachers this morning. It's actually pretty good reading for teachers of any subject. Toward the end of the post, the author reminds us that it helps for teachers to be legitimately open to students' thought processes, whether or not they think what we think they should be thinking. In fact, those are precisely the moments when we want to be most open to what they are thinking. These are the moments that help us to diagnose errors in their thinking -- and in ours.
This passage resonated with my experience:
I have throughout my career been repeatedly surprised by the discovery that nearly every time a student offers an idea authentically (i.e. not as just a random guess), it makes some sort of sense. Maybe not complete sense, and maybe it's not at all where I was headed. But if I can curb my initial reaction of "this kid is totally confused" long enough to actually take in the train of thought, there is almost uniformly some worthwhile reasoning inside it. Then even if I need to say "we're going to stick to the topic", I can do so after acknowledging the reasoning.
Acknowledging students' ideas and thinking is a matter of basic respect, but it also plays a big role in the environment we create in our classes. I hope that I have been respectful and open enough with these students in the past that my question-asker could trust that I wan't mocking him and that I was genuinely surprised. We all learned something that day.
As that blog post goes on to say, we have to make sure that the questions we ask students are legitimate questions. We communicate this intention by acknowledging people when they treat our questions as legitimate. We teachers need to treat our student's questions the same way.
In 7 things I wish people understood about being a teacher, Andrew Simmons captures a few of the things that make teaching so rewarding and so challenging. If you don't understand these truths, you may not understand why anyone would want to teach. You also run the risk of misunderstanding the problems with our education system and thus supporting changes that are unlikely to fix them. Check it out.
Even though Simmons writes of teaching high school, most of what he says applies just as well to university professors. I especially liked this comment, on what software developers call sustainable pace
... would-be superteachers are smart, sometimes masochistic 23-year-olds working 18-hour days to pump up test scores for a few years before moving on to administrative positions, law school, or nervous breakdowns. They embrace an unsustainable load.
I used to wonder why so many good teachers ended up leaving the classroom. One reason is burn-out. Universities burn out researchers and teachers alike by pushing them onto a treadmill, or by letting them get on and stay there of their own volition. Good teaching can happen every year, if we learn how to maintain balance.
My favorite line of the article, though, is this gem:
Everything I learn is filtered through the possibility that it might be taught.
When I read that line, I nodded my head silently. This guy really is a teacher.
Earlier this week, I tweeted a paraphrase of David Parnas that a few people liked:
Parnas observes: "Professional Engineers rarely use 'engineer' as a verb." And it's not because they are busy 'architecting' systems.
The paraphrase comes from Parnas's On ICSE's "Most Influential" Papers, which appears in the July 1995 issue so ACM SIGSOFT's Software Engineering Notes. He wrote that paper in conjunction with his acceptance speech on receiving the Most Influential Paper award at ICSE 17. It's a good read on how the software engineering research community's influence was, at least at that time, limited to other researchers. Parnas asserts that researchers should strive to influence practitioners, the people who are actually writing software.
Why doesn't software engineering research influence practitioners? It's simple:
Computer professionals do not read our literature because it does not help them to do their job better.
In a section called "We are talking to ourselves", Parnas explains why the software engineering literature fails to connect with people who write software:
Most of our papers are written in the tradition of science, not engineering. We write up the results of our research for readers who are also researchers, not for practitioners. We base our papers on previous work done by other researchers, and often ignore current practical problems. In many other engineering fields, the results of research are reported in "How to" papers. Our papers are "This is what I did" papers. We describe our work, how our work differs from other people's work, what we will do next, etc. This is quite appropriate for papers directed to other researchers, but it is not what is needed in papers that are intended to influence practitioners. Practitioners are far more interested in being told the basic assumptions behind the work, than in knowing how this work differs from the work by the author's cohorts in the research game.
Around the time Parnas wrote this article and gave his acceptance speech at ICSE 17, the Pattern Languages of Programs conferences were taking off, with a similar motivation: to create a literature by and for software practitioners. Patterns describe how to create programs in practical terms. They describe techniques, but also the context in which they work, the forces that make them more and less applicable, and the patterns you can use to address the issues that arise after you the technique. The community encouraged writing in a straightforward style, using the vocabulary of professional developers.
At the early PLoP conferences, you could feel the tension between practitioners and academics, some of which grew out of the academic style of writing and the traditions of the scientific literature. I had to learn a lot about how to write for an audience of software developers. Fortunately, the people in the PLoP community took the time to help me get better. I have fond memories of receiving feedback from Frank Buschman, Peter Sommerlad, Ralph Johnson, Ward Cunningham, Kyle Brown, Ken, Auer, and many others. The feedback wasn't always easy to internalize -- it's hard to change! -- but it was necessary.
I'm not sure that an appreciably larger number of academics in software engineering and computer science more broadly write for the wider community of software practitioners these days than when Parnas made his remarks. There are certainly more venues available to us from patterns-related conferences, separate tracks at conferences like SPLASH, and blogs. Unfortunately, the academic reward structure isn't always friendly to this kind of writing, especially early in one's career. Some universities have begun to open up their definition of scholarship, though, which creates new opportunities.
At their best, software patterns are exactly what Parnas calls for: creating a how-to literature aimed at practitioners. Researchers and practitioners can both participate in this endeavor.
![]() |
Early in Arnold Samuelson's With Hemingway: A Year in Key West and Cuba, Papa is giving an impromptu lecture about writing to two aspiring young writers. (He does that a lot in the book, whenever the men are out sailing and fishing.) This particular lecture was prompted by what he thought was bad advice in a book by a successful pulp fiction author on how to get started as a writer. An earlier session had focused on the shortcomings of going to college to learn how to become a writer.
Toward the end of his discourse, Hemingway tells the young writers to do daily writing exercise and generously offers to read read their work, giving feedback on how to get better. This offer elicits a few more remarks about the idea of college writing professors:
"They ought to have me teach some of those college classes. I could teach them something. Most professors of English composition can tell the students what's wrong with their pieces but they don't know how to make them good, because, if they knew that, they'd be writers themselves and they wouldn't have to teach."
"What do you think of the life of a professor?"
"All right for a person who is vain and likes to have the adulation of his students. Well, now, do you fellows think you can remember everything Professor Hemingway has told you? Are you prepared for a written examination on the lecture?"
Teaching computer science must be different from teaching fiction writing. I have been teaching for quite a few years now and have never received any adulation. Then again, though, I've never experienced much derision either. My students seems to develop a narrower set of emotions. Some seem to like me quite a bit and look for chances to take another course with me. Other students are... indifferent. To them, I'm just the guy standing in the way of them getting to somewhere else they want to be.
Hemingway's "have to teach" dig is cliché. Perhaps the Ernest Hemingways and Scott Fitzgeralds of the world should be devoting all of their time to writing, but there have a been any number of excellent authors who have supplemented their incomes and filled the down time between creative bursts by helping other writers find a path for themselves. Samuelson's book itself is a testament to how much Papa loved to share his wisdom and to help newcomers find their footing in a tough business. During all those hours at sea, Hemingway was teaching.
Still, I understand what Hemingway means when he speaks of the difference between knowing that something is bad and knowing how to make something good. One of the biggest challenges I faced in my early years as a professor was figuring out how to go beyond pointing out errors and weaknesses in my students' code to giving them concrete advice on how two design and write good programs. I'm still learning how to do that.
I'm lucky that I like to write programs myself. Writing code and learning new styles and languages is the only way to stay sharp. Perhaps if I were really good, I'd leave academia and build systems for Google or some hot start-up, as Hemingway would have it. I'm certainly under no illusion that I can simulate that kind of experience working at a university. But I do think a person can both do and teach, and that the best teachers are ones who take both seriously. In computer science, it is a constant challenge to keep up with students who are pushing ahead into a world that keeps changing.
~~~~
The photo above comes from the John F. Kennedy Presidential Library and Museum. It shows Hemingway sitting on a dock next to his boat, Pilar, sometime in the 1930s. The conversation quoted above took place on the Pilar in 1934.
I've been reading a bunch of the essays on David Chapman's Meaningness website lately, after seeing a link to one on Twitter. (Thanks, @kaledic.) This morning I read How To Think Real Good, about one of Chapman's abandoned projects: a book of advice for how to think and solve problems. He may never write this book as he once imagined it, but I'm glad he wrote this essay about the idea.
First of all, it was a fun read, at least for me. Chapman is a former AI researcher, and some of the stories he tells remind me of things I experienced when I was in AI. We were even in school at about the same time, though in different parts of the country and different kinds of program. His work was much more important than mine, but I think at some fundamental level most people in AI share common dreams and goals. It was fun to listen as Chapman reminisced about knowledge and AI.
He also introduced me to the dandy portmanteau anvilicious. I keep learning new words! There are so many good ones, and people make up the ones that don't exist already.
My enjoyment was heightened by the fact that the essay stimulated the parts of my brain that like to think about thinking. Chapman includes a few of the heuristics that he intended to include in his book, along with anecdotes that illustrate or motivate them. Here are three:
All problem formulations are "false", because they abstract away details of reality.
Solve a simplified version of the problem first. If you can't do even that, you're in trouble.
Probability theory is sometimes an excellent way of dealing with uncertainty, but it's not the only way, and sometimes it's a terrible way.
He elaborates on the last of these, pointing out that probability theory tends to collapse many different kinds of uncertainty into a single value. This does not work all that well in practice, because different kinds of uncertainty often need to be handles in very different ways.
Chapman has a lot to say about probability. This essay was prompted by what he sees as an over-reliance of the rationalist community on a pop version of Bayesianism as its foundation for reasoning. But as an old AI researcher, he knows that an idea can sound good and fail in practice for all sorts of reasons. He has also seen how a computer program can make clear exactly what does and doesn't work.
Artificial intelligence has always played a useful role as a reality check on ideas about mind, knowledge, reasoning, and thought. More generally, anyone who writes computer programs knows this, too. You can make ambiguous claims with English sentences, but to write a program you really have to have a precise idea. When you don't have a precise idea, your program itself is a precise formulation of something. Figuring out what that is can be a way of figuring out what you were really thing about in the first place.
This is one of the most important lessons college students learn from their intro CS courses. It's an experience that can benefit all students, not just CS majors.
Chapman also includes a few heuristics for approaching the problem of thinking, basically ways to put yourself in a position to become a better thinker. Two of my favorites are:
Try to figure out how people smarter than you think.
Find a teacher who is willing to go meta and explain how a field works, instead of lecturing you on its subject matter.
This really is good advice. Subject matter is much easier to come by than deep understanding of how the discipline work, especially in these days of the web.
The word meta appears frequently throughout this essay. (I love that the essay is posted on the metablog/ portion of his site!) Chapman's project is thinking about thinking, a step up the ladder of abstraction from "simply" thinking. An AI program must reason; an AI researcher must reason about how to reason.
This is the great siren of artificial intelligence, the source of its power and also its weaknesses: Anything you can do, I can do meta.
I think this gets at why I enjoyed this essay so much. AI is ultimately the discipline of applied epistemology, and most of us who are lured into AI's arms share an interest in what it means to speak of knowledge. If we really understand knowledge, then we ought to be able to write a computer program that implements that understanding. And if we do, how can we say that our computer program isn't doing essentially the same thing that makes us humans intelligent?
As much as I love computer science and programming, my favorite course in graduate school was an epistemology course I took with Prof. Rich Hall. It drove straight to the core curiosity that impelled me to study AI in the first place. In the first week of the course, Prof. Hall laid out the notion of justified true belief, and from there I was hooked.
A lot of AI starts with a naive feeling of this sort, whether explicitly stated or not. Doing AI research brings that feeling into contact with reality. Then things gets serious. It's all enormously stimulating.
Ultimately Chapman left the field, disillusioned by what he saw as a fundamental limitation that AI's bag of tricks could never resolve. Even so, the questions that led him to AI still motivate him and his current work, which is good for all of us, I think.
This essay brought back a lot of pleasant memories for me. Even though I, too, am no longer in AI, the questions that led me to the field still motivate me and my interests in program design, programming languages, software development, and CS education. It is hard to escape the questions of what it means to think and how we can do it better. These remain central problems of what it means to be human.
![]() |
I recently came across an old entry on Jonathan Blow's blog called John Carmack on Inlined Code. The bulk of the entry consists an even older email message that Carmack, lead programmer on video games such as Doom and Quake, sent to a mailing list, encouraging developers to consider inlining function calls as a matter of style. This email message is the earliest explanation I've seen of Carmack's drift toward functional programming, seeking to as many of its benefits as possible even in the harshly real-time environment of game programming.
The article is a great read, with much advice borne in the trenches of writing and testing large programs whose run-time performance is key to their success. Some of the ideas involve programming language:
It would be kind of nice if C had a "functional" keyword to enforce no global references.
... while others are more about design style:
The function that is least likely to cause a problem is one that doesn't exist, which is the benefit of inlining it.
... and still others remind us to rely on good tools to help avoid inevitable human error:
I now strongly encourage explicit loops for everything, and hope the compiler unrolls it properly.
(This one may come in handy as I prepare to teach my compiler course again this fall.)
This message-within-a-blog-entry itself quotes another email message, by Henry Spencer, which contains the seeds of a programming challenge. Spencer described a piece of flight software written in a particularly limiting style:
It disallowed both subroutine calls and backward branches, except for the one at the bottom of the main loop. Control flow went forward only. Sometimes one piece of code had to leave a note for a later piece telling it what to do, but this worked out well for testing: all data was allocated statically, and monitoring those variables gave a clear picture of most everything the software was doing.
Wow: one big loop, within which all control flows forward. To me, this sounds like a devilish challenge to take on when writing even a moderately complex program like a scanner or parser, which generally contain many loops within loops. In this regard, it reminds me of the Polymorphism Challenge's prohibition of if-statements and other forms of selection in code. The goal of that challenge was to help programmers really grok how the use of substitutable objects can lead to an entirely different style of program than we tend to create with traditional procedural programming.
Even though Carmack knew that "a great deal of stuff that goes on in the aerospace industry should not be emulated by anyone, and is often self destructive", he thought that this idea might have practical value, so he tried it out. The experience helped him evolve his programming style in a promising direction. This is a great example of the power of the pedagogical pattern known as Three Bears: take an idea to its extreme in order to learn the boundaries of its utility. Sometimes, you will find that those boundaries lie beyond what you originally thought.
Carmack's whole article is worth a read. Thanks to Jonathan Blow for preserving it for us.
~~~~
The image above is an example of the cover art for the "Commander Keen" series of video games, courtesy of Wikipedia. John Carmack was also the lead programmer for this series. What a remarkable oeuvre he has produced.
Like many people, I am fond of analogies between software development and arts like writing and painting. It's easy to be seduced by similarities even when the daily experiences of programmers and artists are often so different.
For that reason, I was glad that this statement by sculptor Jacques Lipschutz stood out in such great relief from the text around it:
Teaching is death. If he teaches, the sculptor has to open up and reveal things that should be closed and sacred.
For me, teaching computer science has been just the opposite. Teaching forces me to open up my thinking processes. It encourages me to talk with professional developers about how they do what they do and what they think about along the way. Through these discussions, we do reveal things that sometimes feel almost sacred, but I think we all benefit from the examination. It not only helps me to teach novice developers more effectively; it also helps me to be a better programmer myself.
~~~~
(The Lipschutz passage comes from Conversations with Artists, which I quoted for the first time last week.)
A great analogy from Frank Cottrell:
Think of it, he says, the sun pours down its energy onto the surface of the planet for millennia. The leaves soak up the energy. The trees fall and turn to coal. Coal is solid sunlight, the stored memory of millions of uninhabited summers. Then one day, in Coalbrookdale, someone opens a hole in the ground and all that stored energy comes pouring out and is consumed in furnaces, engines, motors.
When we -- teachers, parents, carers, friends -- read to our children, I believe that's what we're doing. Laying down strata of fuel, fuel studded with fossils and treasures. If we ask for anything back, we burn it off too soon.
My wife and I surely did a lot of things wrong as we raised our daughters, but I think we did at least two things right: we read to them all the time, and we talked to them like we talk to everyone else. Their ability to speak and reason and imagine grew out of those simple, respectful acts.
Teaching at a university creates an upside-down dynamic by comparison, especially in a discipline many think of as being about jobs. It is the students and parents who are more likely to focus on the utility of knowledge. Students sometimes ask, "When will we use this in industry?" With the cost of tuition and the uncertainty of the times, I understand their concern. Even so, there are times I would like to say "I don't know" or, in my weaker moments, the seemingly disrespectful "I don't care". Something more important should be happening here. We are creating fuel for a lifetime.
(The Cottrell article takes an unusual route to an interesting idea. It was worth a read.)
Painter Adolph Gottlieb was dismissive of art school in the 1950s:
I'd have done what I'm doing now twenty years ago if I hadn't had to go through that crap. What is the function of the art school today? To confuse the student. To make a living for the teacher. The painter can learn from museums -- probably it is the only way he can to learn. All artists have to solve their problems in the context of their own civilization, painting what their time permits them to paint, extending the boundaries a little further.
It isn't much of a stretch to apply this to computer programming in today's world. We can learn so much these days from programs freely available on GitHub and elsewhere on the web. A good teacher can help, but in general is there a better way to learn how to make things than to study existing works and to make our own?
Most importantly, today's programmers-to-be have to solve their problems in the context of their own civilization: today's computing, whether that's mobile or web or Minecraft. University courses have a hard time keeping up with constant change in the programming milieu. Instead, they often rely on general principles that apply across most environments but which seem lifeless in their abstraction.
I hope that, as a teacher, I add some value for the living I receive. Students with interests and questions and goals help keep me and my courses alive. At least I can set a lower bound of not confusing my students any more than necessary.
~~~~
(The passage above is quoted from Conversations with Artists, Selden Rodman's delightful excursion through the art world of the 1950s, chatting with many of the great artists of the era. It's not an academic treatise, but rather more an educated friend chatting with creative friends. I would thank the person who recommended this, but I have forgotten whose tweet or article did.)
Faculty in my department are seeking students to work on research projects next. I've sent a couple of messages to our student mailing list this week with project details. One of my advisees, a bright guy with a good mind and several interests, sent me a question about applying. His question got to the heart of a concern many students have, so I responded to the entire student list. I thought I'd share the exchange as an open letter to all students out there who are hesitant about pursuing an opportunity.
The student wrote something close to this:
Both professors' projects seem like great opportunities, but I don't feel even remotely qualified for either of them. I imagine many students feel like this. The projects both seem like they'd entail a really advanced set of skills -- especially needing mathematics -- but they also require students with at least two semesters left of school. Should I bother contacting them? I don't want to jump the gun and rule myself out.
Many students "self-select out" -- choose not to pursue an opportunity -- because they don't feel qualified. That's too bad. You would be surprised how often the profs would be able to find a way to include a student who are interested in their work. Sometimes, they work around a skill the student doesn't have by finding a piece of the project he or she can contribute to. More often, though, they help the student begin to learn the skill they need. We learn many things best by doing them.
Time constraints can be a real issue. One semester is not enough time to contribute much to some projects. A grant may run for a year and thus work best with a student who will be around for two or more semesters. Even so, the prof may be able to find a way to include you. They like what they do and like to work with other people who do, too.
My advice is to take a chance. Contact the professor. Stop in to talk with him or her about your interest, your skills, and your constraints. The worst case scenario is that you get to know the professor a little better while finding out that this project is not a good fit for you. Another possible outcome, though, is that you find a connection that leads to something fruitful. You may be surprised!
~~~~
Postcript. One student has stopped in already this morning to thank me for the encouragement and to say that he is going to contact one of the profs. Don't let a little uncertainty stand in the way of pursuing something you like.
This:
But we see the core message as something else altogether: In pretty much any area of human endeavor, people have a tremendous capacity to improve their performance, as long as they train in the right way. If you practice something for a few hundred hours, you will almost certainly see great improvement ... but you have only scratched the surface. You can keep going and going and going, getting better and better and better. How much you improve is up to you.
... courtesy of Anders Ericsson himself, in a Salon piece adapted from his new book, Peak, (written with Robert Pool). Ericsson himself, author of the oft-cited paper at the source of the rule, which was made famous by Malcolm Gladwell.
I've seen this dynamic play out over the years for many students. They claimed not to be able any good at math or programming, but then they decided to do the work. And they kept getting better. Some ended up with graduate degrees, and most ended up with surprising success in industry.
Looked at from one perspective, the so-called 10,000 Hour Rule is daunting. "Look how far I am from being really good at this..." Many people shrink in the face of this mountain, willing to settle for limits placed on them by their supposed talents. But, as my friend Richard Gabriel often says, talent doesn't determine good you get, only how fast you get good. As I quoted from Art and Fear long ago, "talent is rarely distinguishable, over the long run, from perseverance and lots of hard work".
That's the most important lesson behind Ericsson's research. If we practice right, we will get better and, with even more of the right kind of training, we will keep getting better. Our limits usually lie much farther away than we think.
![]() |
When I read this interview with Louis C.K. last week, the following response spoke to me as a college professor, not as a stand-up comedian:
Can you explain the difference, practically, between the stand-up you were doing at your peak and what you're doing now?
I think I'm a better comedian overall than I was back then, but back then I was better at performing. When you're that greased up onstage, you just have a higher comedy IQ. It's the ability to go on any stage in the country and be perfectly present and able to maneuver the set and have great timing. Some of it is being in physical shape. When you're under pressure or strain, you get dumb, you know? It's why I started working out in boxing gyms, because you watch a guy who's fighting, he's in a terribly arduous moment and he's making intelligent choices. So to me that's when you're 55 minutes deep into your sixth show of the week, in your fifth city of the week. You have to be able to be great right in that moment. You have to be, "You're not going to believe what I'm going to do next." The audience is tired, and you have to have more energy than anyone in the room. You have to be able to control the pace. At my show last night, I was talking to myself a little bit while my mouth was moving delivering material. I was thinking, You're going too fast. Cool it. You have plenty of time and loads ... to say.
It's funny how so many of us, doing so many different things, experience so many of the same ups and downs in our professions. With a few changes to the surface of this story, it sounds like something a college instructor might say ten years on. I've even reached a point where I can talk to myself in an analytical way during a class session or a presentation. I was never as good in 2004 as Louis was, but I feel the same evolution in how I feel about my work in the classroom.
One thing I haven't tried is boxing. (Perhaps that is one of my more intelligent choices.) I have had to make some tough decisions under grueling conditions while running marathons, but those tend to unfold at a slower pace than in the ring. "Everybody has a plan until they get punched in the face."
Like Louis, I'm always trying to get better at teaching in first gear. He is probably more natural than I am in an amped-up state, too. Both are a challenge for me.
In this Dr. Dobbs interview, Alan Kay reveals how he became interested in education as a life's work: as a teenager, he read the issue of Life magazine that introduced Margaret Bourke-White's photos from Buchenwald. Kay says:
That probably was the turning point that changed my entire attitude toward life. It was responsible for getting me interested in education. My interest in education is unglamorous. I don't have an enormous desire to help children, but I have an enormous desire to create better adults.
This desire has caused Kay to explore how children think and learn more deeply than most people do. Our greatest desires sometimes lead us down paths we would not otherwise go.
For some reason, Kay's comments on his enduring involvement in education made me think of this passage from a profile of Ludwig Wittgenstein in the Paris Review:
We all struggle to form a self. Great teaching, Wittgenstein reminds us, involves taking this struggle and engaging in it with others; his whole life was one great such struggle. In working with poor children, he wanted to transform himself, and them.
Wittgenstein wanted to create a better adult of himself and so engaged for six years in "the struggle to form a self" with elementary school students. Let's hope that the students in his charge grew into better adults as well. As Kay says later in the same interview, "Education is a double-edged sword. You have to start where people are, but if you stay there, you're not educating."
A few years ago, I was giving a short presentation about one of our new programs to people from the community and campus. One of the prompts for the talk was how this program would contribute to teaching "critical thinking skills" across the curriculum. I made a mild joke about the idea, wondering aloud which programs on campus taught uncritical thinking skills. Only later did I learn that our new provost, who was in the audience, had just announced a major focus on critical thinking. Fortunately, our new provost had a sense of humor.
I don't believe that we can teach critical thinking in any useful way outside the context of a particular discipline. I do believe, though, that we can teach it as a part of any discipline -- not just in the humanities or liberal arts, which in too many people's minds don't include the sciences. Studies show that these skills don't tend to transfer when we move to a different discipline, but I am convinced that people who learn to think deeply in one discipline are better prepared to learn another discipline than someone who is learning deeply for the first time.
In a recent essay for Inside Higher Ed, John Schlueter offers a new analogy for thinking about critical thinking:
When it comes to thinking skills, it would be much more productive if we stop thinking "transfer" and start thinking "overlap". That is, once thinking skills become more explicitly taught, especially in general education classes, both professors and students will notice how thinking in the context of one domain (say, economics) overlaps with the kind of thinking processes at work in another (biology).
The idea of overlap fits nicely with how I think about these skills. Making thinking skills more explicit in our instruction might enable students to notice intersections and differences across the disciplines they study. That awareness may help them to internalize general strategies that are useful across disciplines, for times when they are in unknown waters, and be aware of possible points of failure in their own thinking.
I'm not sure if this analogy is any easier to operationalize or test than the notion of transfer, but it does give me a different way to think about thinking.
In an interesting article about words and concepts, Elisa Gabbert repeats a familiar sentiment about teaching:
... the physicist Richard Feynman reportedly said, after being asked to prepare a freshman lecture on why spin-1/2 particles obey Fermi-Dirac statistics, "I couldn't reduce it to the freshman level. That means we really don't understand it."
When I read this, my inner Sheldon Cooper thought, "With the data at hand, you really can't draw that conclusion. All you can say with absolute certainty is that you don't understand it."
Actually, I empathize deeply Feynman's sentiment, which has been attributed to many famous people and stated in one form or other by many people who have tried to teach a challenging topic to others. Most teachers have had the experience of trying to explain an idea they think they know cold, only to find themselves stumbling over concepts or relationships that seem so obvious in their expert mind. I experience this feeling almost every semester. When I was a young teacher, such experiences disconcerted me. I soon learned that they were opportunities to understand the world better.
But I think that, at a logical level, people sometimes draw an invalid conclusion from statements of the sort Feynman reportedly made. It's certainly true that if we don't really understand a complex subject, then we probably won't be able to reduce it to a level appropriate for first-year students. But even if you do understand it really well, you still may have difficulty explaining it to beginners.
Teaching involves two parties: the teacher and the learner. Effective teaching requires being able to communicate new ideas in a way that connect with what the learner knows and can do.
To be effective teachers, we need two kinds of knowledge:
This latter understanding comes at two levels. First, we might know a specific individual well and be able to connect to his or her own personal experiences and knowledge. Second, we might understand a group, such as freshman CS students, based on some common background and maturity level.
Teaching individuals one-on-one can be most effective, but it takes a lot of time and doesn't scale well. As a result, we often find ourselves teaching a group of people all at once or writing for a mass audience. Teaching a class means being able to communicate new ideas to a group of students in a way that prepares most or all of them to learn on their own after they leave the classroom and begin to do their individual work.
Most people who try to teach find out that this is a lot harder than it looks. Over time, we begin to learn what a generic freshman CS student knows and is like. We build up a cache of stories for reaching them in different ways. We encounter pedagogical patterns of effective learning and learn ways to implement them in our teaching. We also begin to learn techniques for working with students individually so that, in our office after class, we can drop down from generic teaching to more intimate, one-on-one instruction.
If you want to find out simultaneously how well your students are understanding what you are teaching and how well you understand what you are teaching, let them ask questions. I am often amazed at the questions students ask, and equally amazed at how hard they can be to answer well. On truly glorious days, I surprise myself (and them!) with an answer or story or example that meets their needs perfectly.
However well I understand a topic, it always takes me time to figure out how to communicate effectively with a new audience. Once I understood that this was natural, it allowed me to take some of the pressure to be perfect off myself and get down to the business of learning how to teach and, often, learning computer science at a deeper level.
So, if we can't reduce some topic to the freshman level, it may well mean that we don't really understand it. But it may also mean that you don't yet understand your audience well enough. Figuring out which is true in a given case is yet another challenge that every teacher faces.
That's a great line from Rhett Allain in Telling You the Answer Isn't the Answer:
Students are under the impression that when they are stuck and confused, they are doing something wrong. Think of it this way. What if you went to the gym to work out but you didn't get sweaty and you weren't sore or tired? You would probably feel like you really didn't get any exercise. The same is true for learning. Confusion is the sweat of learning.
As I read the article, I was a little concerned that Allain's story comes from a physics course designed for elementary education majors... Shouldn't education majors already know that learning is hard and that the teacher's job isn't simply to give answers? But then I realized how glad I am that these students have a chance to learn this lesson from a teacher who is patient enough to work through both the science and the pedagogy with them.
One of the biggest challenges for a teacher is designing workouts that ride along a thin line: confusing students just enough to stretch them into learning something valuable, without working them so hard that they become disheartened by the confusion and failure. This is hard enough to do when working with students individually. The larger and more diverse a class is, the more the teacher has to start shooting for the median, designing materials that work well enough often enough for most of the students and then doing triage with students on both ends of the curve.
Another is helping students learn to appreciate the confusion, perhaps even relish it. The payoff is worth the work.
From Learning styles: what does the research say?:
... it seems to me that the important "takeaway" from the research on learning styles is that teachers need to know about learning styles if only to avoid the trap of teaching in the style they believe works best for them. As long as teachers are varying their teaching style, then it is likely that all students will get some experience of being in their comfort zone and some experience of being pushed beyond it. Ultimately, we have to remember that teaching is interesting because our students are so different, but only possible because they are so similar. Of course each of our students is a unique individual, but it is extraordinary how effective well-planned group instruction can be.
Variety is an essential element of great teaching, and one I struggle to design into my courses. Students need both mental comfort and mental challenge. Changing pace every so often allows students to focus in earnest for a while and then consolidate knowledge in slower moments. Finally, changing format occasionally is one way to keep things interesting, and when students are interested, they are more willing to work. And that's where most learning comes from: practice and reflection.
David Andersen offers a rather thorough list of ways that academics might adapt Google's coding practices to their research. It's a good read; check it out! I did want to comment on one small comment, because it relates to a common belief about pair programming:
But, of course, effective pair programming requires a lot of soft skills and compatible pairs. I'm not going to pretend that this solution works everywhere.
I don't pretend that pair programming works everywhere, either, or that everyone should adopt it, but I often wonder about statements like this one. Andersen seems to think that pair programming is a good thing and has helped him and members of his team's to produce high-quality code in the past. Why downplay the practice in a way he doesn't downplay other practices he recommends?
Throughout, the article encourages the use of new tools and techniques. These tools will alter the practice of his students. Some are complex enough that they will need to be taught, and practiced over some length of time, before they become an effective part of the team's workflow. To me, pair programming is another tool to be learned and practiced. It's certainly no harder than learning git...
Pair programming is a big cultural change for many programmers, and so it does require some coaching and extended practice. This isn't much different than the sort of "onboarding" that Andersen acknowledges will be necessary if he is to adopt some of Google's practices successfully in his lab upon upon his return. Pair programming takes practice and time, too, like most new skills.
I have seen the benefit of pair programming in an academic setting myself. Back when I used to teach our introductory course to freshmen, I had students pair every week in the closed lab sessions. We had thirty students in each section, but only fifteen computers in our lab. I paired students in a rotating fashion, so that over the course of fifteen weeks each student programmed with fifteen different classmates. We didn't use a full-on "pure" XP-style of pairing, but what we did was consistent with the way XP encourages pair programming.
This was a good first step for students. They got to know each other well and learned from one another. The better students often helped their partners in the way senior developers can help junior developers progress. In almost all cases, students helped each other find and fix errors. Even though later courses in the curriculum did not follow up with more pair programming, I saw benefits in later courses, in the way students interacted in the lab and in class.
I taught intro again a couple of falls ago after a long time away. Our lab has twenty-eight machines now, so I was not forced to use pair programming in my labs. I got lazy and let them work solo, with cross-talk. In the end, I regretted it. The students relied on me a lot more to help them find errors in their code, and they tended to work in the same insulated cliques throughout the semester. I don't think the group progressed as much as programmers, either, even though some individuals turned out fine.
A first-year intro lab is a very different setting than a research lab full of doctoral students. However, if freshmen can learn to pair program, I think grad students can, too.
Pair programming is more of a social change than a technical change, and that creates different issues than, say, automated testing and style checking. But it's not so different from the kind of change that capricious adherence to style guidelines or other kinds of code review impose on our individuality.
Are we computer scientists so maladjusted socially that we can't -- or can't be bothered -- to learn the new behaviors we need to program successfully in pairs? In my experience, no.
Like Andersen, I'm not advocating that anyone require pair programming in a lab. But: If you think that the benefits of pair programming exceed the cost, then I encourage you to consider having your research students or even your undergrads use it. Don't shy away because someone else thinks it can't work. Why deprive your students of the benefits?
The bottom line is this. Pair programming is a skill to be learned, like many others we teach our students.
I don't usually offer extra credit assignments in my courses, but I did this semester. Students submitted their last scheduled homework early in the last week of classes: an interpreter for a small arithmetic language with local variables. There wasn't enough time left to assign another homework, but I had plenty of ideas for the next version of our interpreter. Students had just learned how to implement both functions and mutable data, so they could add functions, true variables, and sequences of expressions to the language. They could extend their REPL to support top-level definitions and state. They could add primitive data objects to the language, or boolean values and operators. If they added, booleans, they could add an if expression. If they were willing to learn some new Racket, they could load programs from a text file and evaluate them. I had plenty of ideas for them to try!
So I asked, "If I write up an optional assignment, how many of you would take a stab at some extra credit?" Approximately twenty of the twenty-five students present raised their hands.
I just downloaded the submission folder. The actual number of attempts was a complement of the number of hands raised: five. And, with only one exception, the students who tried the extra credit problems are the students who need it least. They are likely to get an A or a high B in the course anyway. The students who most need extra credit (and the extra practice) didn't attempt the extra credit.
SMH, as the kids say these days. But the more I thought about it, the more this made sense.
These reflections have me thinking... If I want to help the students who most need the help, I need to find ways to reach them sooner. I did try one thing earlier this semester that worked well for many students: the opportunity to rewrite one of their exams at home with access to all the course materials. A couple of students wrote surprisingly candid and insightful assessments of why they had performed below par under test conditions and supplied better work on their second attempt. I hope that experience helps them as they prepare for the final exam.
I've been teaching a long time. I still have much to learn.
Michael Fogus, in the latest issue of Read-Eval-Print-λove, writes:
The book in question was Thinking Forth by Leo Brodie (Brodie 1987) and upon reading it I immediately put it into my own "personal pantheon" of influential programming books (along with SICP, AMOP, Object-Oriented Software Construction, Smalltalk Best Practice Patterns, and Programmers Guide to the 1802).
Mr. Fogus has good taste. Programmers Guide to the 1802 is new to me. I guess I need to read it.
The other five books, though, are in my own pantheon influential programming books. Some readers may be unfamiliar with these books or the acronyms, or aware that so many of them are available free online. Here are a few links and details:
![]() |
There is one book on my own list that Fogus did not mention: Paradigms of Artificial Intelligence Programming, by Peter Norvig. It holds perhaps the top position in my personal pantheon. Subtitled "Case Studies in Common Lisp", this book teaches Common Lisp, AI programming, software engineering, and a host of other topics in a classical case studies fashion. When you finish working through this book, you are not only a better programmer; you also have working versions of a dozen classic AI programs and a couple of language interpreters.
Reading Fogus's paragraph of λove for Thinking Forth brought to mind how I felt when I discovered PAIP as a young assistant professor. I once wrote a short blog entry praising it. May these paragraphs stand as a greater testimony of my affection.
I've learned a lot from other books over the years, both books that would fit well on this list (in particular, A Programming Language by Kenneth Iverson) and others that belong on a different list (say, Gödel, Escher, Bach -- an almost incomparable book). But I treasure certain programming books in a very personal way.
In What Unread Books Can Teach Us Oliver Burkeman relates this story about novelist and scholar Umberto Eco:
While researching his own PhD, Eco recalls, he got deeply stuck, and one day happened to buy a book by an obscure 19th-century abbot, mainly because he liked the binding. Idly paging through it, he found, in a throwaway line, a stunning idea that led him to a breakthrough. Who'd have predicted it? Except that, years later, when a friend asked to see the passage in question, he climbed a ladder to a high bookshelf, located the book... and the line wasn't there. Stimulated by the abbot's words, it seems, he'd come up with it himself. You never know where good ideas will come from, even when they come from you.
A person can learn something from a book he or or she has read, even if the book doesn't contain what the person learned. This is a much steadier path to knowledge than resting in the comfort that all information is available at the touch of a search engine.
A person's anti-library helps to make manifest what one does not yet know. As Eco reminds us, humility is an essential ingredient in this prescription.
A colleague sent me the following exchange from his class, with the tag line "Best comments of the day." His students were working in groups to design a Java program for Conway's Game of Life.
Student 1: I can't comprehend what you are saying.
Student 2: The board doesn't have to be rectangular, does it?
Instructor: In Conway's design, it was. But abstractly, no.
Student 3: So you could have a board of different shapes, or you could even have a three-dimensional "board". Each cell knows its neighbors even if we can't easily display it to the user.
Instructor: Sure, "neighbor" is an abstract concept that you can implement differently depending on your need.
Student 2: I knew there was a reason I took linear algebra.
Student 1: Ok. So let's only allow rectangular boards.
Maybe Student 1 still can't comprehend what everyone is saying... or perhaps he or she understands perfectly well and is a pragmatist. YAGNI for the win!
It always makes me happy when a student encounters a situation in which linear algebra is useful and recognizes its applicability unprompted.
I salute all three of these students, and the instructor who is teaching the class. A good day.
This morning I read three pieces with some connection to universities and learning. Each had a one passage that made me smart off silently as I pedaled.
From The Humanities: What's The Big Idea?:
Boyarin describes his own research as not merely interdisciplinary but "deeply post-disciplinary." (He jokes that when he first came to Berkeley, his dream was to be 5 percent in 20 departments.)
Good luck getting tenure that way, dude.
"Deeply post-disciplinary" is a great bit of new academic jargon. Universities are very much organized by discipline. Figuring out how to support scholars who work outside the lines is a perpetual challenge, one that we really should address at scale if we want to enable universities to evolve.
From this article on Bernie Sanders's free college plan:
Big-picture principles are important, but implementation is important, too.
Hey, maybe he just needs a programmer.
Implementing big abstractions is hard enough when the substance is technical. When you throw in social systems and politics, implementing any idea that deviates very far from standard practice becomes almost impossible. Big Ball of Mud, indeed.
From Yours, Isaac Asimov: A Life in Letters:
Being taught is the intellectual analog of being loved.
I'll remind my students of this tomorrow when I give them Exam 3, on syntactic abstraction. "I just called to say 'I love you'."
Asimov is right. When I think back on all my years in school, I feel great affection for so many of my teachers, and I recall feeling their affection for me. Knowledge is not only power, says Asimov; it is happiness. When people help me learn they offer me knew ways to be happy.
( The Foundation Trilogy makes me happy, too.)
Somehow, I recently came across a link to Stay on the Bus, an excerpt from a commencement speech Arno Rafael Minkkinen gave at the New England School of Photography in June 2004. It is also titled "The Helsinki Bus Station Theory: Finding Your Own Vision in Photography". I almost always enjoy reading the thoughts of an artist on his or her own art, and this was no exception. I also usually hear echoes of what I feel about my own arts and avocations. Here are three.
What happens inside your mind can happen inside a camera.
This is one of the great things about any art. What happens inside your mind can happen in a piano, on a canvas, or in a poem. When people find the art that channels their mind best, beautiful things -- and lives -- can happen.
One of the things I like about programming is that is really a meta-art. Whatever happens in your mind can happen inside a camera, inside a piano, on a canvas, or in a poem. Yet whatever happens inside a camera, inside a piano, or on a canvas can happen inside a computer, in the form of a program. Computing is a new medium for experimenting, simulating, and creating.
Teachers who say, "Oh, it's just student work," should maybe think twice about teaching.
Amen. There is no such thing as student work. It's the work our students are ready to make at a particular moment in time. My students are thinking interesting thoughts and doing their best to make something that reflects those thoughts. Each program, each course in their study is a step along a path.
All work is student work. It's just that some of us students are at a different point along our paths.
Georges Braque has said that out of limited means, new forms emerge. I say, we find out what we will do by knowing what we will not do.
This made me think of an entry I wrote many years ago, Patterns as a Source of Freedom. Artists understand better than programmers sometimes that subordinating to a form does not limit creativity; it unleashes it. I love Minkkinen's way of saying this: we find out what we will do by knowing what we will not do. In programming as in art, it is important to ask oneself, "What will I not do?" This is how we discover we will do, what we can do, and even what we must do.
Those are a few of the ideas that stood out to me as I read Minkkinen's address. The Helsinki Bus Station Theory is a useful story, too.
All last spring, I planned to blog about my Programming Languages class but never got around to writing more than a couple of passing thoughts. I figured this spring would be different, yet here we are at the end of Week 9 and I've not even mentioned the course. This is how guys like me fail in personal relationships. We think a lot of good thoughts but don't follow through with our time and attention.
I am tempted to say that this has been a seesaw semester, but on reflection things have gone pretty well. I have a good group of students, most of whom are engaged in the topic and willing to interact in class. (Lack of interest and engagement was a bit of a problem last spring.) We've had fun in and out of class.
The reason it sometimes feels like I'm on a seesaw is that the down moments stand out more in my memory than they deserve to. You'd think that, as long as I've been teaching, I would have figured out how to manage this dynamic more effectively. Maybe it's just a part of being a teacher. We want things to go perfectly for our students, and when they don't we have to wonder why not.
One source of concern this semester has been the average exam score for the first two tests. They have been lower than historic averages in the course. Have I changed my expectations? Have I done something differently in the classroom? After taking a hard look back on my previous semesters' notes and assignments, I think not. My job now is to help this class reach the level I think they can reach. What can I do differently going forward? What can they do differently, and how do I help them do it?
I know they are capable of growth. Early last month, PhD Comics ran a strip titled The Five Most Typed Words in Academia. The winner was "Sorry for the late reply". At the time, the five most common words my students had said to me in the young semester were:
I didn't read the instructions.
For example, the student would say, "This problem was hard because I didn't know how to take the remainder properly." Me: "I gave that information in the instructions for the assignment." Student: "Oh, I didn't read the instructions."
Fortunately, we seem to moved beyond that stage of our relationship, as most students have come to see that the assignment may actually include some clues to help them out. Or maybe they've just stopped telling me that they don't read the instructions. If so, I appreciate them sparing my feelings.
A related problem is a perennial issue in this course: We learn a new technique, and some students choose not to use it. Writing code to process language expressions is a big part of the course, so we study some structural recursion patterns for processing a grammar specified in BNF. Yet a few students insist on whacking away at the problem with nothing more than a cond expression and a brave heart. When I ask them why, they sometimes say:
I didn't have time to use the technique we learned in class, so...
... so they spent twice as long trying to find their way to a working solution. Even when they find one, the code is generally unreadable even to them. Functional programming is hard, they say.
Fortunately, again, many seem to moved beyond this stage and are now listening to their data structures. The result is beautiful code: short, clear, and expressive. Grading such programs is a pleasure.
It recently occurred to me, though, that I have been guilty of the "I didn't have time to use your technique..." error myself. While trying to improve the recursive programming unit of the course over the last few years, I seem to have invented my own version of the Design Recipe from How to Design Programs. In the spirit of Greenspun's Tenth Rule, I have probably reinvented an ad hoc, informally-specified, bug-ridden, pedagogically less sound version of the Design Recipe.
As we used to say in Usenet newsgroups, "Pot. Kettle, Black." Before the next offering of the course, I intend to do my homework properly and find a way to integrate HtDP's well-tested technique into my approach.
These things stand out in my mind, yet I think that the course is going pretty well. And just when I begin to wonder whether I've been letting the class down, a few students stop in my office and say that this is one of their favorite courses CS courses ever. They are looking forward to the compiler course this fall. I'm not sure students realize the effect such words have on their instructors -- at least on this one.
Off to spring break we go. When we get back: lexical addressing and (eventually) a look at stateful programming. Fun and surprise await us all.
In Sledgehammers vs Nut Crackers, Thomas Guest talks about pulling awk of the shelf to solve a fun little string-processing problem. He then shows a solution in Python, one of his preferred general-purpose programming languages. Why bother with languages like awk when you have Python at the ready? Guest writes:
At the outset of this post I admitted I don't generally bother with awk. Sometimes, though, I encounter the language and need to read and possibly adapt an existing script. So that's one reason to bother. Another reason is that it's elegant and compact. Studying its operation and motivation may help us compose and factor our own programs -- programs far more substantial than the scripts presented here, and in which there will surely be places for mini-languages of our own.
As I watch some of my students struggle this semester to master Racket, recursive programming, and functional style, I offer them hope that learning a new language and a new style will make them better Python and Java programmers, even if they never write another Racket or Lisp program again. The more different ways we know how to think about problems and solutions, the more effective we can be as solvers of problems. Of course, Racket isn't a special purpose language, and a few students find they so like the new style that they stick with the language as their preferred tool.
Experienced programmers understand what Guest is saying, but in the trenches of learning, it can be hard to appreciate the value of knowing different languages and being able to think in different ways. My sledgehammer works fine, my students say; why am I learning to use a nutcracker? I have felt that sensation myself.
I try to keep this feeling in mind as my students work hard to master a new way of thinking. This helps me empathize with their struggle, all the while knowing that Racket will shape how some of them think about every program they write in the future.
In Reversing the Tide of Declining Expectations Matthew Butterick exhorts designers to expect more from themselves, as well as from the tools they use. When people expect more, other people sometimes tell them to be patient. There is a problem with being patient:
[P]atience is just another word for "let's make it someone else's problem. ... Expectations count too. If you have patience, and no expectations, you get nothing.
But what if you find the available tools lacking and want something better?
Scientists often face this situation. My physicist friends seem always to be rigging up some new apparatus in order to run the experiments they want to run. For scientists and so many other people these days, though, if they want a new kind of tool, they have to write a computer program.
Butterick tells a story that shows designers can do the same:
Let's talk about type-design tools. If you've been at the conference [TYPO Berlin, 2012], maybe you saw Petr van Blokland and Frederick Berlaen talking about RoboFont yesterday. But that is the endpoint of a process that started about 15 years ago when Erik and Petr van Blokland, and Just van Rossum (later joined by many others) were dissatisfied with the commercial type-design tools. So they started building their own. And now, that's a whole ecosystem of software that includes code libraries, a new font-data format called UFO, and applications. And these are not hobbyist applications. These are serious pieces of software being used by professional type designers.What makes all of this work so remarkable is that there are no professional software engineers here. There's no corporation behind it all. It's a group of type designers who saw what they needed, so they built it. They didn't rely on patience. They didn't wait for someone else to fix their problems. They relied on their expectations. The available tools weren't good enough. So they made better.
That is fifteen years of patience. But it is also patience and expectation in action.
To my mind, this is the real goal of teaching more people how to program: programmers don't have to settle. Authors and web designers create beautiful, functional works. They shouldn't have to settle for boring or cliché type on the web, in their ebooks, or anywhere else. They can make better. Butterick illustrates this approach to design himself with Pollen, his software for writing and publishing books. Pollen is a testimonial to the power of programming for authors (as well as a public tribute to the expressiveness of a programming language).
Empowering professionals to make better tools is a first step, but it isn't enough. Until programming as a skill becomes part of the culture of a discipline, better tools will not always be used to their full potential. Butterick gives an example:
... I was speaking to a recent design-school graduate. He said, "Hey, I design fonts." And I said, "Cool. What are you doing with RoboFab and UFO and Python?" And he said, "Well, I'm not really into programming." That strikes me as a really bad attitude for a recent graduate. Because if type designers won't use the tools that are out there and available, type design can't make any progress. It's as if we've built this great spaceship, but none of the astronauts want to go to Mars. "Well, Mars is cool, but I don't want to drive a spaceship. I like the helmet, though." Don't be that guy. Go the hell to Mars.
Don't be that person. Go to Mars. While you are at it, help the people you know to see how much fun programming can be and, more importantly, how it can help them make things better. They can expect more.
In The Books in My Life, Henry Miller writes about discussing books with an inquisitive friend:
I remember this short period vividly because it was an exercise in humility and self-control on my part. The desire to be absolutely truthful with my friend caused me to realize how very little I knew, how very little I could reveal, though he always maintained that I was a guide and mentor to him. In the brief, the result of those communions was that I began to doubt all that I had blithely taken for granted. The more I endeavored to explain my point of view, the more I floundered. He may have thought I acquitted myself well, but not I. Often, on parting from him, I would continue the inner debate interminably.
I am guessing that most anyone who teaches knows the feeling Miller describes. I feel it all the time.
I'm feeling it again this semester while teaching my Programming Languages and Paradigms course. We're learning Racket as a way to learn to talk about programming languages, and also as a vehicle for learning functional programming. One of my goals this semester is to be more honest. Whenever I find a claim in my lecture notes that sounds like dogma that I'm asking students to accept on faith, I'm trying to explain in a way that connects to their experience. Whenever students ask a question about why we do something in a particular way, I'm trying to help them really to see how the new way is an improvement over what they are used to. If I can't, I admit that it's convention and resolve not to be dogmatic about it with them.
This is a challenge for me. I am prone to dogma, and having programmed functionally in Scheme for a long time, so much of what my students experience learning Racket is deeply compiled in my brain. Why do we do that? I've forgotten, if I ever knew. I may have a vague memory that, when I don't do it that way, chaos ensues. Trust me! Unfortunately, that is not a convincing way to teach. Trying to give better answers and more constructive explanations gives rise to the sort of floundering that Miller talks about. After class, the inner debate continues as I try to figure out what I know and why, so that I can do better.
Some natural teachers may find this easy, but for me, learning to answer questions in a way that really helps students has been a decades-long lesson in humility.
Background
Whenever I teach my compiler course, it seems as if I run across a fun problem or two to implement in our source language. I'm not sure if that's because I'm looking or because I'm just lucky to read interesting blogs and Twitter feeds.
![]() |
For example, during a previous offering, I read on John Cook's blog about Farey's algorithm for approximating real numbers with rational numbers. This was a perfect fit for the sort of small language that my students were writing a compiler for, so I took a stab at implementing it. Because our source language, Klein, was akin to an integer assembly language, I had to unravel the algorithm's loops and assignment statements into function calls and if statements. The result was a program that computed an interesting result and that tested my students' compilers in a meaningful way. The fact that I had great fun writing it was a bonus.
This Semester's Problem
Early this semester, I came across the concept of excellent numbers. A number m is "excellent" if, when you split the sequence of its digits into two halves, a and b, b² - a² equals n. 48 is the only two-digit excellent number (8² - 4² = 48), and 3468 is the only four-digit excellent number (68² - 34² = 3468). Working with excellent numbers requires only integers and arithmetic operations, which makes them a perfect domain for our programming language.
My first encounter with excellent numbers was Brian Foy's Computing Excellent Numbers, which discusses ways to generate numbers of this form efficiently in Perl. Foy uses some analysis by Mark Jason Dominus, written up in An Ounce of Theory Is Worth a Pound of Search, that drastically reduces the search space for candidate a's and b's. A commenter on the Programming Praxis article uses the same trick to write a short Python program to solve that challenge. Here is an adaptation of that program which prints all of the 10-digit excellent numbers:
for a in range(10000, 100000): b = ((4*a**2+400000*a+1)**0.5+1) / 2.0 if b == int(b): print( int(str(a)+str(int(b))) )
I can't rely on strings or real numbers to implement this in Klein, but I could see some alternatives... Challenge accepted!
My Standard Technique
We do not yet have a working Klein compiler in class yet, so I prefer not to write complex programs directly in the language. It's too hard to get subtle semantic issues correct without being able to execute the code. What I usually do is this:
This produces what I hope is a semantically correct program, using only primitives available in Klein.
Finally, I translate the Python program into Klein and run it through my students' Klein front-ends. This parses the code to ensure that it is syntactically correct and type-checks the code to ensure that it satisfies Klein's type system. (Manifest types is the one feature Klein has that Python does not.)
As mentioned above, Klein is something like integer assembly language, so converting to a Klein-like subset of Python means giving up a lot of features. For example, I have to linearize each loop into a sequence of one or more function calls, recursing at some point back to the function that kicks off the loop. You can see this at play in my Farey's algorithm code from before.
I also have to eliminate all data types other than booleans and integers. For the program to generate excellent numbers, the most glaring hole is a lack of real numbers. The algorithm shown above depends on taking a square root, getting a real-valued result, and then coercing a real to an integer. What can I do instead?
![]() |
Not to worry. sqrt is not a primitive operator in Klein, but we have a library function. My students and I implement useful utility functions whenever we encounter the need and add them to a file of definitions that we share. We then copy these utilities into our programs as needed.
sqrt was one of the first complex utilities we implemented, years ago. It uses Newton's method to find the roots of an integer. For perfect squares, it returns the argument's true square root. For all other integers, it returns the largest integer less than or equal to the true root.
With this answer in hand, we can change the Python code that checks whether a purported square root b is an integer using type coercion:
b == int(b)into Klein code that checks whether the square of a square root equals the original number:
isSquareRoot(r : integer, n : integer) : boolean n = r*r
(Klein is a pure functional language, so the return statement is implicit in the body of every function. Also, without assignment statements, Klein can use = as a boolean operator.)
Generating Excellent Numbers in Klein
I now have all the Klein tools I need to generate excellent numbers of any given length. Next, I needed to generalize the formula at the heart of the Python program to work for lengths other than 10.
For any given desired length, let n = length/2. We can write any excellent number m in two ways:
If we set the two m's equal to one another and solve for b, we get:
1 b = -(1 + sqrt[4a2 + 4(10n)a + 1]) 2
Now, as in the algorithm above, we loop through all values for a with n digits and find the corresponding value for b. If b is an integer, we check to see if m = ab is excellent.
The Python loop shown above works plenty fast, but Klein doesn't have loops. So I refactored the program into one that uses recursion. This program is slower, but it works fine for numbers up to length 6:
> python3.4 generateExcellent.py 6 140400 190476 216513 300625 334668 416768 484848 530901
Unfortunately, this version blows out the Python call stack for length 8. I set the recursion limit to 50,000, which helps for a while...
> python3.4 generateExcellent.py 8 16604400 33346668 59809776 Segmentation fault: 11
Cool.
Next Step: See Spot Run
The port to an equivalent Klein program was straightforward. My first version had a few small bugs, which my students' parsers and type checkers helped me iron out. Now I await their full compilers, due at the end of the week, to see it run. I wonder how far we will be able to go in the Klein run-time system, which sits on top of a simple virtual machine.
If nothing else, this program will repay any effort my students make to implement the proper handling of tail calls! That will be worth a little extra-credit...
This programming digression has taken me several hours spread out over the last few weeks. It's been great fun! The purpose of Klein is to help my students learn to write a compiler. But the programmer in me has fun working at this level, trying to find ways to implement challenging algorithms and then refactoring them to run deeper or faster. I'll let you know the results soon.
I'm either a programmer or crazy. Probably both.
In Numbers, Toys and Music, the editors of Plus Magazine interview Manjul Bhargava, who won a 2014 Fields Medal for his work on a problem involving a certain class of square numbers.
Bhargava talked about getting his start on problems of this sort not by studying Gauss's work from nineteenth century, but by reading the work of the seventh century mathematician Brahmagupta in the original Sanskrit. He said it was exciting to read original texts and old translations of original texts from at least two perspectives. Historically, you see an idea as it is encountered and discovered. It's an adventure story. Mathematically, you see the idea as it was when it was discovered, before it has been reinterpreted over many years by more modern mathematicians, using newer, fancier, and often more complicated jargon than was available to the original solver of the problem.
He thinks this is an important step in making a problem your own:
So by going back to the original you can bypass the way of thinking that history has somehow decided to take, and by forgetting about that you can then take your own path. Sometimes you get too influenced by the way people have thought about something for 200 years, that if you learn it that way that's the only way you know how to think. If you go back to the beginning, forget all that new stuff that happened, go back to the beginning. Think about it in a totally new way and develop your own path.
Bhargava isn't saying that we can ignore the history of math since ancient times. In his Fields-winning work, he drew heavily on ideas about hyperelliptic curves that were developed over the last century, as well as computational techniques unavailable to his forebears. He was prepared with experience and deep knowledge. But by going back to Brahmagupta's work, he learned to think about the problem in simpler terms, unconstrained by the accumulated expectations of modern mathematics. Starting from a simpler set of ideas, he was able to make the problem his own and find his own way toward a solution.
This is good advice in computing as well. When CS researchers tell us to read the work of McCarthy, Newell and Simon, Sutherland, and Engelbart, they are channeling the same wisdom that helped Manjul Bhargava discover new truths about the structure of square numbers.
Nearly nine years ago, digital strategist Russell Davies visited the University of Oregon to work with students and faculty in the advertising program and wrote a blog entry about his stint there. Among his reflections on what the students should be doing and learning, he wrote:
We're heading for a multi-disciplinary world and that butts right up against a university business model. If I were preparing myself for my job right now I'd do classes in film editing, poetry, statistics, anthropology, business administration, copyright law, psychology, drama, the history of art, design, coffee appreciation, and a thousand other things. Colleges don't want you doing that, that destroys all their efficiencies, but it's what they're going to have to work out.
I give similar advice to prospective students of computer science: If they intend to take their CS degrees out into the world and make things for people, they will want to know a little bit about many different things. To maximize the possibilities of their careers, they need a strong foundation in CS and an understanding of all the things that shape how software and software-enhanced gadgets are imagined, made, marketed, sold, and used.
Just this morning, a parent of a visiting high school student said, after hearing about all the computer science that students learn in our programs, "So, our son should probably drop his plans to minor in Spanish?" They got a lot more than a "no" out of me. I talked about the opportunities to engage with the growing population of Spanish-speaking Americans, even here in Iowa; the opportunities available to work for companies with international divisions; and how learning a foreign language can help students study and learn programming languages differently. I was even able to throw in a bit about grammars and the role they play in my compiler course this semester.
I think the student will continue with his dream to study Spanish.
I don't think that the omnivorous course of study that Davies outlines is at odds with the "efficiencies" of a university at all. It fits pretty well with a liberal arts education, which even of our B.S. students have time for. But it does call for some thinking ahead, planning to take courses from across campus that aren't already on some department's list of requirements. A good advisor can help with that.
I'm guessing that computer science students and "creatives" are not the only ones who will benefit from seeking a multi-disciplinary education these days. Davies is right. All university graduates will live in a multi-disciplinary world. It's okay for them (and their parents) to be thinking about careers when they are in school. But they should prepare for a world in which general knowledge and competencies buoy up their disciplinary knowledge and help them adapt over time.
Andy Soltis is an American grandmaster who writes a monthly column for Chess Life called "Chess to Enjoy". He has also written several good books, both recreational and educational. In his August 2015 column, Soltis talks about a couple of odd ways in which computers interact with humans in the chess world, ways that raise bigger questions about teaching and the nature of knowledge.
As most people know, computer programs -- even commodity programs one can buy at the store -- now play chess better than the best human players. Less than twenty years ago, Deep Blue first defeated world champion Garry Kasparov in a single game. A year later, Deep Blue defeated Kasparov in a closely contested six-game match. By 2005, computers were crushing Top Ten players with regularity. These days, world champion Magnus Larson is no match for his chess computer.
![]() |
Yet there are still moments where humans shine through. Soltis opens with a story in which two GMs were playing a game the computers thought Black was winning, when suddenly Black resigned. Surprised journalists asked the winner, GM Vassily Ivanchuk, what had happened. It was easy, he said: it only looked like Black was winning. Well beyond the computers' search limits, it was White that had a textbook win.
How could the human players see this? Were they searching deeper than the computers? No. They understood the position at a higher level, using abstractions such as "being in the square" and passed pawns like splitting a King like "pants". (We chessplayers are an odd lot.)
When you can define 'flexibility' in 12 bits,
it will go into the program.
Attempts to program computers to play chess using such abstract ideas did not work all that well. Concepts like king safety and piece activity proved difficult to implement in code, but eventually found their way into the programs. More abstract concepts like "flexibility", "initiative", and "harmony" have proven all but impossible to implement. Chess programs got better -- quickly -- when two things happened: (1) programmers began to focus on search, implementing metrics that could be applied rapidly to millions of positions, and (2) computer chips got much, much faster.
![]() |
The result is that chess programs can beat us by seeing farther down the tree of possibilities than we do. They make moves that surprise us, puzzle us, and even offend our sense of beauty: "Fischer or Tal would have played this move; it is much more elegant." But they win, easily -- except when they don't. Then we explain why, using ideas that express an understanding of the game that even the best chessplaying computers don't seem to have.
This points out one of the odd ways computers relate to us in the world of chess. Chess computers crush us all, including grandmasters, using moves we wouldn't make and many of us do not understand. But good chessplayers do understand why moves are good or bad, once they figure it out. As Soltis says:
And we can put the explanation in words. This is why chess teaching is changing in the computer age. A good coach has to be a good translator. His students can get their machine to tell them the best move in any position, but they need words to make sense of it.
Teaching computer science at the university is affected by a similar phenomenon. My students can find on the web code samples to solve any problem they have, but they don't always understand them. This problem existed in the age of the book, too, but the web makes available so much material, often undifferentiated and unexplained, so, so quickly.
The inverse of computers making good moves we don't understand brings with it another oddity, one that plays to a different side of our egos. When a chess computer loses -- gasp! -- or fails to understand why a human-selected move is better than the moves it recommends, we explain it using words that make sense of human move. These are, of course, the same words and concepts that fail us most of the time when we are looking for a move to beat the infernal machine. Confirmation bias lives on.
Soltis doesn't stop here, though. He realizes that this strange split raises a deeper question:
Maybe it's one that only philosophers care about, but I'll ask it anyway:Are concepts like "flexibility" real? Or are they just artificial constructs, created by and suitable only for feeble, carbon-based minds?
(Philosophers are not the only ones who care. I do. But then, the epistemology course I took in grad school remains one of my two favorite courses ever. The second was cognitive psychology.)
![]() |
We can implement some of our ideas about chess in programs, and those ideas have helped us create machines we can no longer defeat over the board. But maybe some of our concepts are simply be fictions, "just so" stories we tell ourselves when we feel the need to understand something we really don't. I don't think so, the pragmatist in me keeps pushing for better evidence.
Back when I did research in artificial intelligence, I always chafed at the idea of neural networks. They seemed to be a fine model of how our brains worked at the lowest level, but the results they gave did not satisfy me. I couldn't ask them "why?" and receive an answer at the conceptual level at which we humans seem to live. I could not have a conversation with them in words that helped me understand their solutions, or their failures.
Now we live in a world of "deep learning", in which Google Translate can do a dandy job of translating a foreign phrase for me but never tell me why it is right, or explain the subtleties of choosing one word instead of another. Add more data, and it translates even better. But I still want the sort of explanation that Ivanchuk gave about his win or the sort of story Soltis can tell about why a computer program only drew a game because it saddled itself with inflexible pawn structure.
Perhaps we have reached the limits of my rationality. More likely, though, is that we will keep pushing forward, bringing more human concepts and abstractions within the bounds of what programs can represent, do, and say. Researchers like Douglas Hofstadter continue the search, and I'm glad. There are still plenty of important questions to ask about the nature of knowledge, and computer science is right in the middle of asking and answering them.
~~~~
IMAGE 1. The critical position in Ivanchuk-Jobava, Wijk aan Zee 2015, the game to which Soltis refers in his story. Source: Chess Life, August 2015, Page 17.
IMAGE 2. The cover of Andy Soltis's classic Pawn Structure Chess. Source: the book's page at Amazon.com.
IMAGE 3. A bust of Aristotle, who confronted Plato's ideas about the nature of ideals. Source: Classical Wisdom Weekly.
The beginning of a new semester brings with it a crush of new things to read, write, and do, which means it's a good time to remember this advice from Arthur Schopenhauer:
Hence, in regard to our subject, the art of not reading is highly important. This consists in not taking a book into one's hand merely because it is interesting the great public at the time -- such as political or religious pamphlets, novels, poetry, and the like, which make a noise and reach perhaps several editions in their first and last years of existence. Remember rather that the man who writes for fools always finds a large public: and only read for a limited and definite time exclusively the works of great minds, those who surpass other men of all times and countries, and whom the voice of fame points to as such. These alone really educate and instruct.
"The man who writes for fools always finds a large public." You do not have to be part of it. Time is limited. Read something that matters.
The good news for me is that there is a lot of writing about compilers by great minds. This is, of course, also the bad news. Part of my job is to help my students navigate the preponderance of worthwhile readings.
Reading in my role as department head is an altogether different matter...
~~~~
The passage above is from On Books and Reading, which is available via Project Gutenberg, a wonderful source of many great works.
Physicist, science blogger, and pop science author Chad Orzel offered some advice for prospective science students in a post on his Forbes blog last week. Among other things, he suggests that science students learn to program. Orzel is among many physics profs who integrate computer simulations into their introductory courses, using the Matter and Interactions curriculum (which you may recall reading about here in a post from 2007).
I like the way Orzel explains the approach to his students:
When we start doing programming, I tell students that this matters because there are only about a dozen problems in physics that you can readily solve exactly with pencil and paper, and many of them are not that interesting. And that goes double, maybe triple for engineering, where you can't get away with the simplifying spherical-cow approximations we're so fond of in physics. Any really interesting problem in any technical field is going to require some numerical simulation, and the sooner you learn to do that, the better.
This advice complements Astrachan's Law and its variants, which assert that we should not ask students to write a program if they can do the task by hand. Conversely, if they can't solve their problems by hand, then they should get comfortable writing programs that can. (Actually, that's the contrapositive of Astrachan, but "contrapositively" doesn't sound as good.) Programming is a medium for scientists, just as math is, and it becomes more important as they try to solve more challenging problems.
Orzel and Astrachan both know that the best way to learn to program is to have a problem you need a computer to solve. Curricula such as Matter and Interactions draw on this motivation and integrate computing directly into science courses. This is good news for us in computer science. Some of the students who learn how to program in their science courses find that they like it and want to learn more. We have just the courses they need to go deeper.
I concur with all five of Orzel's suggestions for prospective science students. They apply as well to computer science students as to those interested in the physical sciences. When I meet with prospective CS students and their families, I emphasize especially that students should get involved in research. Here is Orzel's take:
While you might think you love science based on your experience in classes, classwork is a pale imitation of actual science. One of my colleagues at Williams used a phrase that I love, and quote all the time, saying that "the hardest thing to teach new research students is that this is not a three-hour lab."
CS students can get involved in empirical research, but they also have the ability to write their own programs to explore their own ideas and interests. The world of open source software enables them to engage the discipline in ways that preceding generations could only have dreamed of. By doing empirical CS research with a professor or working on substantial programs that have users other than the creators, students can find out what computer science is really about -- and find out what they want to devote their lives to.
As Orzel points out, this is one of the ways in which small colleges are great for science students: undergrads can more readily become involved in research with their professors. This advantage extends to smaller public universities, too. In the past year, we have had undergrads do some challenging work on bioinformatics algorithms, motion virtual manipulatives, and system security. These students are having a qualitatively different learning experience than students who are only taking courses, and it is an experience that is open to all undergrad students in CS and the other sciences here.
Among the reasons David Heinemeier Hansson gives in his advice to Fire the Workaholics is that working too much is a sign of bad judgment:
If all you do is work, your value judgements are unlikely to be sound. Making good calls on "is it worth it?" is absolutely critical to great work. Missing out on life in general to put more hours in at the office screams "misguided values".
I agree, in two ways. First, as DHH says, working too much is itself a general indicator that your judgment is out of whack. Second is the more specific case:
For workaholics, doing more work always looks like a reasonable option. As a result, when you are trying to decide, "Should I make this or not?", you never have to choose not to make the something in question -- even when not making it is the right thing to do. That sort of indifferent decision making can be death in any creative endeavor.
One of the things that ten years teaching the same topic has taught Daniel Lemire is that students generally learn more effectively when they learn practical skills first and only then confront the underlying theory:
Though I am probably biased, I find that it is a lot harder to take students from a theoretical understanding to a practical one... than to take someone with practical skills and teach him the theory. My instinct is that most people can more easily acquire an in-depth practical knowledge through practice (since the content is relevant) and they then can build on this knowledge to acquire the theory.
He summarizes the lesson he learned as:
A good example, well understood, is worth a hundred theorems.
My years of teaching have taught me similar lessons. I described a related idea in Examples First, Names Last: showing students examples of an idea before giving it a name.
Lemire's experience teaching XML and my experience teaching a number of topics, including the object-oriented programming example in that blog post, are specific examples of a pattern I usually call Concrete, Then Abstract. I have found this to be an effective strategy in my teaching and writing. I may have picked up the name from Ralph Johnson at ChiliPLoP 2003, where we were part of a hot topic group sketching programming patterns for beginning programmers. Ralph is a big proponent of showing concrete examples before introducing abstract ideas. You can see that in just about every pattern, paper, and book he has written.
My favorite example of "Concrete, Then Abstract" this week is in an old blog entry by Scott Vokes, Making Diagrams with Graphviz. I recently came back to an idea I've had on hold for a while: using Graphviz to generate a diagram showing all of my department's courses and prerequisites. Whenever I return to Graphviz after time away, I bypass its documentation for a while and pull up instead a cached link to Scott's short introduction. I immediately scroll down to this sample program written in Graphviz's language, DOT:
![]() |
... and the corresponding diagram produced by Graphviz:
![]() |
This example makes me happy, and productive quickly. It demonstrates an assortment of the possibilities available in DOT, including several specific attributes, and shows how they are rendered by Graphviz. With this example as a starting point, I can experiment with variations of my own. If I ever want or need more, I dig deeper and review the grammar of DOT in more detail. By that time, I have a pretty good practical understanding of how the language works, which makes remembering how the grammar works easier.
Sometimes, the abstract idea to learn, or re-learn, is a context-free grammar. Sometimes, it's a rule for solving a class of problems or a design pattern. And sometimes, it's a theorem or a theory. In all these cases, examples provide hooks that help us learn an abstract idea that is initially hard for us to hold in our heads.
In the Paris Review's Garrison Keillor, The Art of Humor No. 2, Keillor thinks back to his decision to become a writer, which left him feeling uncertain about himself:
Someone once asked John Berryman, How do you know if something you've written is good? And John Berryman said, You don't. You never know, and if you need to know then you don't want to be a writer.
This doesn't mean that you don't care about getting better. It means that you aren't doing it to please someone else, or at least that your doing it is not predicated on what someone else thinks. You are doing it because that's what you think about. It means that you keep writing, whether it's good or not. That's how you get better.
It's always fun to watch our students wrestle with this sort of uncertainty and come out on the other side of the darkness. Last fall, I taught first-semester freshmen who were just beginning to find out if they wanted to be programmers or computer scientists, asking questions and learning a lot about themselves. This fall, I'm teaching our senior project course, with students who are nearing the end of their time at the university. Many of them think a lot about programming and programming languages, and they will drive the course with their questions and intensity. As a teacher, I enjoy both ends of the spectrum.
![]() |
We all hear the common refrain these days that more people should learn to program, not just CS majors. I agree. If you know how to program, you can make things. Even if you don't write many programs yourself, you are better prepared to talk to the programmers who make things for you. And even if you don't need to talk to programmers, you have expanded your mind a bit to a way of thinking that is changing the world we live in.
But there are two sides to this equation, as Chris Crawford laments in his essay, Fundamentals of Interactivity:
Why is it that our entertainment software has such primitive algorithms in it? The answer lies in the people creating them. The majority are programmers. Programmers aren't really idea people; they're technical people. Yes, they use their brains a great deal in their jobs. But they don't live in the world of ideas. Scan a programmer's bookshelf and you'll find mostly technical manuals plus a handful of science fiction novels. That's about the extent of their reading habits. Ask a programmer about Rabelais, Vivaldi, Boethius, Mendel, Voltaire, Churchill, or Van Gogh, and you'll draw a blank. Gene pools? Grimm's Law? Gresham's Law? Negentropy? Fluxions? The mind-body problem? Most programmers cannot be troubled with such trivia. So how can we expect them to have interesting ideas to put into their algorithms? The result is unsurprising: the algorithms in most entertainment products are boring, predictable, uninformed, and pedestrian. They're about as interesting in conversation as the programmers themselves.We do have some idea people working on interactive entertainment; more of them show up in multimedia than in games. Unfortunately, most of the idea people can't program. They refuse to learn the technology well enough to express themselves in the language of the medium. I don't understand this cruel joke that Fate has played upon the industry: programmers have no ideas and idea people can't program. Arg!
My office bookshelf occasionally elicits a comment or two from first-time visitors, because even here at work I have a complete works of Shakespeare, a thin volume of William Blake (I love me some Blake!), several philosophy books, and "The Brittanica Book of Usage". I really should have some Voltaire here, too. I do cover one of Crawford's bases: a recent blog entry made a software analogy to Gresham's Law.
In general, I think you're more likely to find a computer scientist who knows some literature than you are to find a literary professional who knows much CS. That's partly an artifact of our school system and partly a result of the wider range historically of literature and the humanities. It's fun to run into a colleague from across campus who has read deeply in some area of science or math, but rare.
However, we are all prone to fall into the chasm of our own specialties and miss out on the well-roundedness that makes us better at whatever specialty we practice. That's one reason that, when high school students and their parents ask me what students should take to prepare for a CS major, I tell them: four years of all the major subjects, including English, math, science, social science, and the arts; plus whatever else interests them, because that's often where they will learn the most. All of these topics help students to become better computer scientists, and better people.
And, not surprisingly, better game developers. I agree with Crawford that more programmers should be learn enough other stuff to be idea people, too. Even if they don't make games.
![]() |
I recently learned about the work of Amelia McNamara via this paper published as Research Memo M-2014-002 by the Viewpoints Research Institute. McNamara is attacking an important problem: the gap between programming tools for beginners and programming tools for practitioners. In Future of Statistical Programming, she writes:
The basic idea is that there's a gap between the tools we use for teaching/learning statistics, and the tools we use for doing statistics. Worse than that, there's no trajectory to make the connection between the tools for learning statistics and the tools for doing statistics. I think that learners of statistics should also be doers of statistics. So, a tool for statistical programming should be able to step learners from learning statistics and statistical programming to truly doing data analysis.
"Learners of statistics should also be doers of statistics." -- yes, indeed. We see the same gap in computer science. People who are learning to program are programmers. They are just working at a different level of abstraction and complexity. It's always a bit awkward, and often misleading, when we give novice programmers a different set of tools than we give professionals. Then we face a new learning barrier when we ask them to move up to professional tools.
That doesn't mean that we should turn students loose unprotected in the wilds of C++, but it does require that that we have a pedagogically sound trajectory for making the connection between novice languages and tools and those used by more advanced programmers.
It also doesn't mean that we can simply choose a professional language that is in some ways suitable for beginners, such as Python, and not think any more about the gap. My recent experience reminds me that there is still a lot of complexity to help our students deal with.
McNamara's Ph.D. dissertation explored some of the ways to bridge this gap in the realm of statistics. It starts from the position that the gap should not exist and suggests ways to bridge it, via both better curricula and better tools.
Whenever I experience this gap in my teaching or see researchers trying to make it go away, I think back to Alan Kay's early vision for Smalltalk. One of the central tenets of the Smalltalk agenda was to create a language flexible and rich enough that it could accompany the beginner as he or she grew in knowledge and skill, opening up to a new level each time the learner was ready for something more powerful. Just as a kindergartener learns the same English language used by Shakespeare and Joyce, a beginning programmer might learn the same language as Knuth and Steele, one that opens up to a new level each time the learner is ready.
We in CS haven't done especially good job at this over the years. Matthias Felleisen and the How to Design Programs crew have made perhaps the most successful effort thus far. (See *SL, Not Racket for a short note on the idea.) But this project has not made a lot of headway yet in CS education. Perhaps projects such as McNamara's can help make inroads for domain-specific programmers. Alan Kay may harbor a similar hope; he served as a member of McNamara's Ph.D. committee.
Reading about the unusual ideas in TempleOS reminded me of a piece of advice I received from the great philosopher of epistemology, Dr. Seuss:
If you want to get eggs
you can't buy at a store,
You have to do things
never thought of before.
As Peter T. Hooper learned in Scrambled Eggs Super, discovering or creating something new requires that we think unusual, or even outrageous, thoughts.
... so much so, that I may never catch up. The last year and a half have been crazy, and I simply have not set aside enough time to blog. A big part of the time crunch was teaching three heavy preps in 2014: algorithms, agile software development, and our intro course. It is fitting, then, that blogging about my courses has suffered most of all -- even though, in the moment, I often have plenty to say. Offhand, I can think of several posts for which I once had big plans and for which I still have drafts or outlines sitting in my ideas/ folder:
Thoughts on teaching Python stand out as especially trenchant even many months later. The intro course is so important, because it creates habits and mindsets in students that often long outlive the course. Teaching a large, powerful, popular programming language to beginners in the era of Google, Bing, and DuckDuckGo is a Sisyphean task. No matter how we try to guide the students' introduction to language features, the Almighty Search Engine sits ever at the ready, delivering size and complexity when they really need simple answers. Maybe we need language levels a lá the HtDP folks.
Alas, my backlog is so deep that I doubt I will ever have time to cover much of it. Life goes on, and new ideas pop up every day. Perhaps I can make time the posts outlined above.
Right now, my excitement comes from the prospect of teaching my compilers course again for the first time in two years. The standard material still provides a solid foundation for students who are heading off into the the world of software development. But in the time since I last taught the course, some neat things have happened in the compiler world that will make the course better, if only by putting the old stuff into a more modern context. Consider announcements just this week about Swift, in particular that the source code is being open-sourced and the run-time ported to Linux. The moment these two things happen, the language instantly becomes of greater interest to more of my students. Its openness also makes it more suitable as content for a university course.
So, there will be plenty to blog about, even if I leave my backlog untouched. That's a good thing.
A lot of people have been talking about Kentaro Toyama's Why Technology Will Never Fix Education, which appeared in the Chronicle of Higher Education earlier this week. Here is the money paragraph:
The real obstacle in education remains student motivation. Especially in an age of informational abundance, getting access to knowledge isn't the bottleneck, mustering the will to master it is. And there, for good or ill, the main carrot of a college education is the certified degree and transcript, and the main stick is social pressure. Most students are seeking credentials that graduate schools and employers will take seriously and an environment in which they're prodded to do the work. But neither of these things is cheaply available online.
My wife just completed the second of two long-term substitute teaching assignments this year in a local elementary school, so we have been discussing the daily challenges that teachers face. The combination of student motivation and support at home account for most of the variation in how well students perform and how well any given class operates. I see a similar pattern at the university. By the time students reach me, the long-term effects of strong or weak support at home has crystallized into study habits and skills. The combination of student motivation and study skills account for most of the variation I see in whether students succeed or struggle in their university courses.
This all reminds me of a short passage from Tyler Cowen's book, Average Is Over:
The more information that's out there, the greater the returns to just being willing to sit down and apply yourself. Information isn't what's scarce; it's the willingness to do something with it.
The easy availability of information made possible by technology places a higher premium on the ability of students to sit down and work hard, and the willingness to do so. We can fool ourselves into thinking we know more than we do when we look things up quickly, but many students can just as easily access the same information.
We have found ways to use technology to make information easily available, but we haven't found a way to make motivation an abundant resource. Motivation has to come from within. So do the skills needed to use the information. We can at least help students develop study habits and skills through school and family life, though these are best learned early in life. It is hard for students to change 15-20 years of bad habits after they get to college.
The irony for young people is that, while they live in an era of increasingly available information, the onus rests more than ever on what they do. That is both good news and bad.
I just turned in my grades. For the most part, this is boring paperwork. Neither students nor faculty really like grades; they are something we all have to do as a part of the system. A lot of people would like to change the system and eliminate grades, but every alternative has its own weaknesses. So we all muddle along.
But there is a bigger picture, one which Matt Reed expresses eloquently:
Tolstoy once claimed that there are really only two stories, and we keep telling each of them over and over again: a stranger comes to town, and a hero goes on a quest. In higher education, we live those two stories continuously. Every semester, a new crop of strangers come to town. And every semester, we set a new group of heroes off on their respective quests.
It's May, so we see a new group of young people set of on their own quests. In a few months, we will welcome a new group of strangers. In between are the students who are in the middle of their own "stranger comes to town" story, who will return to us in the fall a little different yet much the same.
That's the big picture.
Today is graduation day for the Class of 2015 at my university. CS students head out into the world, most with a job in hand or nearly so, ready to apply their hard-earned knowledge and skills to all variety of problems. It's an exciting time for them.
This week also brought two other events that have me thinking about the world in which my students my will live and the ways in which we have prepared them. First, on Thursday, the Technology Association of Iowa organized a #TechTownHall on campus, where the discussion centered on creating and retaining a pool of educated people to participate in, and help grow, the local tech sector. I'm a little concerned that the TAI blog says that "A major topic was curriculum and preparing students to provide immediate value to technology employers upon graduation." That's not what universities do best. But then, that is often what employers want and need.
Second, over the last two mornings, I read James Fallows's classic The Case Against Credentialism, from the archives of The Atlantic. Fallows gives a detailed account of the "professionalization" of many lines of work in the US and the role that credentials, most prominently university degrees, have played in the movement. He concludes that our current approach is biased heavily toward evaluating the "inputs" to the system, such as early success in school and other demonstrations of talent while young, rather than assessing the outputs, namely, how well people actually perform after earning their credentials.
Two passages toward the end stood out for me. In one, Fallows wonders if our professionalized society creates the wrong kind of incentives for young people:
An entrepreneurial society is like a game of draw poker; you take a lot of chances, because you're rarely dealt a pat hand and you never know exactly what you have to beat. A professionalized society is more like blackjack, and getting a degree is like being dealt nineteen. You could try for more, but why?
Keep in mind that this article appeared in 1985. Entrepreneurship has taken a much bigger share of the public conversation since then, especially in the teach world. Still, most students graduating from college these days are likely thinking of ways to convert their nineteens into steady careers, not ways to risk it all on the next Amazon or Über.
Then this quote from "Steven Ballmer, a twenty-nine-year-old vice-president of Microsoft", on how the company looked for new employees:
We go to colleges not so much because we give a damn about the credential but because it's hard to find other places where you have large concentrations of smart people and somebody will arrange the interviews for you. But we also have a lot of walk-on talent. We're looking for programming talent, and the degree is in no way, shape, or form very important. We ask them to send us a program they've written that they're proud of. One of our superstars here is a guy who literally walked in off the street. We talked him out of going to college and he's been here ever since.
Who would have guessed in 1985 the visibility and impact that Ballmer would have over the next twenty years? Microsoft has since evolved from the entrepreneurial upstart to the staid behemoth, and now is trying to reposition itself as an important player in the new world of start-ups and mobile technology.
Attentive readers of this blog may recall that I fantasize occasionally about throwing off the shackles of the modern university, which grow more restrictive every year as the university takes on more of the attributes of corporate and government bureaucracy. In one of my fantasies, I organize a new kind of preparatory school for prospective software developers, one with a more modern view of learning to program but also an attention to developing the whole person. That might not satisfy corporate America's need for credentials, but it may well prepare students better for a world that needs poker players as much as it needs blackjack players. But where would the students come from?
So, on a cloudy graduation day, I think about Fallows's suggestion that more focused vocational training is what many grads need, about the real value of a liberal university education to both students and society, and about how we can best prepare CS students participate to in the world. It is a world that needs not only their technical skills but also their understanding of what tech can and cannot do. As a society, we need them to take a prominent role in civic and political discourse.
One final note on the Fallows piece. It is a bit long, dragging a bit in the middle like a college research paper, but opens and closes strongly. With a little skimming through parts of less interest, it is worth a read. Thanks to Brian Marick for the recommendation.
On the way to making a larger point about the role of software in scientific research, Konrad Hinsen writes these beautiful sentences:
Software is just data that can be interpreted as instructions for a computer. One could conceivably write some interpreter that turns previously generated data into software by executing it.
They express one side of one of the great ideas of computer science, the duality of program and data:
This is one of the reasons why it is so important for CS students to study the principles of programming languages, create languages, and build interpreters. These activities help bring this great idea to life and prepare those who understand it to solve problems in ways that are otherwise hard to imagine.
Besides, the duality is a thing of beauty. We don't have to use it as a tool in order to appreciate this sublime truth.
As Hinsen writes, few people outside of computer science (and, sadly, too many within CS) appreciate "the particular status of software as both tool an information carrier and a tool". The same might be said for our appreciation of data, and the role that language plays in bridging the gap between the two.
I wrote on Twitter Thursday [ 1 | 2 ] that I end up modifying my lecture notes every semester, no matter how well done they were the last time I taught the course. From one semester to the next, I find that I am more likely to change the introductions, transitions, and conclusions of a session than the body. The intros, transitions, and conclusions help to situate the material in a given place and time: the context of this semester and this set of students. The content, once refined, tends to stabilize, though occasionally I feel a need to present even it in a different way, to fit the current semester.
Novelist Italo Calvino knew this feeling as well, when he was preparing to be interviewed:
Rarely does an interviewer ask questions you did not expect. I have given a lot of interviews and I have concluded that the questions always look alike. I could always give the same answers. But I believe I have to change my answers because with each interview something has changed either inside myself or in the world. An answer that was right the first time may not be right again the second.
This echoes my experience preparing for lecture. The answer that was right the last time does not seem right again this time. Sometimes, I have changed. With any luck, I have learned new things since the last time I taught the course, and that makes for a better story. Sometimes, the world has changed: a new programming language such as Clojure or Scala has burst onto the scene, or a new trend in industry such as mobile app development has made a different set of issues relevant to the course. I need to tell a different story that acknowledges -- and takes advantage of -- these changes.
Something else always changes for a teacher, too: the students. It's certainly true the students in the class are different every time I teach a course. But sometimes, the group is so different from past groups that the old examples, stories, and answers just don't seem to work. Such has been the case for me this semester. I've had to work quite a bit to understand how my students think and incorporate that into my class sessions and homework assignments. This is part of the fun and challenge of being a teacher.
We have to be careful not to take this analogy too far. Teaching computer science is different from an author giving an interview about his or her life. For one thing, there is a more formal sense of objective truth in the content of, say, a programming language course. An object is still a closure; a closure is still an object that other code can interact with over time. These answers tend to stay the same over time. But even as a course communicates the same fundamental truths from semester to semester, the stories we need to tell about these truths will change.
Ever the fantastic writer, Calvino saw in his interview experience the shape of a new story, a meta-story of sorts:
This could be the basis of a book. I am given a list of questions, always the same; every chapter would contain the answers I would give at different times. ... The changes would then become the itinerary, the story that the protagonist lives. Perhaps in this way I could discover some truths about myself.
This is one of the things I like about teaching. I often discover truths about myself, and occasionally transform myself.
~~~~
The passages quote above come from The Art of Fiction No. 130, Italo Calvino in The Paris Review. It's not the usual Paris Review interview, as Calvino died before the interviewer was done. Instead, it is a pastiche of four different sources. It's a great read nonetheless.
Over the weekend, I read Hypothetical Reasoning and Failures of Disjunctive Inference, a well-sourced article on the problems people have making disjunctive inferences. It made me think about some of the challenges students have learning to program.
Disjunctive inference is reasoning that requires us to consider hypotheticals. A simple example from the article is "the married problem":
Jack is looking at Ann, but Ann is looking at George. Jack is married, but George is not. Is a married person looking at an unmarried person?
- Yes.
- No.
- Cannot be determined.
The answer is yes, of course, which is obvious if we consider the two possible cases for Ann. Most people, though, stop thinking as soon as they realize that the answer hinges on Ann's status. They don't know her status, so they can't know the answer to the question. Even so, most everyone understands the answer as soon as the reasoning is explained to them.
The reasons behind our difficulties handling disjunctive inferences are complex, including both general difficulties we have with hypotheticals and a cognitive bias sometimes called cognitive miserliness: we seek to apply the minimum amount of effort to solving problems and making decisions. This is a reasonable evolutionary bias in many circumstances, but here it is maladaptive.
The article is fascinating and well worth a full read. It points to a number of studies in cognitive psychology that seek to understand how humans behave in the face if disjunctive inferences, and why. It closes with some thoughts on improving disjunctive reasoning ability, though there are no quick fixes.
As I read the article, it occurred to me that learning to program places our students in a near-constant state of hypothetical reasoning and disjunctive inference. Tracing code that contains an if statement asks them to think alternative paths and alternative outcomes. To understand what is true after the if statement executes is disjunctive inference.
Something similar may be true for a for loop, which executes once each for multiple values of a counter, and a while loop, which runs an indeterminate number of times. These aren't disjunctive inferences, but they do require students to think hypothetically. I wonder if the trouble many of my intro CS students had last semester learning function calls involved failures of hypothetical reasoning as much as it involves difficulties with generalization.
And think about learning to debug a program.... How much of that process involves hypotheticals and even full-on disjunctive inference? If most people have trouble with this sort of reasoning even on simple tasks, imagine how much harder it must be for young people who are learning a programming language for the first time and trying to reason about programs that are much more complex than "the married problem"?
Thinking explicitly about this flaw in human thinking may help us teachers do a better job helping students to learn. In the short term, we can help them by giving more direct prompts for how to reason. Perhaps we can also help them learn to prompt themselves when faced with certain kinds of problems. In the longer term, we can perhaps help them to develop a process for solving problems that mitigates the bias. This is all about forming useful habits of thought.
If nothing else, reading this article will help me be slower to judge my students's work ethic. What looks like laziness is more likely a manifestation of a natural bias to exert the minimum amount of effort to solving problems. We are all cognitive misers to a certain extent, and that serves us well. But not always when we are writing and debugging programs.
Think differently, of course. But be humble. These attitudes go hand-in-hand.
To make money in the markets, you have to think independently and be humble. You have to be an independent thinker because you can't make money agreeing with the consensus view, which is already embedded in the price. Yet whenever you're betting against the consensus there's a significant probability you're going to be wrong, so you have to be humble.
This applies equally well to doing research. You can't make substantial progress with the conventional wisdom, because it defines and limits the scope of the solution. So think differently. But when you leave the safety of conventional wisdom, you find yourself working in an immense space of ideas. There is a significant chance that you will be wrong a lot. So be humble.
(The quote is from Learn or Die: Using Science to Build a Leading-Edge Learning Organization by Ray Dalio.)
After reading What Wittgenstein Learned from Teaching Elementary School in The Paris Review, I think that, while Wittgenstein seems to have had some ideas for making elementary education better, I probably wouldn't want to have him as my teacher. This passage, though, really stuck with me:
We all struggle to form a self. Great teaching, Wittgenstein reminds us, involves taking this struggle and engaging in it with others; his whole life was one great such struggle. In working with poor children, he wanted to transform himself, and them.
The experience of teaching grade school for six years seems to have changed Wittgenstein and how he worked. In his later work, he famously moved away from the idea that language could only function by picturing objects in the world. There is no direct evidence that working with children was the impetus for this shift, but "his later work is full of references to teaching and children". In particular, Philosophical Investigations begins its investigation of "the essence of language" by discussing how children learn language.
And Wittgenstein is sometimes explicit about the connection; he once said that in considering the meaning of a word, it's helpful to ask, "How would one set about teaching a child to use this word?"
We all know that teaching can change the student, but foremost it changes the teacher. Wittgenstein seems to have understood that this is a part of the universal task of forming one's self.
In How Stephen King Teaches Writing, Jessica Lahey asks Stephen King why we should teach grammar:
Lahey: You write, "One either absorbs the grammatical principles of one's native language in conversation and in reading or one does not." If this is true, why teach grammar in school at all? Why bother to name the parts?King: When we name the parts, we take away the mystery and turn writing into a problem that can be solved. I used to tell them that if you could put together a model car or assemble a piece of furniture from directions, you could write a sentence. Reading is the key, though. A kid who grows up hearing "It don't matter to me" can only learn
doesn't if he/she reads it over and over again.
There are at least three nice ideas in King's answer.
All of these are true of teaching programmers, too, in their own way.
Finding ways to integrate design recipes, patterns, and case studies is an area I'd like to explore more in my own teaching.
After a long break from playing chess, I recently played a few games at the local club. Playing a couple of games twice in the last two weeks has reminded me that I am very rusty. I've only made two horrible blunders in four games, but I have made many small mistakes, the kind of errors that accumulate over time and make a position hard to defend, even untenable. Having played better in years past, these inaccuracies are irksome.
Still, I managed to win all four games. As I've watched games at the club, I've noticed that most games are won by the player who makes the second-to-last blunder. Most of the players are novices, and they trade mistakes: one player leaves his queen en prise; later, his opponent launches an underprepared attack that loses a rook; then the first player trades pieces and leaves himself with a terrible pawn structure -- and so on, the players trading weak or bad moves until the position is lost for one of them.
My secret thus far has been one part luck, one part simple strategy: winning by not losing.
This experience reminded me of a paper called The Loser's Game, which in 1975 suggested that it was no longer possible for a fund manager to beat market averages over time because most of the information needed to do well was available to everyone. To outperform the market average, a fund manager has to profit from mistakes made by other managers, sufficiently often and by a sufficient margin to sustain a long-term advantage. Charles Ellis, the author, contrasts this with the bull markets of the 1960s. Then, managers made profits based on the specific winning investments they made; in the future, though, the best a manager could hope for was not to make the mistakes that other investors would profit from. Fund management had transformed from being a Winner's Game to a Loser's Game.
![]() |
Ellis drew his inspiration from another world, too. Simon Ramo had pointed out the differences between a Winner's Game and a Loser's Game in Extraordinary Tennis for the Ordinary Tennis Player. Professional tennis players, Ramo said, win based on the positive actions they take: unreturnable shots down the baseline, passing shots out of the reach of a player at the net, service aces, and so on. We duffers try to emulate our heroes and fail... We hit our deep shots just beyond the baseline, our passing shots just wide of the sideline, and our killer serves into the net. It turns out that mediocre players win based on the errors they don't make. They keep the ball in play, and eventually their opponents make a mistake and lose the point.
Ramo saw that tennis pros are playing a Winner's Game, and average players are playing a Loser's Game. These are fundamentally different games, which reward different mindsets and different strategies. Ellis saw the same thing in the investing world, but as part of a structural shift: what had once been a Winner's Game was now a Loser's Game, to the consternation of fund managers whose mindset is finding the stocks that will earn them big returns. The safer play now, Ellis says, is to minimize mistakes. (This is good news for us amateurs investors!)
This is the same phenomenon I've been seeing at the chess club recently. The novices there are still playing a Loser's Game, where the greatest reward comes to those who make the fewest and smallest mistakes. That's not very exciting, especially for someone who fancies herself to be Adolf Anderssen or Mikhail Tal in search of an immortal game. The best way to win is to stay alive, making moves that are as sound as possible, and wait for the swashbuckler across the board from you to lose the game.
What does this have to do with learning to program? I think that, in many respects, learning to program is a Loser's Game. Even a seemingly beginner-friendly programming language such as Python has an exacting syntax compared to what beginners are used to. The semantics seem foreign, even opaque. It is easy to make a small mistake that chokes the compiler, which then spews an error message that overwhelms the new programmer. The student struggles to fix the error, only to find another error waiting somewhere else in the code. Or he introduces a new error while eliminating the old one, which makes even debugging seem scary. Over time, this can dishearten even the heartiest beginner.
What is the best way to succeed? As in all Loser's Games, the key is to make fewer mistakes: follow examples closely, pay careful attention to syntactic details, and otherwise not stray too far from what you are reading about and using in class. Another path to success is to make the mistakes smaller and less intimidating: take small steps, test the code frequently, and grow solutions rather than write them all at once. It is no accident that the latter sounds like XP and other agile methods; they help to guard us from the Loser's Game and enable us to make better moves.
Just as playing the Loser's Game in tennis or investing calls for a different mindset, so, too does learning to program. Some beginners seem to grok programming quickly and move on to designing and coding brilliantly, but most of us have to settle in for a period of discipline and growth. It may not be exciting to follow examples closely when we want to forge ahead quickly to big ideas, but the alternative is to take big shots and let the compiler win all the battles.
Unlike tennis and Ellis's view of stock investing, programming offers us hope: Nearly all of us can make the transition from the Loser's Game to the Winner's Game. We are not destined to forever play it safe. With practice and time, we can develop the discipline and skills necessary to making bold, winning moves. We just have to be patient and put time and energy into the process of becoming less mistake-prone. By adopting the mindset needed to succeed in a Loser's Game, we can eventually play the Winner's Game.
I'm not too sure about the phrases "Loser's Game" and "Winner's Game", but I think that this analogy can help novice programmers. I'm thinking of ways that I can use it to help my students survive until they can succeed.
Via a circuitous walk of web links, this morning I read an old piece called Two More Things to Unlearn from School, which opens:
I suspect the *most* dangerous habit of thought taught in schools is that even if you don't really understand something, you should parrot it back anyway. One of the most fundamental life skills is realizing when you are confused, and school actively destroys this ability -- teaches students that they "understand" when they can successfully answer questions on an exam, which is very very very far from absorbing the knowledge and making it a part of you. Students learn the habit that eating consists of putting food into mouth; the exams can't test for chewing or swallowing, and so they starve.
Schools don't teach this habit explicitly, but they allow it to develop and grow without check. This is one of the things that makes computer science hard for students. You can only get so far by parroting back answers you don't understand. Eventually, you have to write a program or prove an assertion, and all the memorization of facts in the world can't help you.
That said, though, I think students know very well when when they don't understand something. Many of my students struggle with the things they don't understand. But, as Yudkowsky says, they face the time constraints of a course fitting into a fifteen-week window and of one course competing with others for their time. The habit have they developed over the years is to think that, in the end, not understanding is okay, or at least an acceptable outcome of the course. As long as they get the grade they need to move on, they'll have another chance to get it later. And maybe they won't ever need to understand this thing ever again...
One of the best things we can do for students is to ask them to make things and to discuss with them the things they made, and how they made them. This is a sort of intellectual work that requires a deeper understanding than merely factual. It also forces them to consider the choices and trade-offs that characterize real knowledge.
With an expressive type system for its teaching
languages,
HtDP
could avoid this problem to some
extent, but adding such rich types would also take
the fun out of programming.
As we approach the midpoint of the semester, Matthias Felleisen's Turing Is Useless strikes a chord in me. My students have spent the last two months learning a little Racket, a little functional programming, and a little about how to write data-driven recursive programs. Yet bad habits learned in their previous courses, or at least unchecked by what they learned there, have made the task harder for many of them than it needed to be.
The essay's title plays off the Church-Turing thesis, which asserts that all programming languages have the same expressive power. This powerful claim is not good news for students who are learning to program, though:
Pragmatically speaking, the thesis is completely useless at best -- because it provides no guideline whatsoever as to how to construct programs -- and misleading at worst -- because it suggests any program is a good program.
With a Turing-universal language, a clever student can find a way to solve any problem with some program. Even uninspired but persistent students can tinker their way to a program that produces the right answers. Unfortunately, they don't understand that the right answers aren't the point; the right program is. Trolling StackOverflow will get them a program, but too often the students don't understand whether it is a good or bad program in their current situation. It just works.
I have not been as faithful to the HtDP approach this semester as I probably should have been, but I share its desire to help students to design programs systematically. We have looked at design patterns that implement specific strategies, not language features. Each strategy focuses on the definition of the data being processed and the definition of the value being produced. This has great value for me as the instructor, because I can usually see right away why a function isn't working for the student the way he or she intended: they have strayed from the data as defined by the problem.
This is also of great value to some of my students. They want to learn how to program in a reliable way, and having tools that guide their thinking is more important than finding yet another primitive Racket procedure to try. For others, though "garage programming" is good enough; they just want get the job done right now, regardless of which muscles they use. Design is not part of their attitude, and that's a hard habit to break. How use doth breed a habit in a student!
Last semester, I taught intro CS from what Felleisen calls a traditional text. Coupled that experience with my experience so far this semester, I'm thinking a lot these days about how we can help students develop a design-centered attitude at the outset of their undergrad courses. I have several blog entries in draft form about last semester, but one thing that stands out is the extent to which every step in the instruction is driven by the next cool programming construct. Put them all on the table, fiddle around for a while, and you'll make something that works. One conclusion we can draw from the Church-Turing thesis is that this isn't surprising. Unfortunately, odds are any program created this way is not a very good program.
~~~~~
(The sentence near the end that sounds like Shakespeare is. It's from The Two Gentlemen of Verona, with a suitable change in noun.)
The team is in the weight room. Your coach wants you to work on your upper-body strength and so has assigned you a regimen that includes bench presses and exercises for your lats and delts. You are on the bench, struggling."Hey, coach, this is pretty hard. Can I use my legs to help lift this weight?"
The coach shakes his head and wonders what he is going to do with you.
Using your legs is precisely not the point. You need to make your other muscles stronger. Stick to the bench.
If athletics isn't your thing, we can tell this story with a piano. Or a pen and paper. Or a chessboard.
I love this answer from Matthias Felleisen on the Racket users mailing list today:
The book emphasizes systematic design. You can solve this specific problem with brute force regular-expression matching in a few lines of code. The question is whether you want to learn to solve problems or copy code from mailing lists and StackOverflow without understanding what's really going on.
Students today aren't much different from the students in the good old days. But the tools and information so readily available to them make it a lot easier for them to indulge their baser urges. In the good old days, we had to work hard to get good grades and not understand what we were doing.
... write for undergraduates. Why?
Last fall, Steven Pinker took a stab at explaining why academics stink at writing. He hypothesizes that cognitive science and human psychology explain much of the problem. Experts often find it difficult to imagine that others do not know what experts know, which Pinker calls the curse of knowledge. They work around the limitations of short-term memory by packaging ideas into bigger and more abstract units, often called chunking. Finally, they tend to think about the things they understand well in terms of how they use them, not in terms of what they look like, a transition called functional fixity.
Toward the end of the article, Pinker summarizes:
The curse of knowledge, in combination with chunking and functional fixity, helps make sense of the paradox that classic style is difficult to master. What could be so hard about pretending to open your eyes and hold up your end of a conversation? The reason it's harder than it sounds is that if you are enough of an expert in a topic to have something to say about it, you have probably come to think about it in abstract chunks and functional labels that are now second nature to you but are still unfamiliar to your readers--and you are the last one to realize it.
Most academics aren't trying to write bad prose. They simply don't have enough practice writing good prose.
When Calvin explained to Hobbes, "With a little practice, writing can be an intimidating and impenetrable fog," he got it backward. Fog comes easily to writers; it's the clarity that requires practice. The naïve realism and breezy conversation in classic style are deceptive, an artifice constructed through effort and skill.
Wanting to write better is not sufficient. Exorcising the curse requires writers to learn new skills and to practice. One of the best ways to see if the effort is paying off is to get feedback: show the work to real readers and see if they can follow it.
That's where undergraduates come in. If you want to become a better writer or a better speaker, teach undergraduates regularly. They are about as far removed as you can get from an expert while still having an interest in the topic and some inclination to learn more about it.
When I write lecture notes for my undergrads, I have to eliminate as much jargon as possible. I have to work hard to put topics into the best order for learners, not for colleagues who are also expert in the area. I have to find stories to illuminate ideas, and examples to illustrate them. When I miss the intended mark on any of these attempts, my students will let me know, either through their questions or through their inability to perform as I expected. And then I try again.
My lecture notes are far from perfect, but they are always much better after a few iterations teaching a course than they are the first time I do. The weakest parts tend to be for material I'm adding to the course for the first time; the best parts tend to be revisions of existing material. These facts are no surprise to any writer or presenter, of course. Repetition and effort are how we make things better.
Even if you do not consider yourself a teacher by trade, if you want to improve your ability to communicate science, teach undergrads. Write lecture notes and explanations. Present to live students and monitor lab sessions. The students will reward you with vigorous feedback. Besides, they are good people to get to know.
Many people have been discussing Chris Granger's recent essay Coding is not the New Literacy, and most seem to approve of his argument. Reading it brought to my mind this sentence from Alan Kay in VPRI Memo M-2007-007a, The Real Computer Revolution Hasn't Happened Yet:
Literacy is not just being able to read and write, but being able to deal fluently with the kind of ideas that are important enough to write about and discuss.
Literacy requires both the low-level skills of reading and writing and the higher-order capacity for using them on important ideas.
That is one thing that makes me uneasy about Granger's argument. It is true that teaching people only low-level coding skills won't empower them if they don't know how to use them to use them fluently to build models that matter. But neither will teaching them how to build models without giving them access to the programming skills they need to express their ideas beyond what some tool gives them.
Like Granger, though, I am also uneasy about many of the learn-to-code efforts. Teaching people enough Javascript or Ruby to implement a web site out of the box skips past the critical thinking skills that people need to use computation effectively in their world. They may be "productive" in the short term, but they are also likely to hit a ceiling pretty soon. What then? My guess: they become frustrated and stop coding altogether.
![]() |
We sometimes do a better job introducing programming to kids, because we use tools that allow students to build models they care about and can understand. In the VPRI memo, Kay describes experiences teaching elementary school, students to use eToys to model physical phenomena. In the end, they learn physics and the key ideas underlying calculus. But they also learn the fundamentals of programming, in an environment that opens up into Squeak, a flavor of Smalltalk.
I've seen teachers introduce students to Scratch in a similar way. Scratch is a drag-and-drop programming environment, but it really is a open-ended and lightweight modeling tool. Students can learn low-level coding skills and higher-level thinking skills in tandem.
That is the key to making Granger's idea work in the best way possible. We need to teach people how to think about and build models in a way that naturally evolves into programming. I am reminded of another quote from Alan Kay that I heard back in the 1990s. He reminded us that kindergarteners learn and use the same language that Shakespeare used It is possible for their fluency in the language to grow to the point where they can comprehend some of the greatest literature ever created -- and, if they possess some of Shakepeare's genius, to write their own great literature. English starts small for children, and as they grow, it grows with them. We should aspire to do the same thing for programming.
![]() |
Granger reminds us that literacy is really about composition and comprehension. But it doesn't do much good to teach people how to solidify their thoughts so that they can be written if they don't know how to write. You can't teach composition until your students know basic reading and writing.
Maybe we can find a way to teach people how to think in terms of models and how to implement models in programs at the same time, in a language system that grows along with their understanding. Granger's latest project, Eve, may be a step in that direction. There are plenty of steps left for us to take in the direction of languages like Scratch, too.
Last Thursday, John Cook tweeted:
Contrary to the Zen proverb, there may be more possibilities in the expert's mind than in the beginner's.
This summed up nicely one of the themes in my Programming Languages course that afternoon. Some students come into the course knowing essentially only one language, say Python or Ada. Others come knowing several languages well, including their intro language, Java, C, and maybe a language they learned on the job, such as Javascript or Scala.
Which group do you think has a larger view of what a programming language can be? The more knowledgable, to be sure. This is especially true when their experience includes languages from different styles: procedural, object-oriented, functional, and so on.
Previous knowledge affects expectations. Students coming directly out of their first year courses are more likely to imagine that all languages are similar to what they already know. Nothing in their experience contradicts that idea.
Does this mean that the Zen notion of beginner's mind is wrongheaded? Not at all. I think an important distinction can be made between analysis and synthesis. In a context where we analyze languages, broad experience is more valuable than lack of experience, because we are able to bring to our seeing a wider range of possibilities. That's certainly my experience working with students over the years.
However, in a context, where we create languages, broad experience can be an impediment. When we have seen many different languages, it it can be difficult to create something that looks much different from the languages what we've already seen. Something in our minds seems to pull us toward an existing language that already solves the constraint they are struggling with. Someone else has already solved this problem; their solution is probably best.
This is also my experience working with students over the years. My freshmen will almost always come up with a fresher language design than my seniors. The freshmen don't know much about languages yet, and so their minds are relatively unconstrained. (Fantastically so, sometimes.) The seniors often seem to end up with something that is superficially new but, at its core, thoroughly predictable.
The value of "Zen mind, beginner's mind" also follows a bit from the distinction between expertise and experience. Experts typically reach a level of where they solve problem using heuristics to solve problems. There patterns and shortcuts are efficient, but they also tend to be "compiled" and not all that open to critical examination. We create best when we are able to modify, rearrange, and discard, and that's harder to do when our default mode of thinking is in pre-compiled units.
It should not bother us that useful adages and proverbs contradict one another. The world is complex. As Bokononists say, Busy, busy, busy.
In Mathematics, Live: A Conversation with Laura DeMarco and Amie Wilkinson, Amie Wilkinson recounts the pivotal moment when she knew she wanted to be a mathematician. Insecure about her abilities in mathematics, unsure about what she wanted to do for a career, and with no encouragement, she hadn't applied to grad school. So:
I came back home to Chicago, and I got a job as an actuary. I enjoyed my work, but I started to feel like there was a hole in my existence. There was something missing. I realized that suddenly my universe had become finite. Anything I had to learn for this job, I could learn eventually. I could easily see the limits of this job, and I realized that with math there were so many things I could imagine that I would never know. That's why I wanted to go back and do math. I love that feeling of this infinite horizon.
After having written software for an insurance company during the summers before and after my senior year in college, I knew all too well the "hole in my existence" that Wilkinson talks about, the shrinking universe of many industry jobs. I was deeply interested in the ideas I had found in Gödel, Escher, Bach, and in the idea of creating an intelligent machine. There seemed no room for those ideas in the corporate world I saw.
I'm not sure when the thought of graduate school first occurred to me, though. My family was blue collar, and I didn't have much exposure to academia until I got to Ball State University. Most of my friends went out to get jobs, just like Wilkinson. I recall applying for a few jobs myself, but I never took the job search all that seriously.
At least some of the credit belongs to one of my CS professors, Dr. William Brown. Dr. Brown was an old IBM guy who seemed to know so much about how to make computers do things, from the lowest-level details of IBM System/360 assembly language and JCL up to the software engineering principles needed to write systems software. When I asked him about graduate school, he talked to me about how to select a school and a Ph.D. advisor. He also talked about the strengths and weaknesses of my preparation, and let me know that even though I had some work to do, I would be able to succeed.
These days, I am lucky even to have such conversations with my students.
For Wilkinson, DeMarco and me, academia was a natural next step in our pursuit of the infinite horizon. But I now know that we are fortunate to work in disciplines where a lot of the interesting questions are being asked and answers by people working in "the industry". I watch with admiration as many of my colleagues do amazing things while working for companies large and small. Computer science offers so many opportunities to explore the unknown.
Reading Wilkinson's recollection brought a flood of memories to mind. I'm sure I wasn't alone in smiling at her nod to finite worlds and infinite horizons. We have a lot to be thankful for.
Dan Meyer quotes Scott Farrand in WTF Math Problems:
Anything that makes students ask the question that you plan to answer in the lesson is good, because answering questions that haven't been asked is inherently uninteresting.
My challenge this semester: getting students to ask questions about the programming languages they use and how they work. I myself have many questions about languages! My experience teaching our intro course last semester reminded me that what interests me (and textbook authors) doesn't always interest my students.
If you have any WTF? problems for a programming languages course, please share.
Military Operations Orders are programs that are executed by units. Code re-use and other software engineering principles applied regularly to these.
An alumnus of my department, a CS major-turned-military officer, wrote those lines in an e-mail responding to my recent post, A Little CS Would Help a Lot of College Grads. Contrary to what many people might imagine, he has found what he learned in computer science to be quite useful to him as an Army captain. And he wasn't even a programmer:
One of the biggest skills I had over my peers was organizing information. I wasn't writing code, but I was handling lots of data and designing systems for that data. Organizing information in a way that was easy to present to my superiors was a breeze and having all the supporting data easily accessible came naturally to me.
Skills and principles from software engineering and project development apply to systems other than software. They also provide a vocabulary for talking about ideas that non-programmers encounter every day:
I did introduce my units to the terms border cases, special cases, and layers of abstraction. I cracked a smile every time I heard those terms used in a meeting.
Excel may not be a "real programming language", but knowing the ways in which it is a language can make managers of people and resources more effective at what they do.
For more about how a CS background has been useful to this officer, check out CS Degree to Army Officer, a blog entry that expands on his experiences.
One of the more important essays I read in 2014 was Michael Nielsen's Reinventing Explanation. In it, Nielsen explores how we might design media that help us explain scientific ideas better than we are able with our existing tools.
... it's worth taking non-traditional media seriously not just as a vehicle for popularization or education, which is how they are often viewed, but as an opportunity for explanations which can be, in important ways, deeper.
This essay struck me deep. Nielsen wants us to consider how we might take what we have learned using non-traditional media to popularize and educate and use it to think about how to explain more deeply. I think that learning how to use non-traditional media to explain more deeply will help us change the way we teach and learn.
In too many cases, new technologies are used merely as substitutes for old technology. The web has led to an explosion of instructional video aimed at all levels of learners. No matter how valuable these videos are, most merely replace reading a textbook or a paper. But computational technology enables us to change the task at hand and even redefine what we do. Alan Kay has been telling this story for decades, pointing us to the work of Ivan Sutherland and many others from the early days of computing.
Nielsen points to Bret Victor as an example of someone trying to develop tools that redefine how we think. As Victor himself says, he is following in the grand tradition of Kay, Sutherland, et al. Victor's An Ill-Advised Personal Note about "Media for Thinking the Unthinkable" is an especially direct telling of his story.
Vi Hart is another. Consider her recent Parable of the Polygons, created with Nicky Case, which explains dynamically how local choices and create systemic bias. This simulation uses computation to help people think differently about an idea they might not understand as viscerally from a traditional explanation. Hart has a long body of working using visualization to explain differently, and the introduction of computing extends the depth of her approach.
Over the last few weeks, I have felt myself being pulled by Nielsen's essay and the example of people such as Victor and Hart to think more about how we might design media that help us to teach and explain scientific ideas more deeply. Reinventing explanation might help us reinvent education in a way that actually matters. I don't have a research agenda yet, but looking again at Victor's work is a start.
I would love to see more CS majors, but not everyone should major in CS. I do think that most university students could benefit from learning a little programming. There are plenty of jobs not only for CS and math grads, but also for other majors who have CS and math skills:
"If you're an anthropology major and you want to get a marketing job, well, guess what? The toughest marketing jobs to fill require SQL skills," Sigelman says. "If you can ... along the peripheries of your academic program accrue some strong quantitative skills, you'll still have the advantage [in the job market]." Likewise, some legal occupations (such as intellectual property law) and maintenance and repair jobs stay open for long periods of time, according to the Brookings report, if they require particular STEM skills.
There is much noise these days about the importance of STEM, both for educated citizens and for jobs, jobs, jobs. STEM isn't an especially cohesive category, though, as the quoted Vox article reminds us, and even when we look just at economic opportunity, it misleads. We don't need more college science graduates from every STEM discipline. We do need more people with the math and CS skills that now pervade the workplace, regardless of discipline. As Kurtzleben says in the article, "... characterizing these skill shortages as a broad STEM crisis is misleading to students, and has distorted the policy debate."
... software is hard. It's harder than
anything else I've ever had to do.
--
Donald Knuth
As students were leaving my final CS 1 lab session of the semester, I overheard two talking about their future plans. One student mentioned that he was changing his major to actuarial science. I thought, wow, that's a tough major. How is a student who is struggling with basic programming going to succeed there?
When I checked on his grades, though, I found that he was doing fine in my course, about average. I also remembered that he had enjoyed best the programming exercises that computed terms of infinite arithmetic series and other crazy mathematical values that his classmates often found impenetrable. Maybe actuarial science, even with some hard math, will be a good fit for him.
It really shouldn't surprise us that some students try computer science and decide to major in something else, even something that looks hard to most people. Teaching CS 1 again this semester after a long break reminded me just how much we expect from the students in our introductory course:
In a single course, we expect students to perform tasks in all three of these modes, while mastering a heavy load of details. We expect them to learn by deduction, induction, and abduction, covering many abstract ideas and many concrete details. Many disciplines have challenging first courses, but CS 1 requires an unusual breadth of intellectual tools.
Yes, we can improve our students' experience with careful pedagogy. Over the last few decades we've seen many strong efforts. And yes, we can help students through the process with structural support, emotional support, and empathy. In the end, though, we must keep this in mind: CS 1 is going to be a challenge for most students. For many, the rewards will be worth the s