Search code examples
schemeracketmetaprogrammingevaluationexpression-evaluation

Racket "eval" a datum


I'm trying to level up on metaprogramming in Racket and realized I don't know how to take a datum and simply "eval" it.

If I have

  (for ((x '(("Five" (+ 2 3))
             ("Twelve" (* 6 2))
             ("Three" (- (/ 21 3) 4)))))
    (displayln (format "~s: ~s" (first x) (second x))))

I get

: "Five": (+ 2 3)
: "Twelve": (* 6 2)
: "Three": (- (/ 21 3) 4)

Which is not actually what I want - I want to actually evaluate that list to get the answer.

I'm sure this is simple (perhaps something I need to involve syntax for?) but I'm just missing the picture now. How do I do that?

Edit: I want to evaluate the s-exp just before displaying, not in the initial list. This is why I figure I might need syntax since I would (I think) have to inject the current syntax context.


Solution

  • Eval is almost always the wrong choice, but eval is what you are looking for:

    #lang racket
    
    (define base-ns (make-base-namespace))
    (for ((x '(("Five" (+ 2 3))
               ("Twelve" (* 6 2))
               ("Three" (- (/ 21 3) 4)))))
      (displayln (format "~s: ~s" (first x) (eval (second x) base-ns))))
    

    Alternative 1: Lambda / thunks

    (for ((x `(("Five" ,(thunk (+ 2 3)))
               ("Twelve" ,(thunk (* 6 2)))
               ("Three" ,(thunk (- (/ 21 3) 4))))))
      ;; notice double parentheses to call the thunk
      (displayln (format "~s: ~s" (first x) ((second x)))))
    

    A thunk is just syntax sugar for a lambda with no arguments. I've played a little around having procedures that can print their sources. Thus you can make your own thunk that has the original structure as structure as I demonstrate with my visual lambda:

    (struct proc (src obj)
      #:property prop:procedure (struct-field-index obj)
      #:transparent
      #:methods gen:custom-write
      [(define (write-proc x port mode)
         ((case mode
            [(#t) write]
            [(#f) display]
            [else pretty-print])
          (proc-src x)
          port))])
    
    (define-syntax lambda*
      (syntax-rules ()
        ((_ . rest)
         (proc '(lambda* . rest) (lambda . rest)))))
    
    (define test (lambda* (x y) (+ x y)))
    
    test                  ; ==> #(struct:closure (lambda* (x y) (+ x y)) #<procedure>)
    (proc-src test)       ; ==> (lambda* (x y) (+ x y))
    (proc-obj test)       ; ==> #<procedure>
    ((proc-obj test) 1 2) ; ==> 3
    (test 1 2)            ; ==> 3
    (display test)        ; prints (lambda* (x y) (+ x y))