Due: Friday, February 29, 5pm via turnin (assignment
name hwk7)
Collaboration Policy:
Pairs Permitted
Note: You may opt to do the easier assignment on type checking instead, but that will cap your course grade at B.
This assignment handles the following language:
<expr> ::= <num>
| true
| false
| {+ <expr> <expr>}
| {- <expr> <expr>}
| {* <expr> <expr>}
| {iszero <expr>}
| {bif <expr> <expr> <expr>}
| <id>
| {with {<id> <expr>} <expr>}
| {rec {<id> <expr>} <expr>}
| {fun {<id>} <expr>}
| {<expr> <expr>}
| tempty
| {tcons <expr> <expr>}
| {tempty? <expr>}
| {tfirst <expr>}
| {trest <expr>}
This starter file contains all of the datatype definitions needed for this assignment.
The novelty of this language is that the list operations are now polymorphic; that is, you can create lists of arbitrary data (numbers, booleans, etc).
Adapt your parser to handle this language.
The right hand side of the rec binding does not have to be a
syntactic function. However, you may assume that the rec-bound
identifier only appears under a {fun ...} in the
right hand side of the binding. In other words, the following
expressions are legal:
{rec {f {fun {x} {f x}}}
...}
{rec {f {with {y 4}
{fun {x} {f y}}}}
...}
while the following are not legal:
{rec {f f}
...}
{rec {f {+ 1 f}}
...}
generate-constraints which
consumes a parsed expression of this language and returns a list of
constraints.
Write a function called generate-constraints that
accepts an expression and returns a list of constraints. You have the
freedom to determine the precise contract of this function, including
the representation of your constraints. However, we strongly recommend
that you use the Type data structures as defined in the
starter file as part of your representation of constraints.
During constraint generation, you will need to generate fresh
identifiers. The function gensym returns a unique symbol each time
it is applied. (gensym accepts an optional symbol as an argument;
the result of (gensym 'x) is then a unique symbol that "looks
like " 'x.)
Implement the unification algorithm from the lecture notes. Call the
function unify. The unify function should
accept a list of constraints and return a substitution. However,
unify should signal an error if two types cannot be
unified or if the occurs check fails. Use "unify" or
"occurs" in your error messages for these, respectively. Again, the
precise contract of unify is up to you to define.
eq? function. For symbols, it behaves exactly
as symbol=?; for other values, it compares them for
identity (ie, pointer equality, like Java's ==
comparison). Using eq?, for example, you can compare
expressions in abstract syntax for identity. The constraint solving
approach in Part III relies on identical variables being deemed
equivalent by eq?.
To infer the type of a program, parse it, generate constraints, and unify the constraints to get a substitution. From the substitution, you can determine the type of the program.
In particular, define the function
infer-type :: Expr -> Type
infer-type returns a Type, so you will
need to test types for equality. This can be tricky due to type
variables, particularly if generate-constraints calls
(gensym) to generate unique type variables. The support
code defines a function ((type=? t1) t2) that returns
true if t1 and t2 are equal,
modulo renaming of variables. We've included a few examples that
show you how to use type=? with test/pred
and test/exn. You are free to modify this function as you
see fit or to not use it at all.
You should write unit tests for all functions that you
write. However, for the purpose of jousting, ensure that your test
suites just use parse :: S-exp -> Expr and
infer-type :: Expr -> Type. If you chose to use the
type=? function we have provided, include it with your
test suite.
For a very small amount of extra credit, write a program in this
language for
which your algorithm infers the type
(“a” -> “b”). You
shouldn’t attempt this problem until you’ve fully
completed the
assignment.
You do NOT need to implement an interpreter for this language.
You do NOT need to implement let-based polymorphism.
Submit a Scheme file called infer.ss containing your code and a second file called tests.ss containing your test cases.
Nothing yet ...