Due: Thursday, October 15, 5pm via turnin (assignment
name hwk6)
Collaboration Policy:
Pairs Permitted
Note: Students in CS4536 may opt to do the easier assignment on type checking instead, but that will cap your course grade at B. Graduate students are required to do this assignment.
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}}
...}
Write a function called generate-constraints that
consumes 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.
For a very small amount of extra credit, write a program in this
language for
which your algorithm infers the type
(“a” -> “b”). Your
program should not have run-time errors (such as unbound
identifiers). 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 ...