www

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

commit 67d28645f93d602954524c63ff6d1e9f2538cd3d
parent f7635af9b59d846e10f58102059acf41a328e58b
Author: stchang <stchang@ccs.neu.edu>
Date:   Thu, 22 Oct 2015 17:05:05 -0400

Merged in o+ (pull request #4)

Occurence types, improved

Diffstat:
Mtapl/stlc+occurrence.rkt | 334+++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------
Mtapl/stlc+sub.rkt | 4+++-
Mtapl/tests/stlc+occurrence-tests.rkt | 344+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Mtapl/tests/stlc+sub-tests.rkt | 4++++
4 files changed, 587 insertions(+), 99 deletions(-)

diff --git a/tapl/stlc+occurrence.rkt b/tapl/stlc+occurrence.rkt @@ -1,5 +1,7 @@ #lang s-exp "typecheck.rkt" (extends "stlc+sub.rkt" #:except #%datum) +;(extends "stlc+tup.rkt" #:except + #%datum and) +(extends "stlc+cons.rkt" #:except + #%datum and) ;; Calculus for occurrence typing. ;; - Types can be simple, or sets of simple types @@ -48,7 +50,7 @@ (if (null? τ*) #'Bot (τ-eval #`(∪ #,@τ*)))) - + (define (∖ τ1 τ2) (cond [(∪? τ1) @@ -67,42 +69,69 @@ ;; flatten nested unions (begin-for-syntax + (define τ-eval (current-type-eval)) (define (τ->symbol τ) - ;; TODO recurse for function types - (cadr (syntax->datum τ))) - - (define (∪-eval τ-stx) - (syntax-parse (τ-eval τ-stx) - [(~∪ τ-stx* ...) - ;; Recursively evaluate members - (define τ** - (for/list ([τ (in-list (syntax->list #'(τ-stx* ...)))]) - (let ([τ+ (∪-eval τ)]) - (if (∪? τ+) - (∪->list τ+) - (list τ+))))) - ;; Remove duplicates from the union, sort members - (define τ* - (sort - (remove-duplicates (apply append τ**) (current-type=?)) - symbol<? - #:key τ->symbol)) - ;; Check for empty & singleton lists - (define τ - (cond - [(null? τ*) - (raise-user-error 'τ-eval "~a (~a:~a) empty union type ~a\n" - (syntax-source τ-stx) (syntax-line τ-stx) (syntax-column τ-stx) - (syntax->datum τ-stx))] - [(null? (cdr τ*)) - #`#,(car τ*)] - [else - #`#,(cons #'∪ τ*)])) - (τ-eval τ)] + (syntax-parse τ + [(_ κ) + (syntax->datum #'κ)] + [(_ κ (_ () _ τ* ...)) + (define κ-str (symbol->string (syntax->datum #'κ))) + (define τ-str* + (map (compose1 symbol->string τ->symbol) (syntax->list #'(τ* ...)))) + (string->symbol + (string-append + (apply string-append "(" κ-str τ-str*) + ")"))] [_ - (τ-eval τ-stx)])) + (error 'τ->symbol (~a (syntax->datum τ)))])) + + (define ∪-eval + ;; Private helper: check that all functions have unique arities + ;; It's private because it assumes all τ* have been evaluated + (let ([assert-unique-arity-arrows + (lambda (τ*) + (for/fold ([seen '()]) + ([τ (in-list τ*)]) + (syntax-parse τ + [(~→ τ-dom* ... τ-cod) + (define arity (stx-length #'(τ-dom* ...))) + (when (memv arity seen) + (error '∪ (format "Cannot discriminate types in the union ~a. Multiple functions have arity ~a." (cons '∪ (map syntax->datum τ*)) arity))) + (cons arity seen)] + [_ seen])))]) + (lambda (τ-stx) + (syntax-parse (τ-eval τ-stx) + [(~∪ τ-stx* ...) + ;; Recursively evaluate members + (define τ** + (for/list ([τ (in-list (syntax->list #'(τ-stx* ...)))]) + (let ([τ+ (∪-eval τ)]) + (if (∪? τ+) + (∪->list τ+) + (list τ+))))) + ;; Remove duplicates from the union, sort members + (define τ* + (sort + (remove-duplicates (apply append τ**) (current-type=?)) + symbol<? + #:key τ->symbol)) + ;; Check for empty & singleton lists + (define τ + (cond + [(null? τ*) + (raise-user-error 'τ-eval "~a (~a:~a) empty union type ~a\n" + (syntax-source τ-stx) (syntax-line τ-stx) (syntax-column τ-stx) + (syntax->datum τ-stx))] + [(null? (cdr τ*)) + #`#,(car τ*)] + [else + (assert-unique-arity-arrows τ*) + #`#,(cons #'∪ τ*)])) + (τ-eval τ)] + [_ + (τ-eval τ-stx)])))) (current-type-eval ∪-eval)) ;; ----------------------------------------------------------------------------- @@ -120,36 +149,37 @@ [else (loop x* (cdr y*))]))) - (define sub? (current-sub?)) - - (define (∪-sub? τ1-stx τ2-stx) - (define τ1 ((current-type-eval) τ1-stx)) - (define τ2 ((current-type-eval) τ2-stx)) - (or (Bot? τ1) (Top? τ2) - (match `(,(∪? τ1) ,(∪? τ2)) - ['(#f #t) - ;; AMB : a<:b => a <: (U ... b ...) - (for/or ([τ (in-list (∪->list τ2))]) - (sub? τ1 τ))] - ['(#t #t) - (define τ1* (∪->list τ1)) - (define τ2* (∪->list τ2)) - (match `(,(length τ1*) ,(length τ2*)) - [`(,L1 ,L2) #:when (< L1 L2) - ;; - EXT : (U t' ...) <: (U t t' ...) - (subset? τ1* τ2* #:leq sub?)] - [`(,L1 ,L2) #:when (= L1 L2) - ;; - SUB : a<:b => (U a t' ...) <: (U b t' ...) - ;; `∪->list` guarantees same order on type members - ;; `sub?` is reflexive - (andmap sub? τ1* τ2*)] - [_ #f])] - ['(#t #f) - ;; - ALL : t... <: t' => (U t ...) <: t' - (andmap (lambda (τ) (sub? τ τ2)) (∪->list τ1))] - ['(#f #f) - (sub? τ1 τ2)]))) - + (define ∪-sub? + (let ([sub? (current-sub?)]) + (lambda (τ1-stx τ2-stx) + (define τ1 ((current-type-eval) τ1-stx)) + (define τ2 ((current-type-eval) τ2-stx)) + (or (Bot? τ1) (Top? τ2) + (match `(,(∪? τ1) ,(∪? τ2)) + ['(#f #t) + ;; AMB : a<:b => a <: (U ... b ...) + (for/or ([τ (in-list (∪->list τ2))]) + ((current-sub?) τ1 τ))] + ['(#t #t) + (define τ1* (∪->list τ1)) + (define τ2* (∪->list τ2)) + (match `(,(length τ1*) ,(length τ2*)) + [`(,L1 ,L2) #:when (< L1 L2) + ;; - EXT : (U t' ...) <: (U t t' ...) + (subset? τ1* τ2* #:leq (current-sub?))] + [`(,L1 ,L2) #:when (= L1 L2) + ;; - SUB : a<:b => (U a t' ...) <: (U b t' ...) + ;; `∪->list` guarantees same order on type members + ;; `sub?` is reflexive + (andmap (current-sub?) τ1* τ2*)] + [_ #f])] + ['(#t #f) + ;; - ALL : t... <: t' => (U t ...) <: t' + (andmap (lambda (τ) ((current-sub?) τ τ2)) (∪->list τ1))] + ['(#f #f) + ;; Fall back to OLD sub + (sub? τ1 τ2)]))))) + (current-sub? ∪-sub?) (current-typecheck-relation (current-sub?)) ) @@ -160,47 +190,165 @@ ;; Makes it easy to add a new filter & avoids duplicating this map (begin-for-syntax - (define (simple-Π τ) - (syntax-parse (τ-eval τ) - [~Boolean - #'boolean?] - [~Int - #'integer?] - [~Str - #'string?] - [~Num - #'number?] - [~Nat - #'(lambda (n) (and (integer? n) (not (negative? n))))] - [_ - (error 'Π "Cannot make filter for type ~a\n" (syntax->datum τ))])) - (define current-Π (make-parameter simple-Π))) + (define current-Π (make-parameter (lambda (x) (error 'Π)))) + + (define (type->filter τ) + (define f ((current-Π) τ)) + (unless f + (error 'τ->filter (format "Could not express type '~a' as a filter." (syntax->datum τ)))) + f) + + (define (type*->filter* τ*) + (map (current-Π) τ*)) + + (define (simple-Π τ) + (syntax-parse (τ-eval τ) + [~Boolean + #'boolean?] + [~Int + #'integer?] + [~Str + #'string?] + [~Num + #'number?] + [~Nat + #'(lambda (n) (and (integer? n) (not (negative? n))))] + [(~→ τ* ... τ) + (define k (stx-length #'(τ* ...))) + #`(lambda (f) (and (procedure? f) (procedure-arity-includes? f #,k #f)))] + [(~∪ τ* ...) + (define filter* (type*->filter* (syntax->list #'(τ* ...)))) + #`(lambda (v) (for/or ([f (in-list (list #,@filter*))]) (f v)))] + [_ + (error 'Π "Cannot make filter for type ~a\n" (syntax->datum τ))])) + (current-Π simple-Π) + +) ;; (test (τ ? x) e1 e2) -;; TODO: -;; - check if τ0 is a union type -;; - check if τ-filter is a subtype of τ0 ;; - drop absurd branches? ;; - allow x not identifier (1. does nothing 2. latent filters) (define-typed-syntax test #:datum-literals (?) - [(_ [τ-filter:type ? x-stx:id] e1 e2) - ;; Get the filter type, evaluate to a runtime predicate - #:with f ((current-Π) #'τ-filter) - #:fail-unless (syntax-e #'f) - (format "Could not express type '~a' as a filter." #'τ-filter-stx) - ;; TypeCheck e0:normally, e1:positive, e2:negative + ;; -- THIS CASE BELONGS IN A NEW FILE + [(_ [τ0+:type ? (unop x-stx:id n-stx:nat)] e1 e2) + ;; 1. Check that we're using a known eliminator + #:when (free-identifier=? #'proj #'unop) + ;; 2. Make sure we're filtering with a valid type + #:with f (type->filter #'τ0+) + ;; 3. Typecheck the eliminator call. Remember the type & apply the filter. + ;; (This type is PROBABLY a union -- else why bother testing!) + #:with (e0+ τ0) (infer+erase #'(unop x-stx n-stx)) + #:with τ0- (∖ #'τ0 #'τ0+) + ;; 4. Build the +/- types for our identifier; the thing we apply the elim. + test to + ;; We know that x has a pair type because (proj x n) typechecked + #:with (x (~× τi* ...)) (infer+erase #'x-stx) + #:with τ+ #`(× #,@(replace-at (syntax->list #'(τi* ...)) (syntax-e #'n-stx) #'τ0+)) + #:with τ- #`(× #,@(replace-at (syntax->list #'(τi* ...)) (syntax-e #'n-stx) #'τ0-)) + ;; 5. Check the branches with the refined types + #:with [x1 e1+ τ1] (infer/ctx+erase #'([x-stx : τ+]) #'e1) + #:with [x2 e2+ τ2] (infer/ctx+erase #'([x-stx : τ-]) #'e2) + ;; 6. Desugar, replacing the filtered identifier + (⊢ (if (f e0+) + ((lambda x1 e1+) x-stx) + ((lambda x2 e2+) x-stx)) + : (∪ τ1 τ2))] + ;; TODO lists + ;; For now, we can't express the type (List* A (U A B)), so our filters are too strong + ;; -- THE ORIGINAL + [(_ [τ0+:type ? x-stx:id] e1 e2) + #:with f (type->filter #'τ0+) #:with (x τ0) (infer+erase #'x-stx) - #:with [x1 e1+ τ1] (infer/ctx+erase #'([x-stx : τ-filter]) #'e1) - #:with [x2 e2+ τ2] (infer/ctx+erase #`([x-stx : #,(∖ #'τ0 #'τ-filter)]) #'e2) + #:with τ0- (∖ #'τ0 #'τ0+) + #:with [x1 e1+ τ1] (infer/ctx+erase #'([x-stx : τ0+]) #'e1) + #:with [x2 e2+ τ2] (infer/ctx+erase #'([x-stx : τ0-]) #'e2) ;; Expand to a conditional, using the runtime predicate (⊢ (if (f x-stx) ((lambda x1 e1+) x-stx) ((lambda x2 e2+) x-stx)) : (∪ τ1 τ2))]) -;; - TEST function filters (delayed filters?) -;; - disallow (U (-> ...) (-> ...)) -;; - TEST latent filters -- listof BLAH +;; ============================================================================= +;; === BELONGS IN A NEW FILE + +;; (extends "stlc+occurrence.rkt"); #:rename [test ot:test]) +;; (extends "stlc+tup.rkt" #:except + #%datum) + +(define-for-syntax (replace-at τ* n τ-new) + (for/list ([τ-old (in-list τ*)] + [i (in-naturals)]) + (if (= i n) + τ-new + τ-old))) -;; - integrate with sysf +;; Add subtyping for tuples +(begin-for-syntax + (define ×-sub? + (let ([sub? (current-sub?)]) + (lambda (τ1-stx τ2-stx) + (define τ1 ((current-type-eval) τ1-stx)) + (define τ2 ((current-type-eval) τ2-stx)) + (or (Bot? τ1) (Top? τ2) + (syntax-parse `(,τ1 ,τ2) + [((~× τi1* ...) + (~× τi2* ...)) + (and (stx-length=? #'(τi1* ...) + #'(τi2* ...)) + ;; Gotta use (current-sub?), because products may be recursive + (stx-andmap (current-sub?) #'(τi1* ...) #'(τi2* ...)))] + [_ + (sub? τ1 τ2)]))))) + (current-sub? ×-sub?) + (current-typecheck-relation (current-sub?))) + +;; --- Update Π for products +(begin-for-syntax + (define π-Π + (let ([Π (current-Π)]) + (lambda (τ) + (syntax-parse (τ-eval τ) + [(~× τ* ...) + (define filter* (type*->filter* (syntax->list #'(τ* ...)))) + #`(lambda (v*) + (and (list? v*) + (for/and ([v (in-list v*)] + [f (in-list (list #,@filter*))]) + (f v))))] + [_ ;; Fall back + (Π τ)])))) + (current-Π π-Π)) +;; ============================================================================= +;; === Lists + +;; Subtyping for lists +(begin-for-syntax + (define list-sub? + (let ([sub? (current-sub?)]) + (lambda (τ1-stx τ2-stx) + (define τ1 ((current-type-eval) τ1-stx)) + (define τ2 ((current-type-eval) τ2-stx)) + (or (Bot? τ1) (Top? τ2) + (syntax-parse `(,τ1 ,τ2) + [((~List τi1) + (~List τi2)) + ((current-sub?) #'τi1 #'τi2)] + [_ + (sub? τ1 τ2)]))))) + (current-sub? list-sub?) + (current-typecheck-relation (current-sub?))) + +;; --- Update Π for lists +(begin-for-syntax + (define list-Π + (let ([Π (current-Π)]) + (lambda (τ) + (syntax-parse (τ-eval τ) + [(~List τi) + (define f ((current-Π) #'τi)) + #`(lambda (v*) + (and (list? v*) + (for/and ([v (in-list v*)]) + (#,f v))))] + [_ ;; Fall back + (Π τ)])))) + (current-Π list-Π)) diff --git a/tapl/stlc+sub.rkt b/tapl/stlc+sub.rkt @@ -42,7 +42,9 @@ (Top? τ2))) (define current-sub? (make-parameter sub?)) (current-typecheck-relation sub?) - (define (subs? τs1 τs2) (stx-andmap (current-sub?) τs1 τs2)) + (define (subs? τs1 τs2) + (and (stx-length=? τs1 τs2) + (stx-andmap (current-sub?) τs1 τs2))) (define-syntax (define-sub-relation stx) (syntax-parse stx #:datum-literals (<: =>) diff --git a/tapl/tests/stlc+occurrence-tests.rkt b/tapl/tests/stlc+occurrence-tests.rkt @@ -207,6 +207,15 @@ : Boolean ⇒ #t) +;; Can refine non-union types +(check-type-and-result + ((λ ([x : Top]) + (test (Str ? x) + x + "nope")) + "yes") + : Str ⇒ "yes") + ;; ----------------------------------------------------------------------------- ;; --- misc subtyping + filters (regression tests) (check-type @@ -270,15 +279,340 @@ : Nat ⇒ 2) ;; ----------------------------------------------------------------------------- -;; --- TODO Filter values (should do nothing) +;; --- Functions in union + +(check-type (λ ([x : (∪ Int (∪ Nat) (∪ (→ Int Str Int)) (→ (→ (→ Int Int)) Int))]) #t) + : (→ (∪ Int Nat (→ Int Str Int) (→ (→ (→ Int Int)) Int)) Boolean)) + +(check-type (λ ([x : (∪ Int (→ Int Int))]) #t) + : (→ Int Boolean)) + +;; --- filter functions +(check-type + (λ ([x : (∪ Int (→ Int Int))]) + (test ((→ Int Int) ? x) + (x 0) + x)) + : (→ (∪ Int (→ Int Int)) Int)) + +(check-type + (λ ([x : (∪ (→ Int Int Int) (→ Int Int))]) + (test ((→ Int Int) ? x) + (x 0) + (test (Int ? x) + x + (x 1 0)))) + : (→ (∪ (→ Int Int Int) (→ Int Int)) Int)) + +(check-type-and-result + ((λ ([x : (∪ (→ Int Int Int) (→ Int Int) Int)]) + (test ((→ Int Int) ? x) + (x 0) + (test (Int ? x) + x + (x 1 0)))) 1) + : Int ⇒ 1) + +(check-type-and-result + ((λ ([x : (∪ (→ Int Int Int) (→ Int Int) Int)]) + (test ((→ Int Int) ? x) + (x 0) + (test (Int ? x) + x + (x 1 0)))) (λ ([y : Int]) 5)) + : Int ⇒ 5) + +(check-type-and-result + ((λ ([x : (∪ (→ Int Int Int) (→ Int Int) Int)]) + (test ((→ Int Int) ? x) + (x 0) + (test (Int ? x) + x + (x 1 0)))) (λ ([y : Int] [z : Int]) z)) + : Int ⇒ 0) + +;; --- disallow same-arity functions +(typecheck-fail + (λ ([x : (∪ (→ Int Int) (→ Str Str))]) 1) + #:with-msg "Cannot discriminate") + +;; ----------------------------------------------------------------------------- +;; --- Filter with unions + +(check-type + (λ ([x : (∪ Int Str)]) + (test ((∪ Int Str) ? x) + x + "nope")) + : (→ (∪ Int Str) (∪ Int Str))) + +(check-type + (λ ([x : (∪ Int Str Boolean)]) + (test ((∪ Int Str) ? x) + x + "Nope")) + : (→ (∪ Int Str Boolean) (∪ Int Str))) + +(check-type + (λ ([x : (∪ Int Str Boolean)]) + (test ((∪ Int Str) ? x) + (test (Str ? x) + "yes" + "int") + "bool")) + : (→ (∪ Int Str Boolean) Str)) + +(check-type-and-result + ((λ ([x : (∪ Str Boolean)]) + (test ((∪ Int Nat Num) ? x) + x + (+ 1 2))) "hi") + : Num ⇒ 3) + +(check-type-and-result + ((λ ([x : (∪ Str Int Boolean)]) + (test ((∪ Int Str) ? x) + x + "error")) 1) + : (∪ Str Int) ⇒ 1) + +(check-type-and-result + ((λ ([x : (∪ Str Int Boolean)]) + (test ((∪ Int Str) ? x) + x + "error")) "hi") + : (∪ Int Str) ⇒ "hi") + +;; ----------------------------------------------------------------------------- +;; --- Subtyping products + +(check-type (tup 1) : (× Nat)) +(check-type (tup 1) : (× Int)) +(check-type (tup 1) : (× Num)) +(check-type (tup 1) : (× Top)) +(check-type (tup 1) : Top) + +(check-not-type (tup 1) : Boolean) +(check-not-type (tup 1) : Str) +(check-not-type (tup 1) : (× Str)) +(check-not-type (tup 1) : (× Int Str)) +(check-not-type (tup 1) : Bot) + +(check-type (tup 1 2 3) : (× Int Nat Num)) +(check-type (tup 1 2 3) : (× Num Nat Num)) +(check-type (tup 1 2 3) : (× Top Top Num)) +(check-type (tup 1 "2" 3) : (× Int Top Int)) + +(check-not-type (tup 1 2 3) : (× Nat Nat Str)) + +;; ----------------------------------------------------------------------------- +;; --- Latent filters (on products) + +(check-type + (λ ([v : (× (∪ Int Str) Int)]) + (test (Int ? (proj v 0)) + (+ (proj v 0) (proj v 1)) + 0)) + : (→ (× (∪ Int Str) Int) Num)) + +(check-type-and-result + ((λ ([v : (× (∪ Int Str) Int)]) + (test (Int ? (proj v 0)) + (+ (proj v 0) (proj v 1)) + 0)) + (tup ((λ ([x : (∪ Int Str)]) x) -2) -3)) + : Num ⇒ -5) +(check-type-and-result + ((λ ([v : (× (∪ Int Str) Int)]) + (test (Int ? (proj v 0)) + (+ (proj v 0) (proj v 1)) + 0)) + (tup "hi" -3)) + : Num ⇒ 0) + +;; --- Use a product as filter + +(check-type + (λ ([x : (∪ Int (× Int Int Int))]) + (test (Int ? x) + (+ 1 x) + (+ (proj x 0) (+ (proj x 1) (proj x 2))))) + : (→ (∪ (× Int Int Int) Int) Num)) + +(check-type-and-result + ((λ ([x : (∪ Int (× Int Int Int))]) + (test (Int ? x) + (+ 1 x) + (+ (proj x 0) (+ (proj x 1) (proj x 2))))) + 0) + : Num ⇒ 1) + +(check-type-and-result + ((λ ([x : (∪ Int (× Int Int Int))]) + (test (Int ? x) + (+ 1 x) + (+ (proj x 0) (+ (proj x 1) (proj x 2))))) + (tup 2 2 2)) + : Num ⇒ 6) + +(check-type-and-result + ((λ ([x : (∪ Int (× Str Nat) (× Int Int Int))]) + (test (Int ? x) + (+ 1 x) + (test ((× Int Int Int) ? x) + (+ (proj x 0) (+ (proj x 1) (proj x 2))) + (proj x 1)))) + (tup 2 2 2)) + : Num ⇒ 6) + +(check-type-and-result + ((λ ([x : (∪ Int (× Str Nat) (× Int Int Int))]) + (test (Int ? x) + (+ 1 x) + (test ((× Int Int Int) ? x) + (+ (proj x 0) (+ (proj x 1) (proj x 2))) + (proj x 1)))) + (tup "yolo" 33)) + : Num ⇒ 33) + +;; -- All together now + +(check-type-and-result + ((λ ([x : (∪ Int (× Boolean Boolean) (× Int (∪ Str Int)))]) + (test (Int ? x) + "just an int" + (test ((× Boolean Boolean) ? x) + "pair of bools" + (test (Str ? (proj x 1)) + (proj x 1) + "pair of ints")))) + (tup 33 "success")) + : Str ⇒ "success") + +(check-type-and-result + ((λ ([x : (∪ Int (× Int Int) (× Int (∪ Str Int)))]) + (test (Int ? x) + "just an int" + (test ((× Int Int) ? x) + "pair of ints" + (test (Str ? (proj x 1)) + (proj x 1) + "another pair of ints")))) + (tup 33 "success")) + : Str ⇒ "success") + +;; ----------------------------------------------------------------------------- +;; --- Filter lists + +(check-type + (λ ([x : (List (∪ Int Str))]) + (test ((List Str) ? x) + x + #f)) + : (→ (List (∪ Int Str)) (∪ Boolean (List Str)))) + +;; -- -subtyping lists +(check-type + (cons 1 (nil {Nat})) + : (List Int)) + +(check-type + ((λ ([filter/3 : (→ (List (∪ Int Str)) (List Int))] + [add*/3 : (→ Num (List Num) (List Num))] + [xs : (× (∪ Int Str) (∪ Int Str) (∪ Int Str))]) + (add*/3 5 (filter/3 (cons (proj xs 0) + (cons (proj xs 1) + (cons (proj xs 2) + (nil {(∪ Str Int)}))))))) + ;; filter (okay this is a little tricky for recursion) + (λ ([xs : (List (∪ Int Str))]) + ((λ ([v1 : (∪ Int Str)] + [v2 : (∪ Int Str)] + [v3 : (∪ Int Str)]) + (test (Int ? v1) + (cons v1 (test (Int ? v2) + (cons v2 (test (Int ? v3) + (cons v3 (nil {Int})) + (nil {Int}))) + (test (Int ? v3) + (cons v3 (nil {Int})) + (nil {Int})))) + (test (Int ? v2) + (cons v2 (test (Int ? v3) + (cons v3 (nil {Int})) + (nil {Int}))) + (test (Int ? v3) + (cons v3 (nil {Int})) + (nil {Int}))))) + (head xs) (head (tail xs)) (head (tail (tail xs))))) + ;; add3 + (λ ([n : Num] [xs : (List Num)]) + (cons (+ n (head xs)) + (cons (+ n (head (tail xs))) + (cons (+ n (head (tail (tail xs)))) + (nil {Num}))))) + ;; xs (3-tuple) + (tup 1 "foo" 3)) + : (List Num)) + +;; ----------------------------------------------------------------------------- +;; --- ICFP'10 examples + +;; -- Exaple 1 (x can have any type) +(check-type + (λ ([x : Top]) + (test (Num ? x) + (+ 1 x) + 0)) + : (→ Top Num)) + +;; -- Example 2 +(check-type + (λ ([x : (∪ Str Num)] + [str-length : (→ Str Num)]) + (test (Num ? x) + (+ 1 x) + (str-length x))) + : (→ (∪ Str Num) (→ Str Num) Num)) + +;; -- TODO Example 3 (requires IF) ;; (check-type -;; (test (Int ? 1) #t #f) -;; : Boolean) +;; (λ ([member : (→ Num (List Num) Boolean)]) +;; (λ ([x : Num] [l : (List Num)]) +;; (if (member x l) +;; <compute with x> +;; <fail>))) +;; : <compute-result> + +;; -- Example 4 +(check-type + (λ ([x : (∪ Num Str Top)] [f : (→ (∪ Num Str) Num)]) + (test ((∪ Num Str) ? x) + (f x) + 0)) + : (→ (∪ Num Str Top) (→ (∪ Num Str) Num) Num)) + +;; Exmample 10 (we don't allow non-homogenous lists, so need to select head before filtering) +(check-type + (λ ([p : (List (∪ Nat Str))]) + ((λ ([x : (∪ Nat Str)]) + (test (Num ? x) + (+ 1 x) + 7)) + (head p))) + : (→ (List (∪ Nat Str)) Num)) ;; ----------------------------------------------------------------------------- -;; --- TODO Filter functions +;; --- TODO CPS filters + +;; ----------------------------------------------------------------------------- +;; --- TODO Filter on values (should do nothing) + +;; (check-type +;; (test (Int ? 1) #t #f) +;; : Boolean) ;; ----------------------------------------------------------------------------- -;; --- TODO Latent filters (on data structures) +;; --- TODO Values as filters (check equality) diff --git a/tapl/tests/stlc+sub-tests.rkt b/tapl/tests/stlc+sub-tests.rkt @@ -57,3 +57,7 @@ (typecheck-fail (λ ([x : (→ Int Int)]) (+ x x))) ; x should be Int (typecheck-fail ((λ ([x : Int] [y : Int]) y) 1)) ; wrong number of args (check-type ((λ ([x : Int]) (+ x x)) 10) : Num ⇒ 20) + +(check-not-type (λ ([x : Int]) x) : Int) +(check-not-type (λ ([x : Int] [y : Int]) x) : (→ Int Int)) +(check-not-type (λ ([x : Int]) x) : (→ Int Int Int Int))