Finish up ep11
This commit is contained in:
parent
0a6628e7da
commit
a9d7962916
|
@ -598,7 +598,8 @@ CLOSED: [2022-09-11 Sun 11:08]
|
|||
- https://lxsameer.com
|
||||
- [[https://twitter.com/lxsameer][@lxsameer]] on Twitter
|
||||
|
||||
* Episode 10 - More on Macros
|
||||
* DONE Episode 10 - More on Macros
|
||||
CLOSED: [2022-11-04 Fri 15:25]
|
||||
** Quasiquote aka backquote (`)
|
||||
- Backquote constructs allow us to quote an expression, but selectively evaluate sub-expressions
|
||||
- Similar to a template engine for lisp expressions
|
||||
|
@ -628,3 +629,78 @@ CLOSED: [2022-09-11 Sun 11:08]
|
|||
Let's discuss ~defflag~ and ~when-flag~ macros in [[file:../core/fg42/flags.el]]
|
||||
|
||||
* Episode 11 - Common pitfalls of Macros
|
||||
** Compiletime vs runtime
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
;; `do-something-with-side-effect' evaluates on compile time
|
||||
;; in the macro's context
|
||||
(defmacro wrong (x)
|
||||
(when x
|
||||
(do-something-with-side-effect x)))
|
||||
|
||||
;; `do-something-with-side-effect' evaluates on run time
|
||||
;; in the caller's context
|
||||
(defmacro correct (x)
|
||||
(when x
|
||||
`(do-something-with-side-effect ,x)))
|
||||
#+END_SRC
|
||||
** Variable Capture
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
;; `result' will hide any binding with the same name in the
|
||||
;; parent scope
|
||||
(defmacro wrong (f &rest body)
|
||||
`(let ((result ,f))
|
||||
(if result
|
||||
,@body
|
||||
(message "Failed: %s" result))))
|
||||
|
||||
(defvar result 1)
|
||||
(wrong 200 (message "> %s" result))
|
||||
|
||||
;; Instead we need to generate a local binding
|
||||
(defmacro correct (f &rest body)
|
||||
(let ((var (gensym)))
|
||||
`(let ((,var ,f))
|
||||
(if ,var
|
||||
,@body
|
||||
(message "Failed: %s" ,var)))))
|
||||
|
||||
(defvar result 1)
|
||||
(correct 200 (message "%s" result))
|
||||
#+END_SRC
|
||||
|
||||
More on *uninterned* symbols: https://www.gnu.org/software/emacs/manual/html_node/elisp/Creating-Symbols.html
|
||||
|
||||
** Many evaluation
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
;; The argument `x' will be evaluated 2 times in this example.
|
||||
;; what if it contains side effects?
|
||||
(defmacro wrong (x)
|
||||
`(if ,x
|
||||
(do-something-with ,x)
|
||||
(do-somthing-else ,x)))
|
||||
|
||||
;; It's better to use a local binding for it and evaluate it
|
||||
;; just once
|
||||
(defmacro correct (x)
|
||||
(let ((var (gensym)))
|
||||
`(let ((,var ,x))
|
||||
(if ,var
|
||||
(do-something-with ,var)
|
||||
(do-somthing-else ,var)))))
|
||||
#+END_SRC
|
||||
** Evaluating arguments of a macro
|
||||
Never evaluate the arguments of a macro manually. E.g. with =eval=
|
||||
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
;; `eval' here will be evaluated before the caller context
|
||||
;; is even populated
|
||||
(defmacro wrong (x)
|
||||
(list 'setq (eval x) 'blah))
|
||||
|
||||
;; The correct way is to mark `x' for evaluation instead
|
||||
(defmacro correct (x)
|
||||
`(setq ,x 'blah))
|
||||
#+END_SRC
|
||||
|
||||
More info: https://www.gnu.org/software/emacs/manual/html_node/elisp/Eval-During-Expansion.html
|
||||
** What's next?
|
||||
|
|
Loading…
Reference in New Issue