15-312 Recitation #3: Assignments 1 (autopsy) and 2 (deBruijn, coding, questions) 2002-09-11 Joshua Dunfield (joshuad@cs) Carnegie Mellon University Outline: - Assignment 1 autopsy - deBruijn representation - Coding style issues - Questions ------------------------------------------- ASSIGNMENT 1: AUTOPSY The average score was 35; most people have some understanding of what's going on; most people made a few mistakes. Notes on specific problems: 1.1 and 1.2: very few major problems 1.3: Most people had the right idea 1.4: Four cases were the same as in 1.3, so most people had no problems. The fifth case, s1 type s2 type / s1 -> s2 type, is substantially harder. Some people tried to do the same as 1.3, and claimed various things "by inversion" or by applying a rule in reverse. Inversion is a valuable tool, but it must be applied correctly. In particular, you must make sure that only one rule could have been used! It's wishful thinking to assume that s tycon / s type was applied. If you like to "game the system" (who doesn't?) and you thought you had proved 1.4 as easily as 1.3, you should have wondered why 1.4 was worth more points... Because the last case was by far the most difficult, and because it was the only case differing from 1.3, I skewed the point values: 3 points for the 4 easy cases, 12 points for the hard case. 2.1: Most people did pretty well on this. I ended up taking few or no points off the S and K cases, even for long truth-table-filled solutions. I did not accept solutions that merely asserted that the propositions were tautologies... 2.2: Only one person got full extra credit for this. The rest were mostly "reasonable guesses". The sample solution discusses this problem in some detail. Common problems: - Applying the IH to a derivation that's not necessarily smaller. Common ways to fall into this trap: subd, rule, IH; or subderivation, lemma, IH. Unless the lemma specifically relates the size of the derivations, the lemma tells you nothing about whether the derivation is smaller. - When you have one case for each rule used, say which rule corresponds to which case. Don't make me figure it out. - When you generalize the IH, do so "at the top level", not buried inside the first case where you need the generalization. - Say what you're inducting on, especially if there's a choice, such as when proving "If s1 type' and s2 type' then s1 -> s2 type'". You could induct on the first derivation, the second derivation, or both (as we did last week, to prove transitivity of subtyping). - Small points: + "... tycon list" is not a judgment. + "s", "s1", etc. are strings, not judgments. In a proof by rule induction, you're working with derivations of _judgments_ (s tycon, s2 type'). Final remarks: If you got a low score, but you understand your mistakes and think you probably wouldn't make them again, you're on track. If you don't understand why something was a mistake, you should 1. read the sample solution (everyone should do this) 2. think about it some more If you still don't understand, talk to me or Frank. You do need to be able to write correct proofs by rule induction to get through 312. Low scores on the written assignments may not be fatal, but it is highly likely that the exams will include proofs by (rule) induction... -------------------------------------------- ASSIGNMENT 2 deBruijn representation: Basic idea: count the nesting distance between a declaration of an identifier and its use. fun g (y:...):... is fun f (x:...):... is f x + g y end ... end should translate to fun _ (_:...):... is fun _ (_:...):... is [2] [1] + [4] [3] end ... end It isn't quite as simple as just reading backwards in the code and counting variable declarations; you must count only declarations that are in scope. For example, in fun g (y:...):... is let f' = fun f (x:...):... is f x + g y end in f' y end end the nearest *visible* declaration to f' y is f'; f and x are not in scope. So the deBruijn translation of the above is fun _ (_:...):... is let f' = fun _ (_:...):... is ... end in [1] [2] end Much of the translation is rather straightforward. Suppose we name the translation function tr, so tr(e) is the deBruijn translation of e. The translation of the application of e1 to e2 is simply tr(apply(e1, e2)) = apply(tr(e1), tr(e2)) because an application does not declare any variables. Several other MinML constructs can be translated the same way. General notes about coding style: - Comments: I don't require 212-style specs, but unless you're confident that I will be able to understand your comment-free code after several straight hours of reading your classmates' code, some comments are highly desirable. Writing the type and a brief description of every function is probably a good idea. - 312 is not a class about programming in SML, so I will be less draconian than I would be for, say, 212, but you should try to avoid certain things I consider "bad practice": - writing "if ... then true else false", or its cousins "if ... then true else blah", etc.: use andalso/orelse instead. (Doesn't apply to MinML test cases!) - bizarre indentation (or no indentation): this makes code nearly unreadable and I will certainly deduct points. Anything reasonable is acceptable. If you use emacs, you can use sml-mode and TAB to do automatic indentation. - uninformative names - not using pattern matching where appropriate, the classic example being isSome and valOf instead of case ... SOME x => | NONE. Pattern matching is great -- use it! - Inelegance bugs me. However, the most important consideration is whether your code correctly does everything the assignment asks for.