3

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.

Will Ness
  • 70,110
  • 9
  • 98
  • 181
George Mauer
  • 117,483
  • 131
  • 382
  • 612

2 Answers2

4

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))
Sylwester
  • 47,942
  • 4
  • 47
  • 79
  • Thanks, I knew about thunks but yeah, just wanted to see how to do it here - that struct is really interesting, thank you – George Mauer Jul 22 '20 at 13:44
1

Use backquote together with unquote which is

;; backquote: ` 
;; unquote: ,
;; there is also splice list: ,@

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

;; "Five": 5
;; "Twelve": 12
;; "Three": 3

Gwang-Jin Kim
  • 9,303
  • 17
  • 30
  • Sorry, let me clarify, I don't want to evaluate it at that point in time. I want to evaluate it just before it is displayed. I will edit the question – George Mauer Jul 21 '20 at 13:49
  • 1
    If you want to let it be evaluated just before it is displayed, you are better off using functions because that is exactly their task. (Evaluation at runtime). or you have to write a function which is a small interpreter for those functions you use in that expression to be evaluated. – Gwang-Jin Kim Jul 21 '20 at 14:47
  • Yup, I get that, but this is about me learning how to play with metaprogramming. I understand that wouldn't actually be good practice – George Mauer Jul 21 '20 at 18:06
  • 1
    We are all learning. And in some situations, eval should not be forbidden. read thishttps://lisp-univ-etc.blogspot.com/2020/06/eval-spotted-in-wild.html I commented on this. – Gwang-Jin Kim Jul 21 '20 at 19:01