I was playing with code generation outside macro being inspired by corrected version of nif
https://letoverlambda.com/index.cl/guest/chap3.html#sec_5
When I replace the usual gensym with my interned-gensym I can skip the eval and have more noob friendly version of the code that I can easily copy to REPL for further experiments with macro code creation.
When it’s done, will I be able to replace with defmacro?
Why the usual gensym in macro is not interned?
Besides the eval is evil, what are other pitfalls of writing code this way?
(defun interned-gensym ()
"More suitable variant of gensym for macro experiments"
;; file:~/Programming/sbcl/src/code/symbol.lisp::593
(values (intern (format nil "Z~A"
(let ((old *gensym-counter*))
(setq *gensym-counter* (1+ old))
old)))))
;;; nif
(eval (apply (lambda (expr pos zero neg)
(let ((gexpr (interned-gensym)))
`(let ((,gexpr ,expr))
(cond
((plusp ,gexpr) ,pos)
((zerop ,gexpr) ,zero)
(T ,neg)))))
;; args for the lambda
((- 5 2) :positive :zero :negative)))
You’ll be able to essentially replace it with DEFMACRO, but it’s not 100% equivalent to do EVAL+APPLY. But the core idea is all the same: write code that creates valid S-expression forms.
INTERNED-GENSYM is there just to make code easier to read and type, because using uninterned symbols #:LIKE-THIS are moderately tricky to get the hang of. It is not advised to ever actually use INTERNED-GENSYM because every macro expansion is going permanently allocate memory for new symbols, and these will never get garbage collected.
At a higher level, the EVAL+APPLY way of doing things means that the lexical context (like previously bound variables) can’t be used, and all invocations of EVAL happen at run-time (not compile-time) so it would be excruciatingly slow.