macrotypes/tapl/stlc+cons.rkt
Stephen Chang 5c5f500f39 clean up requires/provides, see other mlish language additions below
- fix bug where recursive tyvar did not have #%type tag
- records
- optional field names in type definitions
- reuse prefixes by default
- list fns
- provide explicit inst (need it for ho poly fns)
- if now allows non-false instead of bool
- small inference fixes
- add rw ocaml tests, ch6, up to "polymorphic variants"
2016-03-10 00:39:01 -05:00

92 lines
3.3 KiB
Racket

#lang s-exp "typecheck.rkt"
(extends "stlc+reco+var.rkt")
;; Simply-Typed Lambda Calculus, plus cons
;; Types:
;; - types from stlc+reco+var.rkt
;; - List constructor
;; Terms:
;; - terms from stlc+reco+var.rkt
;; TODO: enable HO use of list primitives
(define-type-constructor List)
(define-typed-syntax nil/tc #:export-as nil
[(~and ni (_ ~! τi:type-ann))
( null : (List τi.norm))]
; minimal type inference
[ni:id #:with expected-τ (get-expected-type #'ni)
#:when (syntax-e #'expected-τ) ; 'expected-type property exists (ie, not false)
#:with ty_lst (local-expand #'expected-τ 'expression null) ; canonicalize
#:fail-unless (List? #'ty_lst)
(raise (exn:fail:type:infer
(format "~a (~a:~a): Inferred ~a type for nil, which is not a List."
(syntax-source stx) (syntax-line stx) (syntax-column stx)
(type->str #'ty_lst))
(current-continuation-marks)))
#:with (~List τ) #'ty_lst
( null : (List τ))]
[_:id #:fail-when #t
(raise (exn:fail:type:infer
(format "~a (~a:~a): nil requires type annotation"
(syntax-source stx) (syntax-line stx) (syntax-column stx))
(current-continuation-marks)))
#'(void)])
(define-typed-syntax cons/tc #:export-as cons
[(_ e1 e2)
#:with [e1- τ1] (infer+erase #'e1)
; #:with e2ann (add-expected-type #'e2 #'(List τ1))
#:with (e2- (τ2)) ( (add-expected e2 (List τ1)) as List)
#:fail-unless (typecheck? #'τ1 #'τ2)
(format "trying to cons expression ~a with type ~a to list ~a with type ~a\n"
(syntax->datum #'e1) (type->str #'τ1)
(syntax->datum #'e2) (type->str #'(List τ2)))
;; propagate up inferred types of variables
#:with env (stx-flatten (filter (λ (x) x) (stx-map get-env #'(e1- e2-))))
#:with result-cons (add-env #'(cons e1- e2-) #'env)
( result-cons : (List τ1))])
(define-typed-syntax isnil
[(_ e)
#:with (e- _) ( e as List)
( (null? e-) : Bool)])
(define-typed-syntax head
[(_ e)
#:with (e- (τ)) ( e as List)
( (car e-) : τ)])
(define-typed-syntax tail
[(_ e)
#:with (e- τ-lst) (infer+erase #'e)
#:when (List? #'τ-lst)
( (cdr e-) : τ-lst)])
(define-typed-syntax list/tc #:export-as list
[(_) #'nil/tc]
[(~and lst (_ x . rst)) ; has expected type
#:with expected-τ (get-expected-type #'lst)
#:when (syntax-e #'expected-τ)
#:with (~List τ) (local-expand #'expected-τ 'expression null)
#'(cons/tc (add-expected x τ) (list/tc . rst))]
[(_ x . rst) ; no expected type
#'(cons/tc x (list/tc . rst))])
(define-typed-syntax reverse
[(_ e)
#:with (e- τ-lst) (infer+erase #'e)
#:when (List? #'τ-lst)
( (reverse e-) : τ-lst)])
(define-typed-syntax length
[(_ e)
#:with (e- τ-lst) (infer+erase #'e)
#:when (List? #'τ-lst)
( (length e-) : Int)])
(define-typed-syntax list-ref
[(_ e n)
#:with (e- (ty)) ( e as List)
#:with n- ( n as Int)
( (list-ref e- n-) : ty)])
(define-typed-syntax member
[(_ v e)
#:with (e- (ty)) ( e as List)
#:with [v- ty_v] (infer+erase #'(add-expected v ty))
#:when (typecheck? #'ty_v #'ty)
( (member v- e-) : Bool)])