#lang plait
(define-type Exp
  [strE (v : String)]
  [numE (v : Number)]
  [prodE (l : Exp) (r : Exp)])

(define-type Value
  [numV (n : Number)]
  [strV (s : String)])

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

(define (times n str) (if (zero? n) "" (string-append str (times (sub1 n) str))))

(define (interp slang)
  (type-case Exp slang
    [(strE s) (strV s)]
    [(numE n) (numV n)]
    [(prodE l r)
     (let ([l-v (interp l)]
           [r-v (interp r)])
       (cond
         [(and (numV? l-v) (numV? r-v))
          (numV (* (numV-n l-v) (numV-n r-v)))]
         [(numV? l-v)
          (strV (times (numV-n l-v) (strV-s r-v)))]
         [(numV? r-v)
          (strV (times (numV-n r-v) (strV-s l-v)))]         
         [else (error 'interp "type mismatch")]))]))

(test (interp (prodE (numE 6) (numE 7))) (numV 42))
(test (interp (prodE (numE 3) (strE "ha"))) (strV "hahaha"))
(test (interp (prodE (numE 2) (prodE (numE 2) (strE "ha")))) (strV "hahahaha"))
(test (interp (prodE (strE "hello") (numE 3))) (strV "hellohellohello"))
(test/exn (interp (prodE (strE "hello") (strE "world"))) "mismatch")

(define (typecheck exp)
  (type-case Exp exp
    [(strE v) (strT)]
    [(numE v) (numT)]
    [(prodE l r)
     (let
         ([lt (typecheck l)]
          [rt (typecheck r)])
       (cond
         [(and (numT? lt) (numT? rt)) lt]
         [(numT? lt) rt]
         [(numT? rt) lt]
         [else (error 'typecheck "type mismatch")]))]))

(test (typecheck (numE 6)) (numT))
(test (typecheck (strE "hello")) (strT))
(test (typecheck (prodE (numE 6) (numE 7))) (numT))
(test (typecheck (prodE (numE 3) (prodE (numE 6) (numE 7)))) (numT))
(test (typecheck (prodE (numE 3) (strE "ha"))) (strT))
(test (typecheck (prodE (strE "hello") (numE 3))) (strT))
(test (typecheck (prodE (prodE (numE 3) (numE 6)) (numE 7))) (numT))
(test/exn (typecheck (prodE (strE "hello") (strE "world"))) "mismatch")
(test/exn (typecheck (prodE (prodE (numE 2) (strE "hello")) (strE "world"))) "mismatch")
(test/exn (typecheck (prodE (prodE (numE 2) (strE "hello")) (prodE (strE "world") (numE 3)))) "mismatch")
(test/exn (typecheck (prodE (strE "3") (prodE (numE 6) (strE "7")))) "mismatch")
(test/exn (typecheck (prodE (strE "3") (prodE (strE "6") (strE "7")))) "mismatch")