UNB/ CS/ David Bremner/ teaching/ cs4613/ lectures/ lecture08/ tc1.rkt
#lang plait
(define-type BinOp
  [plus]
  [++]) ;; string concat

(define-type Exp
  [binE (operator : BinOp)
        (left : Exp)
        (right : Exp)]
  [numE (value : Number)]
  [strE (value : String)])

(define-type Type
  [numT]
  [strT])

(define (expect2 type lt rt)
  (cond
    [(and (equal? type lt) (equal? type rt)) type]
    [else (type-case Type type
            [(numT) (error 'tc "needs 2 numbers")]
            [(strT) (error 'tc "needs 2 strings")])]))

(define (tc e)
  (type-case Exp e
    [(binE o l r)
     (type-case BinOp o
       [(plus) (expect2 (numT) (tc l) (tc r))]
       [(++) (expect2 (strT) (tc l) (tc r))])]
    [(numE v) (numT)]
    [(strE v) (strT)]))

(test (tc (binE (plus) (numE 5) (numE 6))) (numT))
(test (tc (binE (++) (strE "hello") (strE "world"))) (strT))
(test/exn (tc (binE (++) (numE 5) (numE 6))) "strings")
(test/exn (tc (binE (plus) (strE "hello")
                    (strE "world"))) "numbers")