Assignment 3
Table of Contents
This assignment is due on Thursday 2/12 by 8:00PM. You should put
all of your programming solutions in a file called
assign3/lib/assign3.ml. See the file test/test_assign3.ml for
example behavior of each function. Your written solutions should be
submitted via Gradescope as a single pdf. They must be exceptionally
neat (or typed) and the final answer in your solution to each problem
must be abundantly clear, e.g., surrounded in a box. Also make sure
to cite your sources per the instructions in the course manual.
1. Programming (%40)
This week you'll be building (surprise) a calculator. Or rather, you'll be extending the calculator from the previous assignment so that values can be assigned to variables. The inputs to this calculator are statements of the form
VARIABLE = <expr>
where VARIABLE is a variable name composed solely of upper-case
letters and <expr> is an infix arithmetic expression as in
assignment 2 but potentially with variables. So, for example, after
you've implemented the following functions you should be able to have
the following interaction via dune exec assign3:
ArithVar version 0.0.1 Enter #quit;; to exit # X = 2 * 2;; - : int = 4 # Y = 3 * 4;; - : int = 12 # ADDXY = X + Y ;; - : int = 16
There are several functions to implement, but you should take assignment 2 as a staring point.
1.1. Lexing
Implement the function
val lex : string -> string list
so that lex input is the result of removing the whitespace from
input and separating it into its constituent parts (formally called
lexemes). These parts consist of: (, ), +, -, *, /, =,
nonnegative integers (i.e. contiguous groups of digits) and variables
(i.e., contiguous groups of upper-case letters), e.g.,
let _ = assert (lex "VAR = 2 + 3" = ["VAR"; "="; "2"; "+"; "3"])
The behavior of the function is undefined if the input is not a well-formed statement as described above.
1.2. Evaluation
Evaluation is roughly the same as in assignment 2, except that now
it's possible that the given expression contains variables. We'll be
using association lists to represent variable bindings that we create
during a calculator interaction. An association list is a list of
key-value pairs (('k * 'v) list). See the textbook chapter and the
stdlib320 section for more information. For the above sample
interaction the final assocation list we should get up to reordering
is:
[("X", 4); ("Y", 12); ("ADDXY", 16)]
Implement the function
val eval : (string * int) list -> string list -> int
so that eval env expr is the result of evaluating an arithmetic
expression expr in infix notation with variables (i.e., the
right-hand side of the "=" after lexing) relative to the variable
bindings in env, e.g.,
let _ = assert (eval [("X", 4); ("Y", 12)] ["X"; "+"; "Y"] = 16)
The behavior of the function is undefined if expr is not a valid
arithmetic expression in infix notation with variables post-lexing.
1.3. Unique Insertion
After evaluating an expression, we'll need to add it to the variable bindings maintained by the calculator. Implement the function
val insert_uniq : 'k -> 'v -> ('k * 'v) list -> ('k * 'v) list
so that insert_uniq k v l is the result of inserting (k, v) into
l. Furthermore, your implementation must maintain the invariant
that a key appears at most once in the resulting list. Furthermore
furthermore, your implementation must be tail recursive. Note: The
order of key-value pairs does not need to be maintained in the
resulting list.
let l = [("X", 4); ("Y", 12); ("ADDXY", 16)]
let _ = assert (List.assoc "X" l = 4)
let _ = assert (List.assoc "X" (insert_uniq "X" 7 l) = 7)
2. Written (60%)
For all derivations, make sure that you label every rule instantiation with a rule name, and that you do not include side-conditions. The rules you should use for your derivations are included in the notes for this course under the section 320Caml Specification.
2.1. Typing Derivations
Determine a type \(\tau\) such that the following typing judgment is derivable. Also provide the derivation.
\[ \{ \texttt{f} : \texttt{int -> bool} \} \vdash \texttt{let x = let y = 3 in y + y in f x} : \tau \]
Determine a type \(\tau\) such that the following typing judgment is derivable. Also provide the derivation.
\[ \{\} \vdash \texttt{(fun x -> fun y -> y x) (fun x -> x) (fun f -> f 1)} : \tau \]
2.2. Semantic Derivations
Determine a value \(v\) such that the following semantic judgment is derivable. Also provide the derivation.
\[ \texttt{let x = 3 in if x = 4 then x + 1 else x + 2} \Downarrow v \]
Determine a value \(v\) such that the following semantic judgment is derivable. Also provide the derivation.
\[ \texttt{let x = let y = 3 + 3 in y in x} \Downarrow v \]
2.3. Recursive Let-Expressions
One rule not included in the 320Caml specification is the typing rule for recursive let expressions, which have the following syntax:
<expr> ::= let rec <var> = <expr> in <expr>
The only difference between the let rule and the recursive let rule is that, given an expression of form:
\[ \texttt{let} \ \texttt{rec}\ f \ \texttt{=} \ e_1 \ \texttt{in} \ e_2 \]
determining the type of \(e_1\) requires that \(f\) appears in the context, since the definition of a recursive function can depend on the function itself.
Provide the typing rule for recursive let statements (we'll call it LetRec), and use this to give a derivation of the following typing judgment.1
\[ \{\} \vdash \texttt{let rec f = fun x -> f (x + 1) = }\textcolor{red}{\texttt{false}}\texttt{ in f} : \texttt{int -> bool} \]
Footnotes:
Errata (2/10, 7:00AM): The change to the given expression is in red given.