Thursday, March 25, 2010

Student B goes to the student center to get some tutoring.

“I'm not doing so well in my intro to computer science course. I just took a quiz and didn't finish it. This may sound paranoid, but there's a blog that seems to be talking about the problems I'm having. They were discussing part of the program in the quiz.”

mquander wrote: Where did student B get the idea that he was supposed to evaluate it?

Blaise Pascal wrote:He didn't need to evaluate it to answer the quiz questions, and he
wasn't asked to evaluate it.

Alexey wrote:He was expected to ignore the evaluation path of the sanity check form.

“Anyway,”Student B continued, “I don't understand what they are talking about. What else would you do with a program but evaluate it? Isn't that what programs are for? They're telling what not to do, but not what I ought to be doing instead. Can you help me?“

5 comments:

In my opinion, the tutor should explain to him that computer programs serve two purposes at once: first, they are human artifacts, which represent a person's conception of a process which can take place on his mental model of a machine. They exist as a sort of Platonic thing in that person's head which has gone onto paper or into a text file.

Since almost nobody has a clear mental model from the very bottom to the very top of an actual computer, and since people make mistakes, those programs might be flawed when you consider them in their second role, which is that of an input to some actual, physical machine, which will operate on the input in the real world.

When reasoning about the operation of a program, you're expected to be spending most of your time reasoning about the program in its first manifestation, as a totally abstract process on an ideal computer. The only point to thinking about how an actual physical machine would operate on your program is if you suspect your mental model is wrong or incomplete, and you need to clarify it in order to fix some real-world problem with the program.

The reason student B seems strange is that this distinction exists because of the natural limitations of the human mind. Since computers are such complicated machines, and since software is so large, the only way to produce large programs correctly is to work at a rather high level of abstraction almost all the time. In the real world, you would get things done very slowly if you tried to adopt student B's approach to answering all questions about your programs.

If I were the tutor, I would explain to Student B that, in general, his job when looking at code is analysis, not execution. Analysis of what the code does only rarely involves execution of the code.

In this case, the line in question was preceded by the comment "Sanity check". The comment, as well as the fact that the line made no definitions or would otherwise change the state of the program, implies to me that it is expressing something about what the author intended the (fib n) function to do.

Is there a way to analyze the (fib n) function to verify that it meets that intent without actually trying to evaluate the form?

Even that might be thought of as unnecessary in this case, because none of the questions in the quiz referred to the line in question, nor did the line in question effect the rest of the program in a way which would change the questions.

So what should he have done? He should have analyzed the program given in the quiz, not executed it. And in that analysis, he should have used higher order thinking than simply executing it.

I think the tutor needs to sit B down for an explanation of inductive proofs. Once can quickly prove that the naive implementation of fib will return the correct value of the fib sequence for any starting value of n. Knowing that the program will execute correctly is the human's job. Doing the work is the computer's job. Student B should be taught to do the former, and leave the latter to machines.

Recursion is about trusting the recursive step, which I do not think students can do until they understand a structured way of reasoning about recursion (i.e. the inductive proof). I think what I am saying is a paraphrase of mquander's argument above.

Execution is just one kind of interpretation—a particularly concrete kind. There are others that omit the full detail that execution gives, but that are still formally connected to the semantics of the language. A programmer's job, very generally, is abstract interpretation of the code he or she reads.

The key is to attack the "what else am I supposed to do with a program other than execute it", I reckon.

What are you supposed to do with a program? Nothing! Nothing implicit---whatever the situation asks for. If the quiz asked for the value of (fib 4), then you should compute the value of (fib 4), whether by evaluating fib on 4 directly or by some other means (that you can make sure will give the same answer). But if the quiz asks for the domain of the procedure even? by all means evaluate the definition that gives rise to that procedure if you want, and other other definitions that give rise to procedures it calls, but then analyze the domain, perhaps by a process not unlike type inference. There are many different things that you might want to do with programs, and executing them literally is only one of them.