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.