Below is a list of closed expressions. Consider the value of each expression. For each value, what type or types does it have? Some of the values may have no type that is describable in our type language.
6. proc (x) (x x)
It is not describable in our type language. Because its type is:
(t1 -> t2), where t1 = (t1 -> t2)
There is no way to define a type that contains itself in our type language. So it is quite possible that we cannot decide the type of a recursive procedure. For the same reason, the type of Y-combinator in 13 might be undecidable. But I am not sure for now.
12. proc (x)
proc (p)
proc (f)
if (p x) then -(x, 1) else (f p)
(int -> ((int -> bool) -> (((int -> bool) -> int) -> int)))
Are there any expressed values that have exactly two types according to definition 7.1.1?
Assume there are some expressed values that have exactly two types, then there are at least two different types sharing a value, i.e., two type sets intersect. According to definition 7.1.1:
Definition 7.1.1 The property of an expressed value v being of type t is defined by induction on t:
An expressed value is of type
int
if and only if it is anum-val
.It is of type
bool
if and only if it is abool-val
.It is of type (t1 -> t2) if and only if it is a
proc-val
with the property that if it is given an argument of type t1, then one of the following things happens:a. it returns a value of type t2
b. it fails to terminate
c. it fails with an error other than a type error.
It is easy to see that:
- 1 and 2 have no intersections
- 1 and 3 have no intersections
- 2 and 3 have no intersections
For 3, what is the type of the following program?
letrec f(x) = (f x) in f
The recursive procedure above does not terminate, so we can pass a value of any
type, like (f 1)
or (f zero?(1))
or (f (proc (x) x))
. Besides, since it
doesn't terminate, thus satisfying (b) in 3, t2 can be any
value. Seems it's an expressed value that has more than two types.
For the language LETREC, is it decidable whether an expressed value val is of type t?
At least for some vals, it is decidable.
-
For the non recursive part in LETREC, like the examples in this section 7.1.
-
For the recursive part, at least some expressed values have decidable types. For example:
-
the
fact
procedure has type (int -> int):letrec fact (n) = if zero?(n) then 1 else *(n, (fact -(n, 1))) in fact
-
-
But there exist undecidable expressed values as we have seen in examples in section 7.1 and exercise 7.1.
In our representation,
extend-subst
may do a lot of work if σ is large. Implement an alternate representation in whichextend-subst
is implemented as(define extend-subst (lambda (subst tvar ty) (cons (cons tvar ty) subst)))and the extra work is shifted to
apply-subst-to-type
, so that the propertyt(σ[tv = t') = (tσ)[tv = t']
is still satisfied. For this definition ofextend-subst
, is the no-occurrence invariant needed?
See this for the solution. The key difference is:
;;; apply-subst-to-type : Type x Subst -> Type
(define apply-subst-to-type
(lambda (ty subst)
(cases type ty
(int-type () (int-type))
(bool-type () (bool-type))
(proc-type
(t1 t2)
(proc-type
(apply-subst-to-type t1 subst)
(apply-subst-to-type t2 subst)))
(tvar-type
(sn)
(let ((tmp (assoc ty subst)))
(if tmp
+ (apply-subst-to-type (cdr tmp) subst)
- (cdr tmp)
ty))))))
Question: is there an input to cause it interminable? What if ty
is something
like
(proc-type (tvar-type 0) (tvar-type 0))
Note where the replaced value is found from: subst
. So the case above happens
only if the right-hand side type from subst
contains its left-hand side
variable. But before adding a (tvar . type)
, we always check the added pair to
preserve no-occurrence invariant (this is completed by no-occurrence
in
unifier
, as below:
(define unifier
(lambda (ty1 ty2 subst exp)
(let ((ty1 (apply-subst-to-type ty1 subst))
(ty2 (apply-subst-to-type ty2 subst)))
(cond [(equal? ty1 ty2) subst]
[(tvar-type? ty1)
(if (no-occurrence? ty1 ty2)
(extend-subst subst ty1 ty2)
(report-no-occurrence-violation ty1 ty2 exp))]
[(tvar-type? ty2)
(if (no-occurrence? ty2 ty1)
(extend-subst subst ty2 ty1)
(report-no-occurrence-violation ty2 ty1 exp))]
;; the rest part
;; ...
))))
). Thus we can find the possible loop in advance, and guarantee
apply-subst-to-type
terminates.
The interaction between polymorphism and effects is subtle. Consider a program starting
let p = newref(proc (x : ?) x) in ...
- Finish this program to produce a program that passes the polymorphic inferencer, but whose evaluation is not safe according to the definition at the beginning of the chapter.
- Avoid this difficulty by restricting the right-hand side of a let to have no effect on the store. This is called the value restriction.
If no effect on the store in allowed in right-hand side of let
, how can I
create a reference binding for later use? I think the exercise here means let
polymorphism and effect on the store should NOT appear together. So I need a
way to find:
- let polymorphism;
- side effect on the store in let binding.
2
is very easy: I just need to traverse through the bound expression of
let-exp
. For 1
, however, I need to find a way. If 1
is solved, exercise
7.29 is solved too. Because exercise 7.29 also requires a way to find all the
possible polymorphic type variables in let binding.