TITLE: Strength Later, Weakness Now AUTHOR: Eugene Wallingford DATE: February 23, 2010 6:34 PM DESC: ----- BODY: One of the things I've always liked about Scheme is that the procedures I write are indistinguishable from the primitive procedures of the language. All procedures are applied prefix and named using the same binding mechanism. This similarity extends beyond simple procedure definitions to other features such as variable arity and special forms, which aren't procedures at all. All this makes Scheme an especially handy tool for creating domain-specific languages. I can create a whole new language for the domain I'm working in, say, language processing or financial accounting, simply by defining the procedures and forms that embody the domain's concepts. When I write programs using these procedures, they mix seamlessly with Scheme's primitive procedures and forms to create a nearly friction-less writing -- and reading -- experience. I've always touted this feature of the language to my students, who usually learn Scheme as a tool for writing interpreters and other simple language processors in our programming languages course. However, over the last couple of offerings of the course, I am beginning to realize that this strength is a weakness for many beginners. At the outset of their journey into functional programming, students have so many things to think about (language syntax, new programming principles, idioms to accomplish tasks they understand well, and so on) that a lack of friction may actually hurt them. They have trouble finding the boundary between the language Scheme and the language we build on top of it. For example, when we first began to implement recursive procedures, I gave students a procedure sequence:
    (define sequence
      (lambda (start finish)
        (if (> start finish)
            (cons start (sequence (+ start 1) finish)))))
as a simple example and to use for testing code that processes lists of numbers. For weeks afterwards, I had students e-mailing me because they wrote code that referred to sequence: "Why am I getting errors?" Well, because it's not a primitive and you don't define it. "But we use it in class all the time." Well, I define it each time I need it. "Oh, I guess I forgot." This sequence has re-played itself many times already this semester, with several other pieces of code. I suppose you could say the students ought to be more disciplined in their definition and use of code, or that I ought to do a better job of teaching them how to write and reuse code, or simply that the students need better memories. One or all of these may be true, but I think there is more happening here. A language with no friction between primitives and user-defined code places one more burden on students who are already juggling a lot of new ideas in their minds. As students become more experienced with the language and the new style of programming, they have a better chance to appreciate the value of seamless layers of code as they grow a program. They begin to notice that the lack of friction helps them, as they don't have to slow down to handle special cases, or to change how a piece of code works when they decide to add an abstraction between the code and its clients. Whereas before the lack of friction slowed them down while they pondered boundaries and looked up primitives, now it helps them move faster. This phenomenon is not peculiar to functional programming or Scheme. I think it is also true of OOP and Java. Back when Java was first becoming part of CS 1 at many schools, many of my colleagues objected to use the use of home-grown packages and libraries. The primary argument was that students would not be learning to write "real Java" (which is so wrong!) and that their code would not be as portable (which is true). In retrospect, I think a more compelling case can be made that the use of homegrown packages might interfere with students cognitively as they learn the language and the boundaries around it. There are elements of this in both of their objections, but I now think of it as the crux of the issue. This phenomenon is also not a necessary condition to learning functional programming or Scheme. A number of schools use Scheme in their first-year courses and do just fine. Perhaps instructors at these schools have figured out ways to avoid this problem entirely, or perhaps they rely on some disciplines to help students work around it. I may need to learn something from them. I have noticed my students having this difficulty the last two times we've offered this course and not nearly as much before, so perhaps our students are changing. On the other hand, maybe this reflects an evolution in my own recognition and understanding of the issue. In either case, my job is pretty much the same: find ways to help students succeed. All suggestions are welcome. -----