From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!not-for-mail From: Tim X Newsgroups: gmane.emacs.help Subject: Re: hypothetical question about macros Date: Fri, 15 Apr 2011 14:24:07 +1000 Organization: Unlimited download news at news.astraweb.com Message-ID: <8739lkcfbs.fsf@puma.rapttech.com.au> References: <27b70c81-5057-435e-8304-e400fa8e00d2@w9g2000prg.googlegroups.com> NNTP-Posting-Host: lo.gmane.org Mime-Version: 1.0 Content-Type: text/plain X-Trace: dough.gmane.org 1306267483 10272 80.91.229.12 (24 May 2011 20:04:43 GMT) X-Complaints-To: usenet@dough.gmane.org NNTP-Posting-Date: Tue, 24 May 2011 20:04:43 +0000 (UTC) To: help-gnu-emacs@gnu.org Original-X-From: help-gnu-emacs-bounces+geh-help-gnu-emacs=m.gmane.org@gnu.org Tue May 24 22:04:40 2011 Return-path: Envelope-to: geh-help-gnu-emacs@m.gmane.org Original-Received: from lists.gnu.org ([140.186.70.17]) by lo.gmane.org with esmtp (Exim 4.69) (envelope-from ) id 1QOxqJ-0003HF-2P for geh-help-gnu-emacs@m.gmane.org; Tue, 24 May 2011 22:04:39 +0200 Original-Received: from localhost ([::1]:43780 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1QOxqI-0002I4-8l for geh-help-gnu-emacs@m.gmane.org; Tue, 24 May 2011 16:04:38 -0400 Original-Path: usenet.stanford.edu!news.glorb.com!news2.glorb.com!news-xfer.nntp.sonic.net!news.astraweb.com!border6.newsrouter.astraweb.com!not-for-mail Original-Newsgroups: gnu.emacs.help User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/24.0.50 (gnu/linux) Cancel-Lock: sha1:7i6WZYt9kSKZoTWbOdJhYFVphxE= Original-Lines: 192 Original-NNTP-Posting-Host: ad0398cc.news.astraweb.com Original-X-Trace: DXC=?0a91CPF`U[7HkD[4f_4]SL?0kYOcDh@Z7^o:UA4R?cU6OS?0e1f7=\]G; 2>V^?kWSCAkl5c@Xgk^AfHOFMIU`dTZLmhCdFXR:U Original-Xref: usenet.stanford.edu gnu.emacs.help:186680 X-BeenThere: help-gnu-emacs@gnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: Users list for the GNU Emacs text editor List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: help-gnu-emacs-bounces+geh-help-gnu-emacs=m.gmane.org@gnu.org Original-Sender: help-gnu-emacs-bounces+geh-help-gnu-emacs=m.gmane.org@gnu.org Xref: news.gmane.org gmane.emacs.help:81029 Archived-At: Alan writes: > Here is a hypothetical question about macros. Since a macro can > result in code that gets interpreted, suppose one wanted to have a > macro insert the following code: I don't think this is the major point regarding macros. From that perspective, a macro is no different to a function in the sense they both result in code that gets interpreted. For me, the distinction, at least partially, is the different time at which macros get evaluated compared to functions and that the arguments to a macro are not evaluated as is the case with functions. This means you can add new elisp control structures and other programming constructs to extend the language etc. > > (insert "#1\n") > (insert "#2\n") > (insert "#3\n") > (insert "#4\n") > > This is a silly example, since in real life one is rather unlikely to > do such a thing. However, it is an exercise of interest for > understanding macros. > > Reading the documentation and making some trials, I don't see how to > do this. For example > > (defmacro aw-test () > '(insert "#1\n") > '(insert "#2\n") > '(insert "#3\n") > '(insert "#4\n")) > Remember that like functions, macros return a 'value' and the default is to return the value returned by the evaluation of the last form. Essentially, the first 3 forms are evaluated and return (insert ...), but that return value is ignored/thrown away. Only the last one is returned as the result of the macro execution and then evaluated. > and then using "eval-print-last-sexp" in the scratch buffer on > > (aw-test) > > results in > > #4 > nil > Yep, that is correct. > Similarly: > > (macroexpand '(aw-test)) > > results in > > (insert "#4 > ") > Yep, because only the result of the last statement evaluated is returned. In this case, it is evaluating the value of (quote (insert "#4\n")), which results in (insert "#4\n"), which is what the macro returns and is subsequently evaluated to insert #4\n into the buffer. > The following is a possibility: > > (defmacro aw-test () > '(progn (insert "#1\n") > (insert "#2\n") > (insert "#3\n") > (insert "#4\n"))) > > In the scratch buffer, using "eval-print-last-sexp" on > > (aw-test) > > then gives > > #1 > #2 > #3 > #4 > nil > > and > > (macroexpand '(aw-test)) > > results in: > > (progn (insert "#1 > ") (insert "#2 > ") (insert "#3 > ") (insert "#4 > ")) > > But I had to resort to using "progn" in this case. > > If there were some reason not to have to resort to using "progn" I > don't see how to do so. Consider what it is you are trying to do. You want the macro to return some elisp code that when evaluated, will give your desired result. Your desired result is to evaluate multiple forms. However, a macro only returns a single form. Using progn is one way of doing it, but think about how lisp code is structure and you can see alternative ways, though in this case, I think progn is perfectly acceptable. One way to think about macros is to work backwards rather than forwards. Start with the bit of code you want to have evaluated and then look at how that could be returned by the macro, then look at how to generate that structure within the macro and then look at how to add any dynamic bits (i.e. arguments etc passed as part of the macro call). Perhaps the benefits of macros cannot really be seen with the example your using as you could achieve the same outcome easily with a normal function. Here is a copy of the very first macro I ever wrote. It was to make my debugging of code much easier. Note that this macro does have some problems and limitations, but I think it demonstrates things with less complexity than a more sophisticated version which avoids potential pitfalls (for example, consider what impact a macro for debugging might have if it evaluated arguments as part of the expansion and either that evaluation changed the execution path of the code or the evaluation resulted in a side effect that affected the execution path of the code being debugged or what would happen with this macro if one of its arguments was something like (+ 1 2) or even more likely, you want to include some sort of evaluation such as (< a b) to aid in debugging). (defmacro my-debug (file func &rest vals) `(when *do-debug* (message "File: %s Function: %s" ,file ,func) (dolist (v ',vals) (message "\t%s = %s" (symbol-name v) (symbol-value v))))) and the way I would use it is something like (setq *do-debug* t) (defun my-func (arg1 arg2) (let ((a "this is a") (b "this is b")) (do some stuff.....) (my-debug "test.el" "my-func" arg1 arg2 a b))) (my-func "this is arg1" "this is arg2") To see why I used a macro rather than just a function, consider how you could have a function where inside the function you can determine the symbol names of the arguments used in the call so that you can write something like symbol's name = symbol's value as part of your debug output. i.e. the output from the above would be soemthing like File: test.el Function: my-func arg1 = this is arg1 arg2 = this is arg2 a = this is a b = this is b I personally found writing a macro to assist in my debugging a very valuable learning experience. The macro I typically use now is more complex, but handles things in a more general way. There are some things I still don't quite like with it and continue to improve and refine (and regularly break it). Nearly all the problems I've encountered with it are due to my lack of understanding/insight and working these out has really helped. In reality, I rarely use macros in elisp. I have used them more often in CL. I suspect this may be because, to some extent, elisp is a DSL already and most of the higher level control structures or extensions specific to things neded in an editor already exist. On the other hand CL is a more general programming language and applied in a wider range of domains, so you can easily find a situation where you need a new control structure or notice common domain specific bits of code that could be made clearer or reduce the level of boiler-plate code through the addition of some domain specific macros. In the end, macros are a really great and useful tool, but possibly used a lot less than one would expect for something which seems so powerful/useful. I only resort to a macro if what I want to do cannot be done with a function. Tim -- tcross (at) rapttech dot com dot au