From mboxrd@z Thu Jan 1 00:00:00 1970 Path: main.gmane.org!not-for-mail From: Pascal Bourguignon Newsgroups: gmane.emacs.help Subject: Re: elisp macros problem Date: 27 Jul 2004 14:05:58 +0200 Organization: [posted via Easynet Spain] Sender: help-gnu-emacs-bounces+geh-help-gnu-emacs=m.gmane.org@gnu.org Message-ID: <87oem1bqop.fsf@thalassa.informatimago.com> References: NNTP-Posting-Host: deer.gmane.org Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii X-Trace: sea.gmane.org 1090930033 3316 80.91.224.253 (27 Jul 2004 12:07:13 GMT) X-Complaints-To: usenet@sea.gmane.org NNTP-Posting-Date: Tue, 27 Jul 2004 12:07:13 +0000 (UTC) Original-X-From: help-gnu-emacs-bounces+geh-help-gnu-emacs=m.gmane.org@gnu.org Tue Jul 27 14:07:05 2004 Return-path: Original-Received: from lists.gnu.org ([199.232.76.165]) by deer.gmane.org with esmtp (Exim 3.35 #1 (Debian)) id 1BpQjZ-00047M-00 for ; Tue, 27 Jul 2004 14:07:05 +0200 Original-Received: from localhost ([127.0.0.1] helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.33) id 1BpQme-0003gF-Bx for geh-help-gnu-emacs@m.gmane.org; Tue, 27 Jul 2004 08:10:16 -0400 Original-Path: shelby.stanford.edu!newsfeed.stanford.edu!news-spur1.maxwell.syr.edu!news.maxwell.syr.edu!easynet-monga!easynet.net!easynet-post2!not-for-mail Original-Newsgroups: gnu.emacs.help,comp.lang.lisp User-Agent: Gnus/5.09 (Gnus v5.9.0) Emacs/21.3 Original-Lines: 164 Original-NNTP-Posting-Host: 62.93.174.79 Original-X-Trace: DXC=FJ\HXSMnE^QMbeXN?D^CJZEY<>`XO4V7]>Uh 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 Xref: main.gmane.org gmane.emacs.help:19827 X-Report-Spam: http://spam.gmane.org/gmane.emacs.help:19827 Lowell Kirsh writes: > Hmmm, I was only trying to use macros because I thought what I was > trying wasn't possible with functions. Would this be a more > appropriate approach? > > (defun my-add-hook (hook &rest body) > (add-hook (intern (concat (symbol-name hook) "-mode-hook")) > `(lambda () ,@body))) > > and then call it like: > > (my-add-hook 'lisp-interaction > '(my-translate-paren-types) > '(imenu-add-to-menubar "Symbols") > ) > > This seems a little more verbose with all the quotes, but I guess it > could be the 'better' approach since it avoids using macros. > > Perhaps my macro approach was a crock. The only reason I defended it > was becuase I thought you meant that creating the my-add-hook > functionality itself was the crock. There is no fundamental difference between a function and a macro. The only difference, is that functions are called at so called "run-time" while macros are called at so called "compilation-time". Now, think about it, when you're using actually a lisp interpreter, when is "run-time", and when is "compilation-time"? (You can check CLHS to see how Common-Lisp defines these "times"). [There's also the little difference that macro lambda list may involve destructuring of arguments, while function lambda list don't and function must do it explicitely with DESTRUCTURE-BIND. But let's ignore this detail for now. In lisp that are lexically scoped like Common-Lisp, there's also that little impact, that scopes can be modified only at "compilation-time", not anymore at "run-time". So you'd HAVE to use a macro to process scopes and lexical bindings as first class objects in lexically scoped lisps. But since emacs lisp is not lexically scoped, this difference does not even matter.] Let's consider a simple macro: (defmacro myif (condition then &optional else) `(cond (,condition ,then) (t ,else))) (macroexpand-1 '(myif (= a b) 0 (- a b))) --> (COND ((= A B) 0) (T (- A B))) ; T To see how one can generate an equivalent function, let's use this macro: (defmacro functionify-macro (defmacro-sexp) ;; Naive implementation, for pedagogical purposes only. ;; Lacks smart handling of lambda lists... (let ((fun (intern (concatenate 'string (string (second defmacro-sexp)) "-FUN")))) `(progn (defun ,fun ,@(cddr defmacro-sexp)) (defmacro ,(second defmacro-sexp) ,(third defmacro-sexp) (,fun ,@(mapcan (lambda (arg) (cond ((listp arg) (list (first arg))) ((char= (character "&") (aref (string arg) 0)) nil) ((symbolp arg) (list arg)) (t (error "Bad argument")))) (third defmacro-sexp))))))) (macroexpand-1 '(functionify-macro (defmacro if (condition then &optional else) `(cond (,condition ,then) (t ,else))))) --> (PROGN (DEFUN IF-FUN (CONDITION THEN &OPTIONAL ELSE) `(COND (,CONDITION ,THEN) (T ,ELSE))) (DEFMACRO IF (CONDITION THEN &OPTIONAL ELSE) (IF-FUN CONDITION THEN ELSE))) ; T So, the macro just calls the function with exactly the same arguments. Well, "exactly", not exactly, because if that little difference between "compilation-time" and "run-time", when you invoke a macro, the arguments are not evalued, but when you call a function they are. Only that since the macro already has the arguments unevalued, what they are are literal sexps, not mere values, so when the macro calls the function, the arguments evaluate (at "compilation-time", remember the macro executes at "compilation-time", to themselves, and the function (called by the macro) executed at "compilation-time" will get literal sexps as arguments. That's why it'll be able to do the exact same job as the macro; (functionify-macro (defmacro myif (condition then &optional else) `(cond (,condition ,then) (t ,else)))) (macroexpand-1 '(myif (= a b) 0 (- a b))) --> (COND ((= A B) 0) (T (- A B))) ; T (myif-fun '(= a b) '0 '(- a b)) --> (COND ((= A B) 0) (T (- A B))) Macros are more difficult to use "programmatically", that is, if you don't know the exact value of the arguments at "compilation-time", it's harder to use a macro (you'd have to use eval and have other difficulties). That's why often you find that a defmacro is just a thin layer over a function, so you can use the macro at "compilation-time", and the function at "run-time". [See also the difference between CLOS (defclass, defmethod MACROS) and MOP (add-direct-subclass, add-method FUNCTIONS)]. Now, to come back to your problem, ;; a function: (add-hook-to-mode 'test '(message "test mode hook") '(message "first hook")) or: ;; a macro: (add-hook-to-mode test (message "test mode hook") (message "first hook")) I don't see the point, compared to: (add-hook 'test-mode-hook (lambda () (message "test mode hook") (message "first hook"))) Anyway, if you insist on your function, you can always use progn to minimize the number of quotes: (add-hook-to-mode 'test '(progn (message "test mode hook") (message "first hook"))) > Well, I'm going with functions now, and I'm also going to get back to > reading Paul Graham's 'On Lisp', the only comprehensive lisp macros > book I know of (are there others?) Beware that when you have a hammer in the hand, everything looks like a nail! -- __Pascal Bourguignon__ http://www.informatimago.com/ A: Because it messes up the order in which people normally read text. Q: Why is top-posting such a bad thing? A: Top-posting. Q: What is the most annoying thing on usenet and in e-mail?