Finish up ep11

This commit is contained in:
Sameer Rahmani 2022-11-05 12:46:53 +00:00
parent 0a6628e7da
commit a9d7962916
1 changed files with 77 additions and 1 deletions

View File

@ -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?