#lang racket

(define valof-lexical
  (lambda (exp env)
    (match exp
      [`,y #:when (symbol? y) (apply-env env y)]
      [`,n #:when (number? n) n]
      [`(* ,a ,b) (* (valof-lexical a env) (valof-lexical b env))]
      [`(lambda (,x) ,b) `(lambda (,x) ,b ,env)]
      [`(let ((,x ,val)) ,b)
       (valof-lexical b (ext-env x (valof-lexical val env) env))]
      [`(,rator ,rand)
       (match-let ((`(lambda (,x) ,b ,env) (valof-lexical rator env)) ;; line of essence
                   (a (valof-lexical rand env)))
         (valof-lexical b (ext-env x a env)))])))

(define valof-dynamic
  (lambda (exp env)
    (match exp
      [`,y #:when (symbol? y) (apply-env env y)]
      [`,n #:when (number? n) n]
      [`(* ,a ,b) (* (valof-dynamic a env) (valof-dynamic b env))]
      [`(lambda (,x) ,b) `(lambda (,x) ,b ,env)]
      [`(let ((,x ,val)) ,b)
       (valof-dynamic b (ext-env x (valof-dynamic val env) env))]
      [`(,rator ,rand)
       (match-let ((`(lambda (,x) ,b ,env^) (valof-dynamic rator env)) ;; line of essence
                   (a (valof-dynamic rand env)))
         (valof-dynamic b (ext-env x a env)))])))

(define apply-env
  (lambda (env y)
    (env y)))

(define ext-env
  (lambda (x a env)
    (lambda (e)
      (if (eqv? e x)
          a
          (env e)))))

(define empty-env
  (lambda ()
    (lambda (x)
      (error "unbound ~s" x))))

(valof-lexical '(let ((x 5)) 
                  (let ((f (lambda (y) (* x y))))
                    (let ((x 6))
                      (f x))))
               (empty-env))
;; => 30

(valof-dynamic '(let ((x 5)) 
                  (let ((f (lambda (y) (* x y))))
                    (let ((x 6))
                      (f x))))
               (empty-env))
;; => 36