#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 Value
[strV (value : String)]
[numV (value : Number)])
(define (on-strings func l r)
(cond
[(and (strV? l) (strV? r))
(strV (func (strV-value l) (strV-value r)))]
[else (error 'interp "expected 2 numbers")]))
(define (on-nums func l r)
(cond
[(and (numV? l) (numV? r))
(numV (func (numV-value l) (numV-value r)))]
[else (error 'interp "expected 2 numbers")]))
(define (interp expr)
(type-case Exp expr
[(numE n) (numV n)]
[(strE s) (strV s)]
[(binE o l r)
(let ([l-val (interp l)]
[r-val (interp r)])
(type-case BinOp o
[(++) (on-strings string-append l-val r-val)]
[(plus) (on-nums + l-val r-val)]))]))
(test (interp (binE (plus) (numE 3) (numE 4)))
(numV 7))
(test (interp (binE (++) (strE "3") (strE "4")))
(strV "34"))
(test/exn (interp (binE (plus) (numE 3) (strE "4")))
"numbers")