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.
This week, I read a cool article that covered a lot of ground: Feynman diagrams, experiments at the Large Hadron Collider, algebraic geometry, pendulums, and periods. This paragraph even made me think about software:
That same answer -- the unique thing at the center of all these cohomology theories -- was what Grothendieck called a "motive". "In music it means a recurring theme. For Grothendieck a motive was something which is coming again and again in different forms, but it's really the same," said Pierre Cartier, a mathematician at the Institute of Advanced Scientific Studies outside Paris and a former colleague of Grothendieck's.
Something that comes again and again in different forms but is really the same thing... That sounds like a design pattern. Software patterns are quite different than numeric periods in algebra and geometry, but the idea feels familiar. The analogy to music feels familiar, too.
The article then says:
Motives are in a sense the fundamental building blocks of polynomial equations, in the same way that prime factors are the elemental pieces of larger numbers.
This is how I have often thought of elementary design patterns in software: as the elemental particles out of which all software is constructed. I'd like to think these thoughts again more actively in my programming languages course, when my students and I begin to learn and do functional programming.
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.
This morning I read this blog post by Dan Luu on the use of randomized algorithms in cache eviction. He mentions a paper by Mitzenmacher, Richa, and Sitaraman called The Power of Two Random Choices that explains a cool effect we see when we limit our options when choosing among multiple options. Luu summarizes the result:
The mathematical intuition is that if we (randomly) throw n balls into n bins, the maximum number of balls in any bin is O(log n / log log n) with high probability, which is pretty much just O(log n). But if (instead of choosing randomly) we choose the least loaded of k random bins, the maximum is O(log log n / log k) with high probability, i.e., even with two random choices, it's basically O(log log n) and each additional choice only reduces the load by a constant factor.
Luu used this result to create a cache eviction policy that outperforms random eviction across the board and competes closely with the traditional LRU policy. It chooses two pages at random and evicts the least-recently used of the two. This so-called 2-random algorithm slightly outperforms LRU in larger caches and slightly underperforms LRU in smaller caches. This trade-off may be worth making because, unlike LRU, random eviction policies degrade gracefully as loops get large.
The power of two random choices has potential application in any context that fits the balls-and-bins model, including load balancing. Luu mentions less obvious application areas, too, such as circuit routing.
Very cool indeed. I'll have to look at Mitzenmacher et al. to see how the math works, but first I may try the idea out in some programs. For me, the programming is even more fun...
I am a regular reader of John Regehr's blog, which provides a steady diet of cool compiler conversation. One of Regehr's frequent topics is undefined behavior in programming languages, and what that means for implementing and testing compilers. A lot of those blog entries involve C and C++, which I don't use all that often any more, so reading them is more spectator sport than contact sport.
This week, I got see how capricious C++ compilers can feel up close.
My students are implementing a compiler for a simple subset of a Pascal-like language. We call the simplest program in this language print-one:
$ cat print-one.flr program main(); begin return 1 end.
One of the teams is writing their compiler in C++. The team completed its most recent version, a parser that validates its input or reports an error that renders its input invalid. They were excited that it finally worked:
$ g++ -std=c++14 compiler.cpp -o compiler $ compiler print-one.flr Valid flair program
They had tested their compiler on two platforms:
I sat down at my desktop computer to exercise their compiler.
$ g++ compiler.cpp -o compiler In file included from compiler.cpp:7: In file included from ./parser.cpp:3: In file included from ./ast-utilities.cpp:4: ./ast-utilities.hpp:7:22: warning: in-class initialization of non-static data member is a C++11 extension [-Wc++11-extensions] std::string name = "Node"; ^ [...] 24 warnings generated.
Oops, I forgot the -std=c++14 flag. Still, it compiled, and all of the warnings come from a part of the code has no effect on program validation. So I tried the executable:
$ compiler print-one.flr ERROR at line #3 -- unexpected <invalid> 1 Invalid flair program
Hmm. The warnings are unrelated to part of the executable that I am testing, but maybe they are creating a problem. So I recompile with the flag:
$ g++ -std=c++14 compiler.cpp -o compiler error: invalid value 'c++14' in '-std=c++14'
What? I check my OS and compiler specs:
$ sw_vers -productVersion 10.9.5 $ g++ --version Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/usr/include/c++/4.2.1 Apple LLVM version 6.0 (clang-600.0.57) (based on LLVM 3.5svn) [...]
Oh, right, Apple doesn't ship gcc any more; it ships clang and link gcc to the clang exe. I know my OS is a bit old, but it still seems odd that the -std=c++14 flag isn't supported. I google for an answer (thanks, StackOverflow!) and find that that I need to use -std=c++1y. Okay:
$ g++ -std=c++1y compiler.cpp -o compiler $ compiler print-one.flr ERROR at line #3 -- unexpected <invalid> 1 Invalid flair program
Now the student compiler compiles but gives incorrect, or at least unintended, behavior. I'm surprised that both my clang and the students' gcc compile their compiler yet produce executables that give different answers. I know that gcc and clang aren't 100% compatible, but my students are using a relatively small part of C++. How can this be?
Maybe it has something to do with how clang processes the c++1y standard flag. So I backed up to the previous standard:
$ g++ -std=c++0x compiler.cpp -o compiler $ compiler print-one.flr ERROR at line #3 -- unexpected <invalid> 1 Invalid flair program
Yes, that's c++0x, not c++0y. The student compiler still compiles and still gives incorrect or unintended behavior. Maybe it is a clang problem? I upload their code to our student server, which runs Linux and gcc:
$ cat /etc/debian_version 8.1 $ g++ --version [...] gcc version 4.7.2 (Debian 4.7.2-5)
This version of gcc doesn't support either c++14 or c++1?, so I fell back to c++0x:
$ g++ -std=c++0x compiler.cpp -o compiler $ compiler print-one.flr Valid flair program
Hurray! I can test their code.
I'm curious. I have a Macbook Pro running a newer version of OS X. Maybe...
$ sw_vers -productVersion ProductName:Mac OS X ProductVersion:10.10.5 BuildVersion:14F2009 $ g++ --version Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/usr/include/c++/4.2.1 Apple LLVM version 7.0.2 (clang-700.1.81) [...]
$ g++ -std=c++14 compiler.cpp -o compiler $ compiler print-one.flr Valid flair program
Now, the c++14 flag works, and it produces a compiler that produces the correct behavior -- or at least the intended behavior.
I am curious about this anomaly, but not curious enough to research the differences between clang and gcc, the differences between the different versions of clang, or what Apple or Debian are doing. I'm also not curious enough to figure out which nook of C++ my students have stumbled into that could expose a rift in the behavior of these various C++ compilers, all of which are standard tools and pretty good.
At least now I remember what it's like to program in a language with undefined behavior and can read Regehr's blog posts with a knowing nod of the head.