macrotypes/turnstile/examples/mlish.rkt
Stephen Chang 31c3bba5c9 add current-host-lang; fix reuse to work with non-strs
- other various stx conveniences
- provide more require/provide forms in default mod-beg
- fix tests and examples to work with current-host-lang
2017-03-22 17:04:48 -04:00

1434 lines
58 KiB
Racket
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#lang turnstile/lang
(require
(postfix-in - racket/fixnum)
(postfix-in - racket/flonum)
(for-syntax macrotypes/type-constraints macrotypes/variance-constraints))
(extends
"ext-stlc.rkt"
#:except define #%app λ #%datum begin
+ - * void = zero? sub1 add1 not let let* and
#:rename [~→ ~ext-stlc:→])
(reuse inst #:from "sysf.rkt")
(require (only-in "ext-stlc.rkt" →?))
(require (only-in "sysf.rkt" ~∀ ∀? Λ))
(reuse × tup proj define-type-alias #:from "stlc+rec-iso.rkt")
(require (only-in "stlc+rec-iso.rkt" ~× ×?))
(provide (rename-out [ext-stlc:and and] [ext-stlc:#%datum #%datum]))
(reuse member length reverse list-ref cons nil isnil head tail list
#:from "stlc+cons.rkt")
(require (prefix-in stlc+cons: (only-in "stlc+cons.rkt" list cons nil)))
(require (only-in "stlc+cons.rkt" ~List List? List))
(reuse ref deref := Ref #:from "stlc+box.rkt")
(require (rename-in (only-in "stlc+reco+var.rkt" tup proj ×)
[tup rec] [proj get] [× ××]))
(provide rec get ××)
;; for pattern matching
(require (prefix-in stlc+cons: (only-in "stlc+cons.rkt" list)))
(require (prefix-in stlc+tup: (only-in "stlc+tup.rkt" tup)))
;; ML-like language
;; - top level recursive functions
;; - user-definable algebraic datatypes
;; - pattern matching
;; - (local) type inference
(provide →/test
define-type
List Channel Thread Vector Sequence Hash String-Port Input-Port Regexp
match2)
;; providing version of define-typed-syntax
(define-syntax (define-typed-syntax stx)
(syntax-parse stx
[(_ name:id #:export-as out-name:id . rst)
#'(begin-
(provide- (rename-out [name out-name]))
(define-typerule name . rst))] ; define-typerule doesnt provide
[(_ name:id . rst)
#'(define-typed-syntax name #:export-as name . rst)]
[(_ (name:id . pat) . rst)
#'(define-typed-syntax name #:export-as name [(_ . pat) . rst])]))
(module+ test
(require (for-syntax rackunit)))
;; creating possibly polymorphic types
;; ?∀ only wraps a type in a forall if there's at least one type variable
(define-syntax ?∀
(lambda (stx)
(syntax-case stx ()
[(?∀ () body)
#'body]
[(?∀ (X ...) body)
#'( (X ...) body)])))
;; ?Λ only wraps an expression in a Λ if there's at least one type variable
(define-syntax
(lambda (stx)
(syntax-case stx ()
[( () body)
#'body]
[( (X ...) body)
#'(Λ (X ...) body)])))
(begin-for-syntax
;; matching possibly polymorphic types
(define-syntax ~?∀
(pattern-expander
(lambda (stx)
(syntax-case stx ()
[(?∀ vars-pat body-pat)
#'(~or (~∀ vars-pat body-pat)
(~and (~not (~∀ _ _))
(~parse vars-pat #'())
body-pat))]))))
;; find-free-Xs : (Stx-Listof Id) Type -> (Listof Id)
;; finds the free Xs in the type
(define (find-free-Xs Xs ty)
(for/list ([X (in-stx-list Xs)] #:when (stx-contains-id? ty X)) X))
;; solve for Xs by unifying quantified fn type with the concrete types of stx's args
;; stx = the application stx = (#%app e_fn e_arg ...)
;; tyXs = input and output types from fn type
;; ie (typeof e_fn) = (-> . tyXs)
;; It infers the types of arguments from left-to-right,
;; and it expands and returns all of the arguments.
;; It returns list of 3 values if successful, else throws a type error
;; - a list of all the arguments, expanded
;; - a list of all the type variables
;; - the constraints for substituting the types
(define (solve Xs tyXs stx)
(syntax-parse tyXs
[(τ_inX ... τ_outX)
;; generate initial constraints with expected type and τ_outX
#:with (~?∀ Vs expected-ty)
(and (get-expected-type stx)
((current-type-eval) (get-expected-type stx)))
(define initial-cs
(if (and (syntax-e #'expected-ty) (stx-null? #'Vs))
(add-constraints Xs '() (list (list #'expected-ty #'τ_outX)))
'()))
(syntax-parse stx
[(_ e_fn . args)
(define-values (as- cs)
(for/fold ([as- null] [cs initial-cs])
([a (in-stx-list #'args)]
[tyXin (in-stx-list #'(τ_inX ...))])
(define ty_in (inst-type/cs Xs cs tyXin))
(define/with-syntax [a- ty_a]
(infer+erase (if (empty? (find-free-Xs Xs ty_in))
(add-expected-ty a ty_in)
a)))
(values
(cons #'a- as-)
(add-constraints Xs cs (list (list ty_in #'ty_a))
(list (list (inst-type/cs/orig
Xs cs ty_in
(λ (id1 id2)
(equal? (syntax->datum id1)
(syntax->datum id2))))
#'ty_a))))))
(list (reverse as-) Xs cs)])]))
(define (mk-app-poly-infer-error stx expected-tys given-tys e_fn)
(format (string-append
"Could not infer instantiation of polymorphic function ~s.\n"
" expected: ~a\n"
" given: ~a")
(syntax->datum (get-orig e_fn))
(string-join (stx-map type->str expected-tys) ", ")
(string-join (stx-map type->str given-tys) ", ")))
;; covariant-Xs? : Type -> Bool
;; Takes a possibly polymorphic type, and returns true if all of the
;; type variables are in covariant positions within the type, false
;; otherwise.
(define (covariant-Xs? ty)
(syntax-parse ((current-type-eval) ty)
[(~?∀ Xs ty)
(for/and ([X (in-stx-list #'Xs)])
(covariant-X? X #'ty))]))
;; find-X-variance : Id Type [Variance] -> Variance
;; Returns the variance of X within the type ty
(define (find-X-variance X ty [ctxt-variance covariant])
(match (find-variances (list X) ty ctxt-variance)
[(list variance) variance]))
;; covariant-X? : Id Type -> Bool
;; Returns true if every place X appears in ty is a covariant position, false otherwise.
(define (covariant-X? X ty)
(variance-covariant? (find-X-variance X ty covariant)))
;; contravariant-X? : Id Type -> Bool
;; Returns true if every place X appears in ty is a contravariant position, false otherwise.
(define (contravariant-X? X ty)
(variance-contravariant? (find-X-variance X ty covariant)))
;; find-variances : (Listof Id) Type [Variance] -> (Listof Variance)
;; Returns the variances of each of the Xs within the type ty,
;; where it's already within a context represented by ctxt-variance.
(define (find-variances Xs ty [ctxt-variance covariant])
(syntax-parse ty
[A:id
(for/list ([X (in-list Xs)])
(cond [(free-identifier=? X #'A) ctxt-variance]
[else irrelevant]))]
[(~Any tycons)
(make-list (length Xs) irrelevant)]
[(~?∀ () (~Any tycons τ ...))
#:when (get-arg-variances #'tycons)
#:when (stx-length=? #'[τ ...] (get-arg-variances #'tycons))
(define τ-ctxt-variances
(for/list ([arg-variance (in-list (get-arg-variances #'tycons))])
(variance-compose ctxt-variance arg-variance)))
(for/fold ([acc (make-list (length Xs) irrelevant)])
([τ (in-stx-list #'[τ ...])]
[τ-ctxt-variance (in-list τ-ctxt-variances)])
(map variance-join
acc
(find-variances Xs τ τ-ctxt-variance)))]
[ty
#:when (not (for/or ([X (in-list Xs)])
(stx-contains-id? #'ty X)))
(make-list (length Xs) irrelevant)]
[_ (make-list (length Xs) invariant)]))
;; find-variances/exprs : (Listof Id) Type [Variance-Expr] -> (Listof Variance-Expr)
;; Like find-variances, but works with Variance-Exprs instead of
;; concrete variance values.
(define (find-variances/exprs Xs ty [ctxt-variance covariant])
(syntax-parse ty
[A:id
(for/list ([X (in-list Xs)])
(cond [(free-identifier=? X #'A) ctxt-variance]
[else irrelevant]))]
[(~Any tycons)
(make-list (length Xs) irrelevant)]
[(~?∀ () (~Any tycons τ ...))
#:when (get-arg-variances #'tycons)
#:when (stx-length=? #'[τ ...] (get-arg-variances #'tycons))
(define τ-ctxt-variances
(for/list ([arg-variance (in-list (get-arg-variances #'tycons))])
(variance-compose/expr ctxt-variance arg-variance)))
(for/fold ([acc (make-list (length Xs) irrelevant)])
([τ (in-list (syntax->list #'[τ ...]))]
[τ-ctxt-variance (in-list τ-ctxt-variances)])
(map variance-join/expr
acc
(find-variances/exprs Xs τ τ-ctxt-variance)))]
[ty
#:when (not (for/or ([X (in-list Xs)])
(stx-contains-id? #'ty X)))
(make-list (length Xs) irrelevant)]
[_ (make-list (length Xs) invariant)]))
;; current-variance-constraints : (U False (Mutable-Setof Variance-Constraint))
;; If this is false, that means that infer-variances should return concrete Variance values.
;; If it's a mutable set, that means that infer-variances should mutate it and return false,
;; and type constructors should return the list of variance vars.
(define current-variance-constraints (make-parameter #false))
;; infer-variances :
;; ((-> Stx) -> Stx) (Listof Variance-Var) (Listof Id) (Listof Type-Stx)
;; -> (U False (Listof Variance))
(define (infer-variances with-variance-vars-okay variance-vars Xs τs)
(cond
[(current-variance-constraints)
(define variance-constraints (current-variance-constraints))
(define variance-exprs
(for/fold ([exprs (make-list (length variance-vars) irrelevant)])
([τ (in-list τs)])
(define/syntax-parse (~?∀ Xs* τ*)
;; This can mutate variance-constraints!
;; This avoids causing an infinite loop by having the type
;; constructors provide with-variance-vars-okay so that within
;; this call they declare variance-vars for their variances.
(with-variance-vars-okay
(λ () ((current-type-eval) #`( #,Xs #,τ)))))
(map variance-join/expr
exprs
(find-variances/exprs (syntax->list #'Xs*) #'τ* covariant))))
(for ([var (in-list variance-vars)]
[expr (in-list variance-exprs)])
(set-add! variance-constraints (variance= var expr)))
#f]
[else
(define variance-constraints (mutable-set))
;; This will mutate variance-constraints!
(parameterize ([current-variance-constraints variance-constraints])
(infer-variances with-variance-vars-okay variance-vars Xs τs))
(define mapping
(solve-variance-constraints variance-vars
(set->list variance-constraints)
(variance-mapping)))
(for/list ([var (in-list variance-vars)])
(variance-mapping-ref mapping var))]))
;; make-arg-variances-proc :
;; (Listof Variance-Var) (Listof Id) (Listof Type-Stx) -> (Stx -> (U (Listof Variance)
;; (Listof Variance-Var)))
(define (make-arg-variances-proc arg-variance-vars Xs τs)
;; variance-vars-okay? : (Parameterof Boolean)
;; A parameter that determines whether or not it's okay for
;; this type constructor to return a list of Variance-Vars
;; for the variances.
(define variance-vars-okay? (make-parameter #false))
;; with-variance-vars-okay : (-> A) -> A
(define (with-variance-vars-okay f)
(parameterize ([variance-vars-okay? #true])
(f)))
;; arg-variances : (Boxof (U False (List Variance ...)))
;; If false, means that the arg variances have not been
;; computed yet. Otherwise, stores the complete computed
;; variances for the arguments to this type constructor.
(define arg-variances (box #f))
;; arg-variances-proc : Stx -> (U (Listof Variance) (Listof Variance-Var))
(define (arg-variance-proc stx)
(or (unbox arg-variances)
(cond
[(variance-vars-okay?)
arg-variance-vars]
[else
(define inferred-variances
(infer-variances
with-variance-vars-okay
arg-variance-vars
Xs
τs))
(cond [inferred-variances
(set-box! arg-variances inferred-variances)
inferred-variances]
[else
arg-variance-vars])])))
arg-variance-proc)
;; compute unbound tyvars in one unexpanded type ty
(define (compute-tyvar1 ty)
(syntax-parse ty
[X:id #'(X)]
[() #'()]
[(C t ...) (stx-appendmap compute-tyvar1 #'(t ...))]))
;; computes unbound ids in (unexpanded) tys, to be used as tyvars
(define (compute-tyvars tys)
(define Xs (stx-appendmap compute-tyvar1 tys))
(filter
(lambda (X)
(with-handlers
([exn:fail:syntax:unbound? (lambda (e) #t)]
[exn:fail:type:infer? (lambda (e) #t)])
(let ([X+ ((current-type-eval) X)])
(not (or (tyvar? X+) (type? X+))))))
(stx-remove-dups Xs))))
;; define --------------------------------------------------
;; for function defs, define infers type variables
;; - since the order of the inferred type variables depends on expansion order,
;; which is not known to programmers, to make the result slightly more
;; intuitive, we arbitrarily sort the inferred tyvars lexicographically
(define-typed-syntax define
[(_ x:id e)
[ e e- τ]
#:with y (generate-temporary)
--------
[ (begin-
(define-syntax x (make-rename-transformer ( y : τ)))
(define- y e-))]]
; explicit "forall"
[(_ Ys (f:id [x:id (~datum :) τ] ... (~or (~datum ->) (~datum )) τ_out)
e_body ... e)
#:when (brace? #'Ys)
;; TODO; remove this code duplication
#:with g (add-orig (generate-temporary #'f) #'f)
#:with e_ann #'(add-expected e τ_out)
#:with (τ+orig ...) (stx-map (λ (t) (add-orig t t)) #'(τ ... τ_out))
;; TODO: check that specified return type is correct
;; - currently cannot do it here; to do the check here, need all types of
;; top-lvl fns, since they can call each other
#:with (~and ty_fn_expected (~?∀ _ (~ext-stlc:→ _ ... out_expected)))
((current-type-eval) #'(?∀ Ys (ext-stlc:→ τ+orig ...)))
--------
[ (begin-
(define-syntax f (make-rename-transformer ( g : ty_fn_expected)))
(define- g
(Λ Ys (ext-stlc:λ ([x : τ] ...) (ext-stlc:begin e_body ... e_ann)))))]]
;; alternate type sig syntax, after parameter names
[(_ (f:id x:id ...) (~datum :) ty ... (~or (~datum ->) (~datum )) ty_out . b)
--------
[ (define (f [x : ty] ... -> ty_out) . b)]]
[(_ (f:id [x:id (~datum :) τ] ... (~or (~datum ->) (~datum )) τ_out)
e_body ... e)
#:with Ys (compute-tyvars #'(τ ... τ_out))
#:with g (add-orig (generate-temporary #'f) #'f)
#:with e_ann (syntax/loc #'e (ann e : τ_out)) ; must be macro bc t_out may have unbound tvs
#:with (τ+orig ...) (stx-map (λ (t) (add-orig t t)) #'(τ ... τ_out))
;; TODO: check that specified return type is correct
;; - currently cannot do it here; to do the check here, need all types of
;; top-lvl fns, since they can call each other
#:with (~and ty_fn_expected (~?∀ _ (~ext-stlc:→ _ ... out_expected)))
(set-stx-prop/preserved
((current-type-eval) #'(?∀ Ys (ext-stlc:→ τ+orig ...)))
'orig
(list #'( τ+orig ...)))
--------
[ (begin-
(define-syntax f (make-rename-transformer ( g : ty_fn_expected)))
(define- g
( Ys (ext-stlc:λ ([x : τ] ...) (ext-stlc:begin e_body ... e_ann)))))]])
;; define-type -----------------------------------------------
;; TODO: should validate τ as part of define-type definition (before it's used)
;; - not completely possible, since some constructors may not be defined yet,
;; ie, mutually recursive datatypes
;; for now, validate types but punt if encountering unbound ids
(define-syntax (define-type stx)
(syntax-parse stx
[(define-type Name:id . rst)
#:with NewName (generate-temporary #'Name)
#:with Name2 (add-orig #'(NewName) #'Name)
#`(begin-
(define-type Name2 . #,(subst #'Name2 #'Name #'rst))
(stlc+rec-iso:define-type-alias Name Name2))]
[(define-type (Name:id X:id ...)
;; constructors must have the form (Cons τ ...)
;; but the first ~or clause accepts 0-arg constructors as ids;
;; the ~and is a workaround to bind the duplicate Cons ids (see Ryan's email)
(~and (~or (~and IdCons:id
(~parse (Cons [fld (~datum :) τ] ...) #'(IdCons)))
(Cons [fld (~datum :) τ] ...)
(~and (Cons τ ...)
(~parse (fld ...) (generate-temporaries #'(τ ...)))))) ...)
;; validate tys
#:with (ty_flat ...) (stx-flatten #'((τ ...) ...))
#:with (_ _ (_ _ (_ _ (_ _ ty+ ...))))
(with-handlers
([exn:fail:syntax:unbound?
(λ (e)
(define X (stx-car (exn:fail:syntax-exprs e)))
#`(lambda () (let-syntax () (let-syntax () (#%app void unbound)))))])
(expand/df
#`(lambda (X ...)
(let-syntax
([Name
(syntax-parser
[(_ X ...) (mk-type #'void)]
[stx
(type-error
#:src #'stx
#:msg
(format "Improper use of constructor ~a; expected ~a args, got ~a"
(syntax->datum #'Name) (stx-length #'(X ...))
(stx-length (stx-cdr #'stx))))])]
[X (make-rename-transformer (mk-type #'X))] ...)
(void ty_flat ...)))))
#:when (or (equal? '(unbound) (syntax->datum #'(ty+ ...)))
(stx-map
(lambda (t+ t) (unless (type? t+)
(type-error #:src t
#:msg "~a is not a valid type" t)))
#'(ty+ ...) #'(ty_flat ...)))
#:with NameExpander (format-id #'Name "~~~a" #'Name)
#:with NameExtraInfo (format-id #'Name "~a-extra-info" #'Name)
#:with (StructName ...) (generate-temporaries #'(Cons ...))
#:with ((e_arg ...) ...) (stx-map generate-temporaries #'((τ ...) ...))
#:with ((e_arg- ...) ...) (stx-map generate-temporaries #'((τ ...) ...))
#:with ((τ_arg ...) ...) (stx-map generate-temporaries #'((τ ...) ...))
#:with ((exposed-acc ...) ...)
(stx-map
(λ (C fs) (stx-map (λ (f) (format-id C "~a-~a" C f)) fs))
#'(Cons ...) #'((fld ...) ...))
#:with ((acc ...) ...) (stx-map (λ (S fs) (stx-map (λ (f) (format-id S "~a-~a" S f)) fs))
#'(StructName ...) #'((fld ...) ...))
#:with (Cons? ...) (stx-map mk-? #'(StructName ...))
#:with (exposed-Cons? ...) (stx-map mk-? #'(Cons ...))
#`(begin-
(define-syntax (NameExtraInfo stx)
(syntax-parse stx
[(_ X ...) #'(('Cons 'StructName Cons? [acc τ] ...) ...)]))
(begin-for-syntax
;; arg-variance-vars : (List Variance-Var ...)
(define arg-variance-vars
(list (variance-var (syntax-e (generate-temporary 'X))) ...)))
(define-type-constructor Name
#:arity = #,(stx-length #'(X ...))
#:arg-variances (make-arg-variances-proc arg-variance-vars
(list #'X ...)
(list #'τ ... ...))
#:extra-info 'NameExtraInfo)
(struct- StructName (fld ...) #:reflection-name 'Cons #:transparent) ...
(define-syntax (exposed-acc stx) ; accessor for records
(syntax-parse stx
[_:id
( acc (?∀ (X ...) (ext-stlc:→ (Name X ...) τ)))]
[(o . rst) ; handle if used in fn position
#:with app (datum->syntax #'o '#%app)
#`(app
#,(assign-type #'acc #'(?∀ (X ...) (ext-stlc:→ (Name X ...) τ)))
. rst)])) ... ...
(define-syntax (exposed-Cons? stx) ; predicates for each variant
(syntax-parse stx
[_:id ( Cons? (?∀ (X ...) (ext-stlc:→ (Name X ...) Bool)))]
[(o . rst) ; handle if used in fn position
#:with app (datum->syntax #'o '#%app)
#`(app
#,(assign-type #'Cons? #'(?∀ (X ...) (ext-stlc:→ (Name X ...) Bool)))
. rst)])) ...
;; TODO: remove default provides to use define-typed-syntax here
(define-typerule Cons
; no args and not polymorphic
[C:id
#:when (and (stx-null? #'(X ...)) (stx-null? #'(τ ...)))
--------
[ (C)]]
; no args but polymorphic, check expected type
[:id (NameExpander τ-expected-arg (... ...))
#:when (stx-null? #'(τ ...))
--------
[ (StructName)]]
; id with multiple expected args, HO fn
[:id
#:when (not (stx-null? #'(τ ...)))
--------
[ StructName (?∀ (X ...) (ext-stlc:→ τ ... (Name X ...)))]]
[(C τs e_arg ...)
#:when (brace? #'τs) ; commit to this clause
#:with [X* (... ...)] #'[X ...]
#:with [e_arg* (... ...)] #'[e_arg ...]
#:with {~! τ_X:type (... ...)} #'τs
#:with (τ_in:type (... ...)) ; instantiated types
(inst-types/cs #'(X ...) #'([X* τ_X.norm] (... ...)) #'(τ ...))
;; e_arg* helps align ellipses
[ e_arg* e_arg*- τ_in.norm] (... ...)
#:with [e_arg- ...] #'[e_arg*- (... ...)]
--------
[ (StructName e_arg- ...) (Name τ_X.norm (... ...))]]
[(C . args) ; no type annotations, must infer instantiation
#:with StructName/ty
(set-stx-prop/preserved
( StructName : (?∀ (X ...) (ext-stlc:→ τ ... (Name X ...))))
'orig
(list #'C))
--------
[ (mlish:#%app StructName/ty . args)]])
...)]))
;; match --------------------------------------------------
(begin-for-syntax
(define (get-ctx pat ty)
(unify-pat+ty (list pat ty)))
(define (unify-pat+ty pat+ty)
(syntax-parse pat+ty
[(pat ty) #:when (brace? #'pat) ; handle root pattern specially (to avoid some parens)
(syntax-parse #'pat
[{(~datum _)} #'()]
[{(~literal stlc+cons:nil)} #'()]
[{A:id} ; disambiguate 0-arity constructors (that don't need parens)
#:when (get-extra-info #'ty)
#'()]
;; comma tup syntax always has parens
[{(~and ps (p1 (unq p) ...))}
#:when (not (stx-null? #'(p ...)))
#:when (andmap (lambda (u) (equal? u 'unquote)) (syntax->datum #'(unq ...)))
(unify-pat+ty #'(ps ty))]
[{p ...}
(unify-pat+ty #'((p ...) ty))])] ; pair
[((~datum _) ty) #'()]
[((~or (~literal stlc+cons:nil)) ty) #'()]
[(A:id ty) ; disambiguate 0-arity constructors (that don't need parens)
#:with (_ (_ (_ C) . _) ...) (get-extra-info #'ty)
#:when (member (syntax->datum #'A) (syntax->datum #'(C ...)))
#'()]
[(x:id ty) #'((x ty))]
[((p1 (unq p) ...) ty) ; comma tup stx
#:when (not (stx-null? #'(p ...)))
#:when (andmap (lambda (u) (equal? u 'unquote)) (syntax->datum #'(unq ...)))
#:with (~× t ...) #'ty
#:with (pp ...) #'(p1 p ...)
(unifys #'([pp t] ...))]
[(((~literal stlc+tup:tup) p ...) ty) ; tup
#:with (~× t ...) #'ty
(unifys #'([p t] ...))]
[(((~literal stlc+cons:list) p ...) ty) ; known length list
#:with (~List t) #'ty
(unifys #'([p t] ...))]
[(((~seq p (~datum ::)) ... rst) ty) ; nicer cons stx
#:with (~List t) #'ty
(unifys #'([p t] ... [rst ty]))]
[(((~literal stlc+cons:cons) p ps) ty) ; arb length list
#:with (~List t) #'ty
(unifys #'([p t] [ps ty]))]
[((Name p ...) ty)
#:with (_ (_ Cons) _ _ [_ _ τ] ...)
(stx-findf
(syntax-parser
[(_ 'C . rst)
(equal? (syntax->datum #'Name) (syntax->datum #'C))])
(stx-cdr (get-extra-info #'ty)))
(unifys #'([p τ] ...))]
[p+t #:fail-when #t (format "could not unify ~a" (syntax->datum #'p+t))
#'()]))
(define (unifys p+tys) (stx-appendmap unify-pat+ty p+tys))
(define (compile-pat p ty)
(syntax-parse p
[pat #:when (brace? #'pat) ; handle root pattern specially (to avoid some parens)
(syntax-parse #'pat
[{(~datum _)} #'_]
[{(~literal stlc+cons:nil)} (syntax/loc p (list))]
[{A:id} ; disambiguate 0-arity constructors (that don't need parens)
#:when (get-extra-info ty)
(compile-pat #'(A) ty)]
;; comma tup stx always has parens
[{(~and ps (p1 (unq p) ...))}
#:when (not (stx-null? #'(p ...)))
#:when (andmap (lambda (u) (equal? u 'unquote)) (syntax->datum #'(unq ...)))
(compile-pat #'ps ty)]
[{pat ...} (compile-pat (syntax/loc p (pat ...)) ty)])]
[(~datum _) #'_]
[(~literal stlc+cons:nil) ; nil
#'(list)]
[A:id ; disambiguate 0-arity constructors (that don't need parens)
#:with (_ (_ (_ C) . _) ...) (get-extra-info ty)
#:when (member (syntax->datum #'A) (syntax->datum #'(C ...)))
(compile-pat #'(A) ty)]
[x:id p]
[(p1 (unq p) ...) ; comma tup stx
#:when (not (stx-null? #'(p ...)))
#:when (andmap (lambda (u) (equal? u 'unquote)) (syntax->datum #'(unq ...)))
#:with (~× t ...) ty
#:with (p- ...) (stx-map (lambda (p t) (compile-pat p t)) #'(p1 p ...) #'(t ...))
#'(list p- ...)]
[((~literal stlc+tup:tup) . pats)
#:with (~× . tys) ty
#:with (p- ...) (stx-map (lambda (p t) (compile-pat p t)) #'pats #'tys)
(syntax/loc p (list p- ...))]
[((~literal stlc+cons:list) . ps)
#:with (~List t) ty
#:with (p- ...) (stx-map (lambda (p) (compile-pat p #'t)) #'ps)
(syntax/loc p (list p- ...))]
[((~seq pat (~datum ::)) ... last) ; nicer cons stx
#:with (~List t) ty
#:with (p- ...) (stx-map (lambda (pp) (compile-pat pp #'t)) #'(pat ...))
#:with last- (compile-pat #'last ty)
(syntax/loc p (list-rest p- ... last-))]
[((~literal stlc+cons:cons) p ps)
#:with (~List t) ty
#:with p- (compile-pat #'p #'t)
#:with ps- (compile-pat #'ps ty)
#'(cons p- ps-)]
[(Name . pats)
#:with (_ (_ Cons) (_ StructName) _ [_ _ τ] ...)
(stx-findf
(syntax-parser
[(_ 'C . rst)
(equal? (syntax->datum #'Name) (syntax->datum #'C))])
(stx-cdr (get-extra-info ty)))
#:with (p- ...) (stx-map compile-pat #'pats #'(τ ...))
(syntax/loc p (StructName p- ...))]))
;; pats = compiled pats = racket pats
(define (check-exhaust pats ty)
(define (else-pat? p)
(syntax-parse p [(~literal _) #t] [_ #f]))
(define (nil-pat? p)
(syntax-parse p
[((~literal list)) #t]
[_ #f]))
(define (non-nil-pat? p)
(syntax-parse p
[((~literal list-rest) . rst) #t]
[((~literal cons) . rst) #t]
[_ #f]))
(define (tup-pat? p)
(syntax-parse p
[((~literal list) . _) #t] [_ #f]))
(cond
[(or (stx-ormap else-pat? pats) (stx-ormap identifier? pats)) #t]
[(List? ty) ; lists
(unless (stx-ormap nil-pat? pats)
(error 'match2 (let ([last (car (stx-rev pats))])
(format "(~a:~a) missing nil clause for list expression"
(syntax-line last) (syntax-column last)))))
(unless (stx-ormap non-nil-pat? pats)
(error 'match2 (let ([last (car (stx-rev pats))])
(format "(~a:~a) missing clause for non-empty, arbitrary length list"
(syntax-line last) (syntax-column last)))))
#t]
[(×? ty) ; tuples
(unless (stx-ormap tup-pat? pats)
(error 'match2 (let ([last (car (stx-rev pats))])
(format "(~a:~a) missing pattern for tuple expression"
(syntax-line last) (syntax-column last)))))
(syntax-parse pats
[((_ p ...) ...)
(syntax-parse ty
[(~× t ...)
(apply stx-andmap
(lambda (t . ps) (check-exhaust ps t))
#'(t ...)
(syntax->list #'((p ...) ...)))])])]
[else ; algebraic datatypes
(syntax-parse (get-extra-info ty)
[(_ (_ (_ C) (_ Cstruct) . rst) ...)
(syntax-parse pats
[((Cpat _ ...) ...)
(define Cs (syntax->datum #'(C ...)))
(define Cstructs (syntax->datum #'(Cstruct ...)))
(define Cpats (syntax->datum #'(Cpat ...)))
(unless (set=? Cstructs Cpats)
(error 'match2
(let ([last (car (stx-rev pats))])
(format "(~a:~a) clauses not exhaustive; missing: ~a"
(syntax-line last) (syntax-column last)
(string-join
(for/list ([C Cs][Cstr Cstructs] #:unless (member Cstr Cpats))
(symbol->string C))
", ")))))
#t])]
[_ #t])]))
;; TODO: do get-ctx and compile-pat in one pass
(define (compile-pats pats ty)
(stx-map (lambda (p) (list (get-ctx p ty) (compile-pat p ty))) pats))
)
(define-typed-syntax match2 #:datum-literals (with ->)
[(match2 e with . clauses)
#:fail-unless (not (null? (syntax->list #'clauses))) "no clauses"
[ e e- τ_e]
#:with ([(~seq p ...) -> e_body] ...) #'clauses
#:with (pat ...) (stx-map ; use brace to indicate root pattern
(lambda (ps) (syntax-parse ps [(pp ...) (syntax/loc this-syntax {pp ...})]))
#'((p ...) ...))
#:with ([(~and ctx ([x ty] ...)) pat-] ...) (compile-pats #'(pat ...) #'τ_e)
#:with ty-expected (get-expected-type this-syntax)
[[x x- : ty] ... (add-expected e_body ty-expected) e_body- ty_body] ...
#:when (check-exhaust #'(pat- ...) #'τ_e)
--------
[ (match- e- [pat- (let- ([x- x] ...) e_body-)] ...) ( ty_body ...)]])
(define-typed-syntax match #:datum-literals (with -> ::)
;; e is a tuple
[(match e with . clauses)
#:fail-unless (not (null? (syntax->list #'clauses))) "no clauses"
[ e e- τ_e]
#:when (×? #'τ_e)
#:with t_expect (get-expected-type this-syntax) ; propagate inferred type
#:with ([x ... -> e_body]) #'clauses
#:with (~× ty ...) #'τ_e
#:fail-unless (stx-length=? #'(ty ...) #'(x ...))
"match clause pattern not compatible with given tuple"
[[x x- : ty] ... (add-expected e_body t_expect) e_body- ty_body]
#:with (acc ...) (for/list ([(a i) (in-indexed (syntax->list #'(x ...)))])
#`(lambda (s) (list-ref s #,(datum->syntax #'here i))))
#:with z (generate-temporary)
--------
[ (let- ([z e-])
(let- ([x- (acc z)] ...) e_body-)) ty_body]]
;; e is a list
[(match e with . clauses)
#:fail-unless (not (null? (syntax->list #'clauses))) "no clauses"
[ e e- τ_e]
#:when (List? #'τ_e)
#:with t_expect (get-expected-type this-syntax) ; propagate inferred type
#:with ([(~or (~and (~and xs [x ...]) (~parse rst (generate-temporary)))
(~and (~seq (~seq x ::) ... rst:id) (~parse xs #'())))
-> e_body] ...+)
#'clauses
#:fail-unless (stx-ormap
(lambda (xx) (and (brack? xx) (zero? (stx-length xx))))
#'(xs ...))
"match: missing empty list case"
#:fail-unless (not (and (stx-andmap brack? #'(xs ...))
(= 1 (stx-length #'(xs ...)))))
"match: missing non-empty list case"
#:with (~List ty) #'τ_e
[[x x- : ty] ... [rst rst- : (List ty)]
(add-expected e_body t_expect) e_body- ty_body] ...
#:with (len ...) (stx-map (lambda (p) #`#,(stx-length p)) #'((x ...) ...))
#:with (lenop ...) (stx-map (lambda (p) (if (brack? p) #'=- #'>=-)) #'(xs ...))
#:with (pred? ...) (stx-map
(lambda (l lo) #`(λ- (lst) (#,lo (length lst) #,l)))
#'(len ...) #'(lenop ...))
#:with ((acc1 ...) ...) (stx-map
(lambda (xs)
(for/list ([(x i) (in-indexed (syntax->list xs))])
#`(lambda- (lst) (list-ref- lst #,(datum->syntax #'here i)))))
#'((x ...) ...))
#:with (acc2 ...) (stx-map (lambda (l) #`(lambda- (lst) (list-tail- lst #,l))) #'(len ...))
--------
[ (let- ([z e-])
(cond-
[(pred? z)
(let- ([x- (acc1 z)] ... [rst- (acc2 z)]) e_body-)] ...))
( ty_body ...)]]
;; e is a variant
[(match e with . clauses)
#:fail-unless (not (null? (syntax->list #'clauses))) "no clauses"
[ e e- τ_e]
#:when (and (not (×? #'τ_e)) (not (List? #'τ_e)))
#:with t_expect (get-expected-type this-syntax) ; propagate inferred type
#:with ([Clause:id x:id ...
(~optional (~seq #:when e_guard) #:defaults ([e_guard #'(ext-stlc:#%datum . #t)]))
-> e_c_un] ...+) ; un = unannotated with expected ty
#'clauses
;; length #'clauses may be > length #'info, due to guards
#:with info-body (get-extra-info #'τ_e)
#:with (_ (_ (_ ConsAll) . _) ...) #'info-body
#:fail-unless (set=? (syntax->datum #'(Clause ...))
(syntax->datum #'(ConsAll ...)))
(type-error #:src this-syntax
#:msg (string-append
"match: clauses not exhaustive; missing: "
(string-join
(map symbol->string
(set-subtract
(syntax->datum #'(ConsAll ...))
(syntax->datum #'(Clause ...))))
", ")))
#:with ((_ _ _ Cons? [_ acc τ] ...) ...)
(map ; ok to compare symbols since clause names can't be rebound
(lambda (Cl)
(stx-findf
(syntax-parser
[(_ 'C . rst) (equal? Cl (syntax->datum #'C))])
(stx-cdr #'info-body))) ; drop leading #%app
(syntax->datum #'(Clause ...)))
;; this commented block experiments with expanding to unsafe ops
;; #:with ((acc ...) ...) (stx-map
;; (lambda (accs)
;; (for/list ([(a i) (in-indexed (syntax->list accs))])
;; #`(lambda (s) (unsafe-struct*-ref s #,(datum->syntax #'here i)))))
;; #'((acc-fn ...) ...))
#:with (e_c ...+) (stx-map (lambda (ec) (add-expected-ty ec #'t_expect)) #'(e_c_un ...))
[[x x- : τ] ... [e_guard e_guard- Bool] [e_c e_c- τ_ec]] ...
#:with z (generate-temporary) ; dont duplicate eval of test expr
--------
[ (let- ([z e-])
(cond-
[(and- (Cons? z)
(let- ([x- (acc z)] ...) e_guard-))
(let- ([x- (acc z)] ...) e_c-)] ...))
( τ_ec ...)]])
; special arrow that computes free vars; for use with tests
; (because we can't write explicit forall
(define-syntax →/test
(syntax-parser
[(→/test (~and Xs (X:id ...)) . rst)
#:when (brace? #'Xs)
#'(?∀ (X ...) (ext-stlc:→ . rst))]
[(→/test . rst)
#:with Xs (compute-tyvars #'rst)
#'(?∀ Xs (ext-stlc:→ . rst))]))
; redefine these to use lifted →
(provide (typed-out [+ : ( Int Int Int)]
[- : ( Int Int Int)]
[* : ( Int Int Int)]
[max : ( Int Int Int)]
[min : ( Int Int Int)]
[void : ( Unit)]
[= : ( Int Int Bool)]
[<= : ( Int Int Bool)]
[< : ( Int Int Bool)]
[> : ( Int Int Bool)]
[modulo : ( Int Int Int)]
[zero? : ( Int Bool)]
[sub1 : ( Int Int)]
[add1 : ( Int Int)]
[not : ( Bool Bool)]
[abs : ( Int Int)]
[even? : ( Int Bool)]
[odd? : ( Int Bool)]))
; all λs have type (?∀ (X ...) (→ τ_in ... τ_out))
(define-typed-syntax λ #:datum-literals (:)
[(λ (x:id ...) body) (~?∀ (X ...) (~ext-stlc:→ τ_in ... τ_out))
#:fail-unless (stx-length=? #'[x ...] #'[τ_in ...])
(format "expected a function of ~a arguments, got one with ~a arguments"
(stx-length #'[τ_in ...]) (stx-length #'[x ...]))
[(X ...) ([x x- : τ_in] ...) [body body- τ_out]]
--------
[ (λ- (x- ...) body-)]]
[(λ ([x : τ_x] ...) body) (~?∀ (V ...) (~ext-stlc:→ τ_in ... τ_out))
#:with [X ...] (compute-tyvars #'(τ_x ...))
[[X X- :: #%type] ... [τ_x τ_x- :: #%type] ...]
[τ_in τ⊑ τ_x- #:for x] ...
;; TODO is there a way to have λs that refer to ids defined after them?
[(V ... X- ...) ([x x- : τ_x-] ...) body body- τ_out]
--------
[ (λ- (x- ...) body-)]]
[(λ ([x : τ_x] ...) body)
#:with [X ...] (compute-tyvars #'(τ_x ...))
;; TODO is there a way to have λs that refer to ids defined after them?
[([X X- :: #%type] ...) ([x x- : τ_x] ...) body body- τ_body]
#:with [τ_x* ...] (inst-types/cs #'[X ...] #'([X X-] ...) #'[τ_x ...])
#:with τ_fn (add-orig #'(?∀ (X- ...) (ext-stlc:→ τ_x* ... τ_body))
#`( #,@(stx-map get-orig #'[τ_x* ...]) #,(get-orig #'τ_body)))
--------
[ (λ- (x- ...) body-) τ_fn]])
;; #%app --------------------------------------------------
(define-typed-syntax mlish:#%app #:export-as #%app
[(_ e_fn e_arg ...)
;; compute fn type (ie ∀ and →)
[ e_fn e_fn- (~?∀ Xs (~ext-stlc:→ . tyX_args))]
;; solve for type variables Xs
#:with [[e_arg- ...] Xs* cs] (solve #'Xs #'tyX_args this-syntax)
;; instantiate polymorphic function type
#:with [τ_in ... τ_out] (inst-types/cs #'Xs* #'cs #'tyX_args)
#:with (unsolved-X ...) (find-free-Xs #'Xs* #'τ_out)
;; arity check
#:fail-unless (stx-length=? #'[τ_in ...] #'[e_arg ...])
(num-args-fail-msg #'e_fn #'[τ_in ...] #'[e_arg ...])
;; compute argument types
#:with (τ_arg ...) (stx-map typeof #'(e_arg- ...))
;; typecheck args
[τ_arg τ⊑ τ_in #:for e_arg] ...
#:with τ_out* (if (stx-null? #'(unsolved-X ...))
#'τ_out
(syntax-parse #'τ_out
[(~?∀ (Y ...) τ_out)
#:fail-unless (→? #'τ_out)
(mk-app-poly-infer-error this-syntax #'(τ_in ...) #'(τ_arg ...) #'e_fn)
(for ([X (in-list (syntax->list #'(unsolved-X ...)))])
(unless (covariant-X? X #'τ_out)
(raise-syntax-error
#f
(mk-app-poly-infer-error this-syntax #'(τ_in ...) #'(τ_arg ...) #'e_fn)
this-syntax)))
#'( (unsolved-X ... Y ...) τ_out)]))
--------
[ (#%app- e_fn- e_arg- ...) τ_out*]])
;; cond and other conditionals
(define-typed-syntax cond
[(_ [(~or (~and (~datum else) (~parse test #'(ext-stlc:#%datum . #t)))
test)
b ... body] ...+) τ_expected
[ test test- Bool] ...
[ (begin b ... body) body- τ_expected] ...
--------
[ (cond- [test- body-] ...)]]
[(_ [(~or (~and (~datum else) (~parse test #'(ext-stlc:#%datum . #t)))
test)
b ... body] ...+)
[ test test- Bool] ...
[ (begin b ... body) body- τ_body] ...
--------
[ (cond- [test- body-] ...) ( τ_body ...)]])
(define-typed-syntax (when test body ...)
[ test test- _]
[ body body- _] ...
--------
[ (when- test- body- ... (void-)) Unit])
(define-typed-syntax (unless test body ...)
[ test test- _]
[ body body- _] ...
--------
[ (unless- test- body- ... (void-)) Unit])
;; sync channels and threads
(define-type-constructor Channel)
(define-typed-syntax make-channel
[(make-channel (~and tys {ty}))
#:when (brace? #'tys)
--------
[ [_ (make-channel-) : (Channel ty)]]])
(define-typed-syntax channel-get
[(channel-get c) : ty
[ [c c- : (Channel ty)]]
--------
[ [_ (channel-get- c-) : _]]]
[(channel-get c)
[ [c c- : (~Channel ty)]]
--------
[ [_ (channel-get- c-) : ty]]])
(define-typed-syntax channel-put
[(channel-put c v)
[ [c c- : (~Channel ty)]]
[ [v v- : ty]]
--------
[ [_ (channel-put- c- v-) : Unit]]])
(define-base-type Thread)
;; threads
(define-typed-syntax thread
[(thread th)
[ [th th- : (~?∀ () (~ext-stlc:→ τ_out))]]
--------
[ [_ (thread- th-) : Thread]]])
(provide (typed-out [random : ( Int Int)]
[integer->char : ( Int Char)]
[string->list : ( String (List Char))]
[string->number : ( String Int)]))
(define-typed-syntax number->string
[number->string:id
--------
[ [_ number->string- : ( Int String)]]]
[(number->string n)
--------
[_ (number->string n (ext-stlc:#%datum . 10))]]
[(number->string n rad)
[ [n n- : Int]]
[ [rad rad- : Int]]
--------
[ [_ (number->string- n rad) : String]]])
(provide (typed-out [string : ( Char String)]
[sleep : ( Int Unit)]
[string=? : ( String String Bool)]
[string<=? : ( String String Bool)]))
(define-typed-syntax string-append
[(string-append str ...)
[ [str str- : String] ...]
--------
[ [_ (string-append- str- ...) : String]]])
;; vectors
(define-type-constructor Vector)
(define-typed-syntax vector
[(vector (~and tys {ty}))
#:when (brace? #'tys)
--------
[ [_ (vector-) : (Vector ty)]]]
[(vector v ...) : (Vector ty)
[ [v v- : ty] ...]
--------
[ [_ (vector- v- ...) : _]]]
[(vector v ...)
[ [v v- : ty] ...]
#:when (same-types? #'(ty ...))
#:with one-ty (stx-car #'(ty ...))
--------
[ [_ (vector- v- ...) : (Vector one-ty)]]])
(define-typed-syntax make-vector
[(make-vector n)
--------
[_ (make-vector n (ext-stlc:#%datum . 0))]]
[(make-vector n e)
[ [n n- : Int]]
[ [e e- : ty]]
--------
[ [_ (make-vector- n- e-) : (Vector ty)]]])
(define-typed-syntax vector-length
[(vector-length e)
[ [e e- : (~Vector _)]]
--------
[ [_ (vector-length- e-) : Int]]])
(define-typed-syntax vector-ref
[(vector-ref e n) : ty
[ [e e- : (Vector ty)]]
[ [n n- : Int]]
--------
[ [_ (vector-ref- e- n-) : _]]]
[(vector-ref e n)
[ [e e- : (~Vector ty)]]
[ [n n- : Int]]
--------
[ [_ (vector-ref- e- n-) : ty]]])
(define-typed-syntax vector-set!
[(vector-set! e n v)
[ [e e- : (~Vector ty)]]
[ [n n- : Int]]
[ [v v- : ty]]
--------
[ [_ (vector-set!- e- n- v-) : Unit]]])
(define-typed-syntax vector-copy!
[(vector-copy! dest start src)
[ [dest dest- : (~Vector ty)]]
[ [start start- : Int]]
[ [src src- : (Vector ty)]]
--------
[ [_ (vector-copy!- dest- start- src-) : Unit]]])
;; sequences and for loops
(define-type-constructor Sequence)
(define-typed-syntax in-range
[(in-range end)
--------
[_ (in-range (ext-stlc:#%datum . 0) end (ext-stlc:#%datum . 1))]]
[(in-range start end)
--------
[_ (in-range start end (ext-stlc:#%datum . 1))]]
[(in-range start end step)
[ [start start- : Int]]
[ [end end- : Int]]
[ [step step- : Int]]
--------
[ [_ (in-range- start- end- step-) : (Sequence Int)]]])
(define-typed-syntax in-naturals
[(in-naturals)
--------
[_ (in-naturals (ext-stlc:#%datum . 0))]]
[(in-naturals start)
[ [start start- : Int]]
--------
[ [_ (in-naturals- start-) : (Sequence Int)]]])
(define-typed-syntax in-vector
[(in-vector e)
[ [e e- : (~Vector ty)]]
--------
[ [_ (in-vector- e-) : (Sequence ty)]]])
(define-typed-syntax in-list
[(in-list e)
[ [e e- : (~List ty)]]
--------
[ [_ (in-list- e-) : (Sequence ty)]]])
(define-typed-syntax in-lines
[(in-lines e)
[ [e e- : String]]
--------
[ [_ (in-lines- (open-input-string- e-)) : (Sequence String)]]])
(define-typed-syntax for
[(for ([x:id e]...) b ... body)
[ [e e- : (~Sequence ty)] ...]
[() ([x x- : ty] ...)
[b b- : _] ... [body body- : _]]
--------
[ [_ (for- ([x- e-] ...) b- ... body-) : Unit]]])
(define-typed-syntax for*
[(for* ([x:id e]...) b ... body)
[ [e e- : (~Sequence ty)] ...]
[() ([x x- : ty] ...)
[b b- : _] ... [body body- : _]]
--------
[ [_ (for*- ([x- e-] ...) b- ... body-) : Unit]]])
(define-typed-syntax for/list
[(for/list ([x:id e]...) body)
[ [e e- : (~Sequence ty)] ...]
[() ([x x- : ty] ...) [body body- : ty_body]]
--------
[ [_ (for/list- ([x- e-] ...) body-) : (List ty_body)]]])
(define-typed-syntax for/vector
[(for/vector ([x:id e]...) body)
[ [e e- : (~Sequence ty)] ...]
[() ([x x- : ty] ...) [body body- : ty_body]]
--------
[ [_ (for/vector- ([x- e-] ...) body-) : (Vector ty_body)]]])
(define-typed-syntax for*/vector
[(for*/vector ([x:id e]...) body)
[ [e e- : (~Sequence ty)] ...]
[() ([x x- : ty] ...) [body body- : ty_body]]
--------
[ [_ (for*/vector- ([x- e-] ...) body-) : (Vector ty_body)]]])
(define-typed-syntax for*/list
[(for*/list ([x:id e]...) body)
[ [e e- : (~Sequence ty)] ...]
[() ([x x- : ty] ...) [body body- : ty_body]]
--------
[ [_ (for*/list- ([x- e-] ...) body-) : (List ty_body)]]])
(define-typed-syntax for/fold
[(for/fold ([acc init]) ([x:id e] ...) body) : τ_expected
[ [init init- : τ_expected]]
[ [e e- : (~Sequence ty)] ...]
[() ([acc acc- : τ_expected] [x x- : ty] ...)
[body body- : τ_expected]]
--------
[ [_ (for/fold- ([acc- init-]) ([x- e-] ...) body-) : _]]]
[(for/fold ([acc init]) ([x:id e] ...) body)
[ [init init- : τ_init]]
[ [e e- : (~Sequence ty)] ...]
[() ([acc acc- : τ_init] [x x- : ty] ...)
[body body- : τ_init]]
--------
[ [_ (for/fold- ([acc- init-]) ([x- e-] ...) body-) : τ_init]]])
(define-typed-syntax for/hash
[(for/hash ([x:id e]...) body)
[ [e e- : (~Sequence ty)] ...]
[() ([x x- : ty] ...) [body body- : (~× ty_k ty_v)]]
--------
[ [_ (for/hash- ([x- e-] ...)
(let- ([t body-])
(values- (car- t) (cadr- t))))
: (Hash ty_k ty_v)]]])
(define-typed-syntax for/sum
[(for/sum ([x:id e]...
(~optional (~seq #:when guard) #:defaults ([guard #'#t])))
body)
[ [e e- : (~Sequence ty)] ...]
[() ([x x- : ty] ...)
[guard guard- : _] [body body- : Int]]
--------
[ [_ (for/sum- ([x- e-] ... #:when guard-) body-) : Int]]])
; printing and displaying
(define-typed-syntax printf
[(printf str e ...)
[ [str s- : String]]
[ [e e- : ty] ...]
--------
[ [_ (printf- s- e- ...) : Unit]]])
(define-typed-syntax format
[(format str e ...)
[ [str s- : String]]
[ [e e- : ty] ...]
--------
[ [_ (format- s- e- ...) : String]]])
(define-typed-syntax display
[(display e)
[ [e e- : _]]
--------
[ [_ (display- e-) : Unit]]])
(define-typed-syntax displayln
[(displayln e)
[ [e e- : _]]
--------
[ [_ (displayln- e-) : Unit]]])
(provide (typed-out [newline : ( Unit)]))
(define-typed-syntax list->vector
[(list->vector e) : (~Vector ty)
[ [e e- : (List ty)]]
--------
[ [_ (list->vector- e-) : _]]]
[(list->vector e)
[ [e e- : (~List ty)]]
--------
[ [_ (list->vector- e-) : (Vector ty)]]])
(define-typed-syntax let
[(let name:id (~datum :) ty:type ~! ([x:id e] ...) b ... body)
[ [e e- : ty_e] ...]
[() ([name name- : ( ty_e ... ty.norm)] [x x- : ty_e] ...)
[b b- : _] ... [body body- : ty.norm]]
--------
[ [_ (letrec- ([name- (λ- (x- ...) b- ... body-)])
(name- e- ...))
: ty.norm]]]
[(let ([x:id e] ...) body ...)
--------
[_ (ext-stlc:let ([x e] ...) (begin body ...))]])
(define-typed-syntax let*
[(let* ([x:id e] ...) body ...)
--------
[_ (ext-stlc:let* ([x e] ...) (begin body ...))]])
(define-typed-syntax begin
[(begin body ... b) : τ_expected
[ [body body- : _] ...]
[ [b b- : τ_expected]]
--------
[ [_ (begin- body- ... b-) : _]]]
[(begin body ... b)
[ [body body- : _] ...]
[ [b b- : τ]]
--------
[ [_ (begin- body- ... b-) : τ]]])
;; hash
(define-type-constructor Hash #:arity = 2)
(define-typed-syntax in-hash
[(_ e)
[ [e e- : (~Hash ty_k ty_v)]]
--------
[ [_ (hash-map- e- list-) : (Sequence (stlc+rec-iso:× ty_k ty_v))]]])
; mutable hashes
(define-typed-syntax hash
[(hash (~and tys {ty_key ty_val}))
#:when (brace? #'tys)
--------
[ [_ (make-hash-) : (Hash ty_key ty_val)]]]
[(hash (~seq k v) ...)
[ [k k- : ty_k] ...]
[ [v v- : ty_v] ...]
#:when (same-types? #'(ty_k ...))
#:when (same-types? #'(ty_v ...))
#:with ty_key (stx-car #'(ty_k ...))
#:with ty_val (stx-car #'(ty_v ...))
--------
[ [_ (make-hash- (list- (cons- k- v-) ...)) : (Hash ty_key ty_val)]]])
(define-typed-syntax hash-set!
[(hash-set! h k v)
[ [h h- : (~Hash ty_k ty_v)]]
[ [k k- : ty_k]]
[ [v v- : ty_v]]
--------
[ [_ (hash-set!- h- k- v-) : Unit]]])
(define-typed-syntax hash-ref
[(hash-ref h k)
[ [h h- : (~Hash ty_k ty_v)]]
[ [k k- : ty_k]]
--------
[ [_ (hash-ref- h- k-) : ty_v]]]
[(hash-ref h k fail)
[ [h h- : (~Hash ty_k ty_v)]]
[ [k k- : ty_k]]
[ [fail fail- : ( ty_v)]]
--------
[ [_ (hash-ref- h- k- fail-) : ty_val]]])
(define-typed-syntax hash-has-key?
[(hash-has-key? h k)
[ [h h- : (~Hash ty_k _)]]
[ [k k- : ty_k]]
--------
[ [_ (hash-has-key?- h- k-) : Bool]]])
(define-typed-syntax hash-count
[(hash-count h)
[ [h h- : (~Hash _ _)]]
--------
[ [_ (hash-count- h-) : Int]]])
(define-base-type String-Port)
(define-base-type Input-Port)
(provide (typed-out [open-output-string : ( String-Port)]
[get-output-string : ( String-Port String)]
[string-upcase : ( String String)]))
(define-typed-syntax write-string
[(write-string str out)
--------
[_ (write-string str out (ext-stlc:#%datum . 0) (string-length str))]]
[(write-string str out start end)
[ [str str- : String]]
[ [out out- : String-Port]]
[ [start start- : Int]]
[ [end end- : Int]]
--------
[ [_ (begin- (write-string- str- out- start- end-) (void-)) : Unit]]])
(define-typed-syntax string-length
[(string-length str)
[ [str str- : String]]
--------
[ [_ (string-length- str-) : Int]]])
(provide (typed-out [make-string : ( Int String)]
[string-set! : ( String Int Char Unit)]
[string-ref : ( String Int Char)]))
(define-typed-syntax string-copy!
[(string-copy! dest dest-start src)
--------
[_ (string-copy!
dest dest-start src (ext-stlc:#%datum . 0) (string-length src))]]
[(string-copy! dest dest-start src src-start src-end)
[ [dest dest- : String]]
[ [src src- : String]]
[ [dest-start dest-start- : Int]]
[ [src-start src-start- : Int]]
[ [src-end src-end- : Int]]
--------
[ [_ (string-copy!- dest- dest-start- src- src-start- src-end-) : Unit]]])
(provide (typed-out [fl+ : ( Float Float Float)]
[fl- : ( Float Float Float)]
[fl* : ( Float Float Float)]
[fl/ : ( Float Float Float)]
[flsqrt : ( Float Float)]
[flceiling : ( Float Float)]
[inexact->exact : ( Float Int)]
[exact->inexact : ( Int Float)]
[char->integer : ( Char Int)]
[real->decimal-string : ( Float Int String)]
[fx->fl : ( Int Float)]))
(define-typed-syntax quotient+remainder
[(quotient+remainder x y)
[ [x x- : Int]]
[ [y y- : Int]]
--------
[ [_ (let-values- ([[a b] (quotient/remainder- x- y-)])
(list- a b))
: (stlc+rec-iso:× Int Int)]]])
(provide (typed-out [quotient : ( Int Int Int)]))
(define-typed-syntax set!
[(set! x:id e)
[ [x x- : ty_x]]
[ [e e- : ty_x]]
--------
[ [_ (set!- x e-) : Unit]]])
(define-typed-syntax provide-type
[(provide-type ty ...)
--------
[_ (provide- ty ...)]])
(define-typed-syntax mlish-provide #:export-as provide
[(provide x:id ...)
[ [x x- : ty_x] ...]
; TODO: use hash-code to generate this tmp
#:with (x-ty ...) (stx-map (lambda (y) (format-id y "~a-ty" y)) #'(x ...))
--------
[_ (begin-
(provide- x ...)
(stlc+rec-iso:define-type-alias x-ty ty_x) ...
(provide- x-ty ...))]])
(define-typed-syntax require-typed
[(require-typed x:id ... #:from mod)
#:with (x-ty ...) (stx-map (lambda (y) (format-id y "~a-ty" y)) #'(x ...))
#:with (y ...) (generate-temporaries #'(x ...))
--------
[_ (begin-
(require- (rename-in (only-in mod x ... x-ty ...) [x y] ...))
(define-syntax x (make-rename-transformer (assign-type #'y #'x-ty))) ...)]])
(define-base-type Regexp)
(provide (typed-out [regexp-match : ( Regexp String (List String))]
[regexp : ( String Regexp)]))
(define-typed-syntax equal?
[(equal? e1 e2)
[ [e1 e1- : ty1]]
[ [e2 e2- : ty1]]
--------
[ [_ (equal?- e1- e2-) : Bool]]])
(define-typed-syntax read-int
[(read-int)
--------
[ [_ (let- ([x (read-)])
(cond- [(exact-integer?- x) x]
[else (error- 'read-int "expected an int, given: ~v" x)]))
: Int]]])
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(module+ test
(begin-for-syntax
(check-true (covariant-Xs? #'Int))
(check-true (covariant-Xs? #'(stlc+box:Ref Int)))
(check-true (covariant-Xs? #'( Int Int)))
(check-true (covariant-Xs? #'( (X) X)))
(check-false (covariant-Xs? #'( (X) (stlc+box:Ref X))))
(check-false (covariant-Xs? #'( (X) ( X X))))
(check-false (covariant-Xs? #'( (X) ( X Int))))
(check-true (covariant-Xs? #'( (X) ( Int X))))
(check-true (covariant-Xs? #'( (X) ( ( X Int) X))))
(check-false (covariant-Xs? #'( (X) ( ( ( X Int) Int) X))))
(check-false (covariant-Xs? #'( (X) ( (stlc+box:Ref X) Int))))
(check-false (covariant-Xs? #'( (X Y) ( X Y))))
(check-true (covariant-Xs? #'( (X Y) ( ( X Int) Y))))
(check-false (covariant-Xs? #'( (X Y) ( ( X Int) ( Y Int)))))
(check-true (covariant-Xs? #'( (X Y) ( ( X Int) ( Int Y)))))
(check-false (covariant-Xs? #'( (A B) ( ( Int (stlc+rec-iso:× A B))
( String (stlc+rec-iso:× A B))
(stlc+rec-iso:× A B)))))
(check-true (covariant-Xs? #'( (A B) ( ( (stlc+rec-iso:× A B) Int)
( (stlc+rec-iso:× A B) String)
(stlc+rec-iso:× A B)))))
))