How do I make a macro that evaluates an argument only once? reddit / emacs ---------------------------------------------------------------------- flora_best_maid · 2022-01-27 07:58 Let's say I have the following macro (defmacro dup (f x) "Call F by applying X twice." (if (symbolp f) (list f x x) (append f (list x x)))) It does work: (dup concat "me") ; "meme" (dup (concat "nice ") "me") ; "nice meme" However, it has some undesireable behaviour: (macroexpand '(dup some-function (long (composition x)))) ; (some-function (long (composition x)) (long (composition x))) How do I make a macro that evaluates X once? I understand I could do it with a function, but I like that I don't have to quote things in a macro. ---------------------------------------------------------------------- Least_Isopod_4061 · 2022-01-27 08:14 Am I missing something or can't you bind it at the start? (defmacro dup (f x) (let ((y (eval x))) (if (symbolp f) (list f y y) (append f (list y y))))) ---------------------------------------------------------------------- flora_best_maid · 2022-01-27 08:50 Because it has different undesired behaviour: (macroexpand '(dup append (list "1" "2" "3"))) ; (append ("1" "2" "3") ("1" "2" "3")) ---------------------------------------------------------------------- Least_Isopod_4061 · 2022-01-27 09:16 Oh I see, you don't want to actually evaluate it. I think this works with all your examples (defmacro dup (f x) `(let ((y ,x)) ,(if (symbolp f) `(,f y y) (append f '(y y))))) ---------------------------------------------------------------------- deaddyfreddy · 2022-01-27 11:09 > I understand I could do it with a function, but I like that I don't have to quote things in a macro. "the first rule is: don't use a macro where a function will do." ---------------------------------------------------------------------- flora_best_maid · 2022-01-27 12:46 That's indeed what I want. It seems obvious now. Thanks! ---------------------------------------------------------------------- Gangsir · 2022-01-27 13:48 >I understand I could do it with a function, but I like that I don't have to quote things in a macro. Use things for their intended purposes. That's like saying "I need to hammer nails, but I prefer using a drill because I hate hitting things". Lisp is annoying sometimes with syntax. Gotta deal. ---------------------------------------------------------------------- 00-11 · 2022-01-27 17:33 +1. This is also the second rule. And the third. ---------------------------------------------------------------------- arthurno1 · 2022-01-27 19:19 > How do I make a macro that evaluates X once? You let-bind it to a variable. You eval x when binding it, and use the binding in rest of your macro. > I understand I could do it with a function How would function change the fact that you are evaling symbol every time you use it? It is a good practice to eval you macro args once, however, every time you use a variable, system vill have to eval it to get it's value. That is regardless of if it is a macro or function. Lisp works with symbols, it is not a statically compiled language like C. ---------------------------------------------------------------------- arthurno1 · 2022-01-27 19:24 > Use things for their intended purposes. What is the intended purpose? > Lisp is annoying sometimes with syntax. Don't we invent tools to deal with annoying things? Isn't it the purpose of programming languages to start with? ---------------------------------------------------------------------- gepardcv · 2022-01-27 22:37 Read _On Lisp_: http://www.paulgraham.com/onlisp.htm Elisp is shittier than Common Lisp (the language used in the book), but it's close enough and most (all?) basic techniques from the book should work with only a few changes. The topic of macro argument evaluation, and its pitfalls, is covered in extensive detail. ---------------------------------------------------------------------- fragglestickcar0 · 2022-01-27 23:04 > Elisp is shittier than Common Lisp As much as I embrace your contemptuous world view, I generally avoid shitting on people unless I know I can write something better. In the case of elisp, I'm certain I couldn't. ---------------------------------------------------------------------- BobSanchez47 · 2022-01-27 23:40 There’s a section of the book *Let over Lambda* which deals with this. [Here is the link](https://letoverlambda.com/index.cl/guest/chap3.html#sec_5). Read it and play around with it; it explains things extremely well. In most cases, if you only want something evaluated once, you should just use a function. For example, you could use ``` (defun dup (f x) (apply f x x)) ``` though this would unfortunately require you to use the `#’` everywhere. If you insist on doing things with macros, the correct code would be as follows: ``` (defmacro dup (f x) (let ((g (gensym))) `(let ((,g ,x)) (,f ,g ,g)))) ``` If you try `(macroexpand ‘(dup (f (print 1))))`, you should get something like ``` (let ((GENSYM:42 (print 1))) (f GENSYM:42 GENSYM:42)) ``` Here, `GENSYM:42` is a fresh variable name generated by the compiler which is guaranteed not to clash with any previously existing names. ---------------------------------------------------------------------- BobSanchez47 · 2022-01-27 23:48 This doesn’t work at all. For example, consider the following code: ``` (defun square (x) (dup * x)) ``` You’ll get a compile-time error since `x` can’t be evaluated at compile time. ---------------------------------------------------------------------- c256 · 2022-01-28 05:53 That seems like an awful lot of quoting to avoid quoting. :-)