Thursday, April 1, 2010

Back in the classroom Student Z complains “You're talking about analyzing code. I don't want to analyze code. That's ivory tower crap. In the real world, they write code and run it. Why should I bother learning this stuff?”

Is this a reasonable viewpoint?

Student B has a more technical question. “So I think I'm getting this idea that you want to be able to understand the code without simply evaluating it, but what I don't get is how you can tell what you need to evaluate and what you don't. If you don't evaluate anything, the how could you know that (define foo (lambda (x) ...)) is going to define a name as a procedure? You sort of have to do some evaluation. But then how do you know that you don't have to evaluate the ‘sanity check’ code?
“I guess what I'm asking is: are there some rules I should know, or is this just seat-of-your-pants stuff?”

In other news, I'm thinking about how to do a simple persistent store in Scheme/Lisp. I do a fair amount of database type stuff at work and I'm pretty sure there a lot of improvements on what I'm seeing. It seems to me that the second year course in computer science should deal with persistence and databases, and that a “Structure and Interpretation of Persistent Data” or a “How to Design Databases” would be a great book.

So in order to play with these ideas, I'm putting some work into MIT Scheme. The first thing I wanted was ‘:keyword’ objects — self-evaluating symbolic constants. I prefer the leading colon style, but I know others prefer the trailing. SRFI-88 specifies trailing, but many versions of Scheme have leading (or both). I decided to have a way to switch between the two styles.

CPH suggested that switching should be on a per-file basis, like case-sensitivity. Neither one of us like the #!fold-case #!no-fold-case flags that R6RS suggests. (Read Clinger's rant). After a bit of casting around for ideas, the one that made the most sense was to put the case folding and keyword style in the ‘file attributes line’ at the top of the file.
;;; -*- Mode: Scheme; keyword-style: prefix -*-
That was slightly painful to implement because there is a large variability in the syntax of the file attributes line and I wanted to provide a ‘soft landing’ for strangely written lines.

The upshot is that now I can use keywords (in prefix style!) and correctly interact with code that is written with trailing-colon keywords or with no keywords at all. (Although the latter will find it fairly nasty to invoke to my code: (foo (string->keyword "a") 22).)


  1. Student Z's viewpoint is not only unreasonable, it's wrong. In fact, a huge amount of a "real world" programmer's time is spent reading code---that is, "analyzing" it. The word "analyze" is perhaps a bit loaded with connotations of things compilers do, like type inference or control flow analysis, but it is the best technical analogue to the "reading" that programmers do all the time. And of course, "writing" has a very tight feedback loop with "reading", and, presumably, you analyze what you are about to write at least to some degree before you write it.

    I don't have a very good answer to Student B's question, and I suspect it does not have a very good answer. He is certainly right that some of the thoughts that happen on investigating a piece of code so as to answer a quiz question about it just mimic evaluating that piece of code. He is also right that others do not. How, in great detail, to choose which to do when may very well be AI-complete. I can offer some heuristics: be guided by the goal, in this case the quiz question; if an evaluation seems to be taking a long time, think about how to circumvent it, and learn what you need about the evaluee some other way (induction arguments, for example, can be good here); if you're lost, perhaps you can try every trick in the book breadth-first and see whether any of them lead to the answer. But as a teacher, given the current state of understanding of programs and of the process of programming, the best I think I can do is to teach particular things that the students can do, and give them examples of what works in various sitations and what doesn't, and hope they figure it out.

  2. I agree with everything Alexey said.

    One thing you could do to drive the point home to Student Z is to find an open source project written in Scheme (or something else he or she should reasonably be able to deal with, like elisp) that has a known bug, point Student Z at it, and say "fix this bug".

    In this case, a profiler can easily tell you that your program is spending a tremendous amount of time in fib, but only analyzing the code can tell you why fib is taking so long, and it only through the other stuff in this class not mentioned on this quiz that'll allow you to modify or rewrite fib so it doesn't take so long.

  3. FWIW, Guile 2 grovels the first line or two for mode declarations, to determine the encoding of the file.

    I wonder to what extent character encoding is related to character folding and keyword syntax.

  4. I just don't understand Student B's position anymore. He says:

    If you don't evaluate anything, the how could you know that (define foo (lambda (x) ...)) is going to define a name as a procedure?

    What do you mean, how do you know it? That's what you've been learning in class! If that's not good enough, you go and try it a few times, and find out what it does. Or you ask someone smart. Or read the R[5|6]RS specification.

    Student B seems to be using some extremely formal definition of "knowledge" that doesn't have anything to do with how humans figure things out and answer questions.

    As for student Z, if he doesn't want to analyze code, perhaps he should drop his computer science course and take a different class.

  5. How do the prefix keywords interact with SRFI 42 (eager comprehensions)?

  6. Ian asks “
    How do the prefix keywords interact with SRFI 42 (eager comprehensions)?”

    Probably not too well. The `:' generator would be fine because it wouldn't be confused with the empty keyword, but :list or :string would be a problem. You could either switch to suffix style, or just escape the problematic symbols:
    (|:range| k 2 n)

    The other possibility would be to hack the SRFI implementation so that it used keywords in addition to symbols (as synonyms). Then it wouldn't matter if you used keywords or not.

  7. > Is this a reasonable viewpoint?

    Obviously, no. The real world has billions invested in non-runtime analysis of various stripes; many companies, like Coverity, are founded solely on selling and supporting static analysis of programs.