Following the Dec 1 notes, derive type constraints for this 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>}
The only novelty of this language is that the list operations are now
polymorphic; that is, you can create lists of values of any type.
Note: 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}}
...}
Then, write a function which consumes an expression of this language, and returns a list of constraints (of the type defined in Part II).The correspondence between type constraints and the terms in Part II is as follows:
'number or 'boolean.
'-> and a list of two arguments, or'listof and a list of one argument.gensym returns a unique
identifier on every call.
Implement the unification algorithm from the Dec 1 notes. The algorithm should work for a generic term representation, as defined below.
A term is either:
In addition, you will need data types for representing a constraint (a pair of terms) and substitution (a variable and a term). The unification algorithm will consume a list of constraints (as defined in Part I) and produce either a list of substitutions or an error string.
Errors can arise from two situations: when the unification of two terms is impossible, or when the occurs check fails. In both cases, you should return a string with an appropriate error message.
Finally, when comparing variables for equality, use Scheme's built-in
eq? function. For symbols, it behaves exactly as
symbol=?; for other values, it compares them for identity (like
Java's == comparison). We will rely on identical
variables being deemed equivalent by eq? when solving the
constraints generated in the following section.
To infer the type of a program, first parse it, then generate constraints, and finally unify the constraints using the functions written for parts I & II as appropriate. The result will be a list of substitutions; by looking up the subsitution for the entire expression, you can access its type.
To implement this, your code needs to define a function,
infer-type,
which consumes a concrete representation of the program (as given above), and produces
either an error string or a representation of the inferred type.
Represent types concretely as:
<type> ::= number
| boolean
| (listof <type>)
| (<type> -> <type>)
| <string>
where strings are used to represent type variables. For example, the
type of length would be:
((listof "a") -> number)
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.