-----------------------
COS 441 Take-home Final
-----------------------
STUDENT NAME:
STUDENT ID:
TIME THIS EXAM WAS DOWNLOADED:
Read the instructions below carefully.
You have 3 hours and 20 minutes from when you download this file to
email it back completed to BOTH dpw@cs.princeton.edu and
chris@monsan.to Put [COS 441 FINAL] in the subject of the email. In
principal, the exam is 3 hours long, but you need some time to read
these instructions; you may need a restroom break and you may need a
minute or two after the exam is done to collect yourself and
officially press "send" on the email. The extra 20 minutes is a grace
period in addition to the official 3 hours for the exam itself. You
may use this grace period however you choose.
The work on the final must be your own work. You must not communicate
with anyone else about the material on the final until Jan 19 at noon,
when all students have finished taking the exam. If you have a
question about the exam material, make a reasonable assumption and
write down the assumption. Then proceed to answer the question to the
best of your ability. Partial solutions will be granted partial
credit.
You may refer to or use any of your notes from class, any material on
the course web site, the course textbooks and any code that you wrote
for your assignments. You may refer to any documentation about
Haskell from the Haskell web site or from Hoogle. In order to do
question 2 of the exam, you will also need the contents of this file:
http://www.cs.princeton.edu/~dpw/cos441-11/notes/exam-semantics.txt
You should download that file now.
This file must type check and compile when you hand it in. Do not
submit solutions that do not compile and type check or you will lose
many points. When writing code, feel free to add debugging code to
help you test your solution (that is a good idea!). Be sure, however,
to make your solution clear using comments or otherwise. Do not
submit multiple solutions to the same problem, hoping that one of them
is correct (such a strategy will not result in more points than
submitting one wrong solution).
There are 4 questions (each question has several subparts listed
(a), (b), (c), etc.):
1: 8 Points
2: 4 Points
3: 34 Points
4: 17 Points
+ 2 Points for writing out and signing the honour code below
Total: 65 Points
===================================
Here is the Princeton Honor Code:
I pledge my honor that I have not violated the Honor Code during this
examination.
Please copy the Honor Code here:
Type your name here to indicate you will abide by the honor code:
===================================
1. [Total: 8 Points]
In the following, use standard Haskell functions map, foldr, foldl and
++ to write equivalent functions that do not use explicit recursion.
You may add additional anonymous or auxiliary helper functions, but
none of the functions you write may be recursive (or mutually
recursive with eachother). You may use library other than functions
map, foldr, foldl, or ++, provided those other functions are not
recursive. For example, you may use functions fst and snd but you may
not use reverse (unless you define it yourself using map and fold).
You will receive partial credit if your program is correct on some
inputs but not others. You will receive no credit if your program
does not type check and compile.
(a) [4 Points]
> thresholdR :: Int -> [Int] -> [Int]
> thresholdR n [] = []
> thresholdR n (x:xs) =
> if x > n then x : thresholdR n xs
> else 0 : thresholdR n xs
Write an equivalent function called "threshold" that is not recursive
itself but may call map, foldr, or foldl here:
> threshold :: Int -> [Int] -> [Int]
> threshold n xs = error "not implemented"
(b) [4 Points]
> prefixAddR :: [Int] -> [Int]
> prefixAddR xs = aux 0 xs
> where
> aux n [] = []
> aux n (x:xs) = (n+x) : aux (n+x) xs
Write an equivalent function called "prefixAdd" that is not recursive
itself but does call map, foldr, or foldl here:
> prefixAdd :: [Int] -> [Int]
> prefixAdd xs = error "not implemented"
====================================
2. [Total: 4 Points] Is this a valid partial Hoare triple?
{true}
x = 1;
while (x > 0) { x = x + 1 }
{ x < -10 }
Yes or No:
If yes, write the loop invariant you would use to prove that it is valid:
====================================
3. [Total: 34 Points]
The syntax, operational semantics and typing rules for the lambda
calculus with booleans, functions and sum types is here:
http://www.cs.princeton.edu/~dpw/cos441-11/notes/exam-semantics.txt
(a) [4 Points] Show that this program:
\x:Bool. if x then true else false
is well-typed by building a proof using the typing rules from the file
above. Proof (rewrite below):
<...>
--------------------------------- --------------------------------------------
<... build proof upwards here...> <...your lines may be as wide as your like ...>
---------------------------------------------------------------------------------------------
|- \x:Bool. if x then True else False : Bool -> Bool
(b) [4 Points] Show that this program:
\z:Bool + Bool. (case z of (Left x -> True) (Right y -> y))
is well-typed by building a proof using the rules from the file above.
Proof:
<...>
----------------------------- -----------------------------------------------
<... build proof upwards here>
---------------------------------------------------------------------------------------------
|- \z:Bool + Bool. (case z of (Left x -> True) (Right y -> y)) : ???
(c) [2 Points] Explain (in general) what a "stuck expression" is:
(d) [2 Points] Give an example of a stuck expression that involves
either a "Left" "Right" or "Case" expression in some way:
(e) [2 Points] Explain what the canonical forms lemma should say about
a sum type t1 + t2:
(f) [10 Points] The preservation lemma says:
If . |- e : t and e --> e' then . |- e' : t
The proof is by induction on the derivation of e --> e'. Below,
prove the cases corresponding to the rules (E-case1) and (E-case2).
In your proof, you may assume that standard Exchange, Weakening,
Canonical Forms, and Substitution lemmas have already been proven. In
other words, you may use any of those lemmas in your proof without
proving them yourself.
case for rule (E-case1):
(p1) e --> e'
------------------------------------------------------- (E-case1)
case e of (Left x -> e2) (Right y -> e3) -->
case e' of (Left x -> e2) (Right y -> e3)
(1) |- case e of (Left x -> e2) (Right y -> e3) : t (assumed)
......
case for rule (E-case2):
----------------------------------------------------------- (E-case2)
case (Left v) of (Left x -> e2) (Right y -> e3) --> e2[v/x]
(1) |- case (Left v) of (Left x -> e2) (Right y -> e3) : t (assumed)
......
(g) [5 Points] The progress lemma states:
If |- e:t, then either:
(i) e is a value or
(ii) e -> e'
The proof is by induction on the derivation of |- e:t. In your proof,
you may assume that standard Exchange, Weakening, Canonical Forms,
Substitution lemmas have already been proven. In other words, you may
use any of those lemmas in your proof without proving them
yourself. Prove the case that corresponds to the typing rule (T-Left).
case for rule (T-Left):
G |- e : t1
---------------------- (T-Left)
G |- Left e : t1 + t2
......
(h) [5 Points] Suppose we were to extend the language defined in this file:
http://www.cs.princeton.edu/~dpw/cos441-11/notes/exam-semantics.txt
with a new kind of expression "Middle e". Suppose also that we added a new
typing rule for middle expressions:
G |- e : t2
------------------------------
G |- Middle e : t1 + (t2 + t3)
Suppose that "Middle v" counts as a value (ie: Middle e is a value
if e is a value v) and suppose that we add the following operational
rule for middle values:
e --> e'
-----------------------
Middle e --> Middle e'
In this case, at least one of Progress or Preservation will not be true for
the new language. Take your pick of either the Progress or the Preservation
theorem and explain why it is not true for the language containing Middle
expressions. Your explanation should include a (small) concrete example program
that helps you demonstrate precisely why the theorem you chose does not hold.
Which one did you pick (Write Progress or Preservation here)?:
Explanation:
===================================
4. [Total: 17 Points]
Consider the following BNF definition for "multi-expressions"
where n is any integer (0, 1, 2, 3,...,-1, -2, -3,...):
e ::= n | both(e1,e2) | neither | e1 + e2
Multi-expressions evaluate in such a way that they have 0, 1, 2, ... or
many answers. In other words, one multi-expression evaluates to a list
of values. Here is an explanation of how evaluation should work:
-- integer n evaluates to the list containing just the integer [n]
-- if e1 evaluates to the list of values vs1 and e2 evaluates to the list
of values vs2 then both(e1,e2) evaluates to the concatenation of lists vs1
and vs2
-- neither evaluates to the empty list of values
-- if e1 evaluates to the list of values vs1 and e2 evaluates to the list
of values vs2 then e1 + e2 evaluates to the list of values vs3 constructed
by adding each element of the list in vs1 to each element of the list in
vs2.
NOTE: You will not be judged on having the elements of the resulting
list in any particular order. You just need to have the correct elements
(and the correct number of such elements).
For example:
both(2,3) + 7 = [9,10]
both(2,3) + both(4,5) = [6,7,7,8]
3 + neither = []
both(4,5) + neither = []
Here is a data type for representing multi-expressions:
> data Exp = Num Int | Both Exp Exp | Neither | Add Exp Exp
Here is a monad that will help you process lists of results:
> newtype Result a = R [a] deriving (Eq,Show)
>
> instance Monad Result where
> return x = R [x]
> (R xs) >>= f = R (squash f xs)
>
> squash :: (a -> Result b) -> [a] -> [b]
> squash f [] = []
> squash f (x:xs) =
> let R ys = f x in
> ys ++ squash f xs
>
> many :: [a] -> Result a
> many xs = R xs
An example using bind and return explicitly:
(run this to find out what it does!)
> eg1 :: Result Int
> eg1 = many [3,4,5] >>= (\x -> return 1 >>= \y -> return (x-y))
(a) [2 Points]
Write an equivalent computation to eg1 above but use do notation instead
of using >>= explicitly.
> eg2 :: Result Int
> eg2 = error "fill me in using do notation"
(b) [10 Points]
Write a function eval that evaluates expressions and returns a list of
integers. For full credit, use the monad defined above effectively
in your solution. If you don't understand how the monad works,
you may write the evaluator any way you choose and receive partial
credit for this question. You may write any auxiliary functions or
definitions you choose.
If you cannot figure out how to create an eval function that has the
type Exp -> [Int] then for partial credit, create your own alternative
eval function (perhaps call it "myeval") and give it a type that you
can make work. It is better to solve part of this problem but have
your code type check and compile than to try to solve it all but have
your code not compile. Use comments to explain your code where
necessary. (In particular, use comments if you solve part of the
problem to explain what you have and have not attempted to do.)
Your solution may judged on style and elegance as well as correctness.
> eval :: Exp -> [Int]
> eval e = error "implement me"
(c) [5 Points] Consider any monad.
Assume that addition is associative and commutative. In other words,
assume these laws are true:
(Associativity) (x + y) + z = x + (y + z)
(Commutativity) x + y = y + x
Also, assume that the monad laws hold. Here are the laws:
(L1) return a >>= f = f a
(L2) m >>= (\x -> return x) = m
(L3) (m >>= f) >>= g = m >>= (\x -> f x >>= g)
Now, using the usual 2-column style from class, prove that the
following equation concerning monads is true for any integers x and y.
Justify each step of your proof using one of the laws above or by
substitution of equals for equals or calculation as we have done in
class.
return x >>= (\a -> return y >>= (\b -> return (a+b)))
=
return y >>= (\a -> return x >>= (\b -> return (a+b)))
Proof: