13 — Poly Types
Tuesday, 18 February 2020
Presenters (1) Benjamin Herzberg & Nicolas Burniske (2) Jacob Chvatal & Thomas Morriss
Simple Types Limit Developers ...
it has no types for your favorite data structure (dictionaries)
it has no types for alternatives (e.g., Boolean and similar user-defined types)
it forces us to copy code because the types differ:
Lectures/13/types.rkt
#lang racket (struct int [] #:transparent) (struct -> [domain range] #:transparent) ;; - - - - - - - - - - - - - - - - - - - - - - - - - (struct ref [content] #:transparent) ;; - - - - - - - - - - - - - - - - - - - - - - - - - #; {Type is one of: -- (int) ;; represents the set of integers -- (-> Type Type) ;; .. the set of functions -- (ref Type)} ;; the set of cells ;; - - - - - - - - - - - - - - - - - - - - - - - - - (define type=? equal?) (define (type? t) (or (int? t) (->? t) (ref? t))) (provide (struct-out int) (struct-out ->) (struct-out ref) #; {Any -> Boolean : Type} type? #; {Type Type -> Boolean} type=?) a cell-content swapper for integer cells (int) is identical to a swapper for integer-to-integer cells (-> (int) (int))
if we had the somewhat obvious types for cells (see figure 59)
a sort function of list of integers would be identical except for sort of list of pairs of integers
if we had list types, but we don’t even have those
never mind: it doesn’t allow programmers to define a type of lists or trees
and we can’t apply an increment function for ints to nats (if we had nats in our language) or a length function for lists to the type of non-empty lists (if we had such things in our language to avoid first(empty))
... So We Need More Types
products (tuples)
sums (alternatives)
labeled records (dictionary)
encapuslation (existential)
polymorphism (universal)
recursive types
structural subtypes (nat < int, (d -> r) < (d’ -> r’))
The Types of Cells
The copying code part is particularly disturbing and today’s topic. Indeed, because of the weakness of the type system, it’s all so bad, we can’t even get @, !, and = right.
Let’s say we want to play the recursion game and set up factorial’s recursion via a ref cell. So we add types to the initial environment so that our type checker can deal with cells. See figure 60.
Lectures/13/type-env0.rkt
#lang racket (require "types.rkt") (require "../6/environment.rkt") ;; - - - - - - - - - - - - - - - - - - - - - - - - - (define int2int (-> (int) (int))) (define population (list (list "+" (-> (int) (-> (int) (int)))) (list "*" (-> (int) (-> (int) (int)))) (list "^" (-> (int) (-> (int) (int)))) (list "@" (-> int2int (ref int2int))) (list "!" (-> (ref int2int) int2int)) (list "=" (-> (ref int2int) (-> int2int int2int))))) (define TYPE-ENV0 (for/fold ([env empty]) ([name-type population]) (add (first name-type) (second name-type) env))) ;; - - - - - - - - - - - - - - - - - - - - - - - - - (provide TYPE-ENV0)
Now we can run a “poem” style example where we first set up a simple identity function in a cell and then assign the function we want. Recall that = is returns the old value in the cell. So there, take a look at figure 61,
Lectures/13/ex0.rkt
#lang racket (require "cheap-poly-tc.rkt") (require "ast.rkt") (require "types.rkt") (require "../define-names.rkt") (define-names f x @ ! = cell1 cell2 swap tmp) ;; - - - - - - - - - - - - - - - - - - - - - - - - - - - (type-check (tdecl swap (-> (ref (-> (int) (int))) (-> (ref (-> (int) (int))) (-> (int) (int)))) [tfun* cell1 (ref (-> (int) (int))) [tfun* cell2 (ref (-> (int) (int))) [tcall [tcall = cell2] [tcall (tcall = cell1) (tcall ! cell2)]]]] swap)) ;; - - - - - - - - - - - - - - - - - - - - - - - - - - - (type-check (tdecl swap (-> (ref (int)) (-> (ref (int)) (int))) [tfun* cell1 (ref (int)) [tfun* cell2 (ref (int)) [tcall [tcall = cell2] [tcall (tcall = cell1) (tcall ! cell2)]]]] swap))
With the base type environment as set up, we can only get the first example to type check not the second one.
Polymorphic Types, Done Right
; A Ty is one of: ; – ... ; – t : String (type variable) ; – (All (t) (tech "Ty")) : a polymorphic type
; A TypedAST is one of: ; – ; – [FUN* Var TypedAST] : type abstraction /\ ; – [CALL TypedAst Ty] : type application [ .. ]
|
TEnv + (x,TYPE) |- bdy : s |
------------------------------------------- |
TEnv |- (FUN* x bdy) : (All (x) s) |
|
TEnv + (x,TYPE) |- f : (All (x) t), t : Type |
---------------------------------------------------------------- |
TEnv |- (CALL f t) : (All (x) s) |
|
fint = [CALL id (int)] fint2int = [CALL id (-> (int) (int))]
@ = /\t.\c:t.. allocate store location, place c in there ..
[tcall [tapp (at) int] 0]
! = /\t.\c:cell t.. retrieve store location l from c, value from l in store
[tcall [tapp ! int] [tcall [tapp (at) int] 0]]
sort = /\t.\ a:array[t], <: t x t ->Boolean. sort a with <
[tcall [tapp sort int] [1,0,1]]
Polymorphic Types, Done Cheaply
Lectures/13/cheap-poly-tc.rkt
#lang racket (require "ast.rkt") ;; monotypes for @ etc. ; (require "type-env0.rkt") ;; type generators for @ etc. (require "type-env1.rkt") (require "types.rkt") (require "../6/environment.rkt") ;; - - - - - - - - - - - - - - - - - - - - - - - - - (define UNDECLARED "undeclared variable") (define FUNCTION "function type expected") (define DOMAIN "domain type doesn't match arg type") (define DECL "type of a declaration doesn't match") (define IF-TEST "int expected in if test position") (define IF-BRANCH "same types expected in branches of if") (define INSTANTIATE "types not sufficiently instantiated") #; {Type Type -> Type} (define (function-type-match tf ta) (match tf [(-> tdom trng) (if (type=? tdom ta) trng (error 'tc DOMAIN))] [(? procedure?) ;; using "dirt cheap local inference" (define instantiated (tf ta)) (->-range instantiated)] [else (error 'tc FUNCTION)])) #; {TypedISL -> Type} (define (type-check isl0) #; {TypedISL TEnv -> Type} (define (tc/accu isl env) (match isl [(tcall f a) (define tf (tc/accu f env)) (define ta (tc/accu a env)) (function-type-match tf ta)] ;; - - - - - - - - - - - - - - - - - - - - - - - - ;; old [(? string?) (if (defined? isl env) (lookup isl env) (error 'tc UNDECLARED))] [(? integer?) (int)] [(tfun* x t b) (define env+ (add x t env)) (define tbdy (tc/accu b env+)) (-> t tbdy)] [(tdecl f type rhs body) (define env+ (add f type env)) (define trhs (tc/accu rhs env+)) (if (type=? trhs type) (tc/accu body env+) (error 'tc DECL))] [(tif-0 tst thn els) (define ttst (tc/accu tst env)) (cond [(not (int? ttst)) (error 'tc IF-TEST)] [else (define tthn (tc/accu thn env)) (define tels (tc/accu els env)) (if (type=? tthn tels) tthn (error 'tc IF-BRANCH))])])) (define result (tc/accu isl0 TYPE-ENV0)) (if (type? result) result (error 'tc INSTANTIATE))) ;; - - - - - - - - - - - - - - - - - - - - - - - - - (provide type-check)
Lectures/13/type-env1.rkt
#lang racket (require "types.rkt") (require "../6/environment.rkt") ;; - - - - - - - - - - - - - - - - - - - - - - - - - (define int2int (-> (int) (int))) (define population (list ;; - - - - - - - - - - - - - - - - - - - - - - - - [list "@" (λ (type) (if (type? type) (-> type (ref type)) (error 'tc POLY)))] [list "!" (λ (ctype) (match ctype [(ref type) (-> ctype type)] [_ (error 'tc CALL)]))] [list "=" (λ (ctype) (match ctype [(ref type) (-> ctype (λ (val-type) (if (type=? type val-type) (-> type type) (raise CALL))))] [_ (error 'tc "= ...")]))] ;; - - - - - - - - - - - - - - - - - - - - - - - - ;; old (list "+" (-> (int) (-> (int) (int)))) (list "*" (-> (int) (-> (int) (int)))) (list "^" (-> (int) (-> (int) (int)))))) (define POLY "@ is polymorphic") (define CALL "domain doesn't match") (define TYPE-ENV0 (for/fold ([env empty]) ([name-type population]) (add (first name-type) (second name-type) env))) ;; - - - - - - - - - - - - - - - - - - - - - - - - - (provide TYPE-ENV0)