(module tc-lambda-unit (lib "")
  (require ""
           "" ;; doesn't need tests
           "" ;; maybe needs tests
           "" ;; doesn't need tests
           "" ;; maybe needs tests
           "" ;; has tests
           (lib ""))
  (require-for-template (lib "") "")
  (require (planet "" ("cobbe" "environment.plt" 3 0)))  
  (require-for-template mzscheme)
  (import typechecker^)
  (export tc-lambda^)
  (define (remove-var id thns elss)
    (let/ec exit
      (define (fail) (exit #f))
      (define (rv e)
        (match e
          [($ var-true-effect v) (if (module-identifier=? v id) (make-latent-var-true-effect) (fail))]
          [($ var-false-effect v) (if (module-identifier=? v id) (make-latent-var-false-effect) (fail))]
          [(or ($ true-effect) ($ false-effect)) e]
          [($ restrict-effect t v) (if (module-identifier=? v id) (make-latent-restrict-effect t) (fail))]
          [($ remove-effect t v) (if (module-identifier=? v id) (make-latent-remove-effect t) (fail))]))
      (cons (map rv thns) (map rv elss))))
  ;; typecheck a single lambda, with argument list and body
  ;; fixme: abstract the two cases!
  ;; syntax-list expr-list -> type
  (define (tc/lambda-clause args body)
    (syntax-case args ()
      [(args ...)
       (let* ([arg-list (syntax->list #'(args ...))]
              [arg-types (map get-type arg-list)])
          arg-list arg-types
          (match (tc-exprs (syntax->list body))   
            [($ tc-result t thn els)
               ;; this is T-AbsPred               
               ;; if this function takes only one argument, and all the effects are about that one argument
               [(and (= 1 (length arg-list)) (remove-var (car arg-list) thn els))
                => (lambda (thn/els) (make-arr arg-types t #f (car thn/els) (cdr thn/els)))]
               ;; otherwise, the simple case
               [else (make-arr arg-types t)])]
            [_ (int-err "bad match")])))]
      [(args ... . rest)
       (let* ([arg-list (syntax->list #'(args ...))]
              [arg-types (map get-type arg-list)]
              [rest-type (get-type #'rest)])
          (cons #'rest arg-list) 
          (cons (make-Listof rest-type) arg-types)
          (match-let ([($ tc-result t thn els) (tc-exprs (syntax->list body))])
            (make-arr arg-types t rest-type))))]))

  ;(trace tc-args)
  ;; tc/mono-lambda : syntax-list syntax-list -> Funty
  ;; typecheck a sequence of case-lambda clauses
  (define (tc/mono-lambda formals bodies)
    (make-funty (map tc/lambda-clause (syntax->list formals) (syntax->list bodies))))
  ;; typecheck a sequence of case-lambda clauses, which is possibly polymorphic
  ;; tc/lambda syntax syntax-list syntax-list -> Type
  (define (tc/lambda form formals bodies)
    (if (syntax-property form 'typechecker:plambda)
        (tc/plambda form formals bodies)
        (ret (tc/mono-lambda formals bodies))))
  ;; tc/plambda syntax syntax-list syntax-list -> Poly
  ;; formals and bodies must by syntax-lists
  (define (tc/plambda form formals bodies)
    (with-syntax ([tvars (syntax-property form 'typechecker:plambda)])
      (let* ([literal-tvars (map syntax-e (syntax->list #'tvars))]
             [new-tvars (map make-tvar literal-tvars)]
             [ty (parameterize ([current-tvars (extend-env literal-tvars new-tvars (current-tvars))])
                   (tc/mono-lambda formals bodies))])
        (ret (make-poly literal-tvars ty)))))