From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!not-for-mail From: Toby Cubitt Newsgroups: gmane.emacs.help Subject: Re: Writing a function/macro in Elisp that generates a function at runtime Date: Tue, 28 Oct 2008 11:38:20 +0100 Message-ID: <4906EB9C.10501@dr-qubit.org> References: Reply-To: Toby Cubitt NNTP-Posting-Host: lo.gmane.org Mime-Version: 1.0 Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: 7bit X-Trace: ger.gmane.org 1225194119 11434 80.91.229.12 (28 Oct 2008 11:41:59 GMT) X-Complaints-To: usenet@ger.gmane.org NNTP-Posting-Date: Tue, 28 Oct 2008 11:41:59 +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 Oct 28 12:43:01 2008 connect(): Connection refused Return-path: Envelope-to: geh-help-gnu-emacs@m.gmane.org Original-Received: from lists.gnu.org ([199.232.76.165]) by lo.gmane.org with esmtp (Exim 4.50) id 1KumyL-0004du-1v for geh-help-gnu-emacs@m.gmane.org; Tue, 28 Oct 2008 12:42:53 +0100 Original-Received: from localhost ([127.0.0.1]:53134 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1KumxE-0006H6-HU for geh-help-gnu-emacs@m.gmane.org; Tue, 28 Oct 2008 07:41:44 -0400 Original-Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1Kumu4-00053c-5E for help-gnu-emacs@gnu.org; Tue, 28 Oct 2008 07:38:28 -0400 Original-Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1Kumu2-00052m-Dx for help-gnu-emacs@gnu.org; Tue, 28 Oct 2008 07:38:27 -0400 Original-Received: from [199.232.76.173] (port=46027 helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1Kumu2-00052h-B3 for help-gnu-emacs@gnu.org; Tue, 28 Oct 2008 07:38:26 -0400 Original-Received: from mail.geekisp.com ([216.168.135.169]:46805 helo=starfish.geekisp.com) by monty-python.gnu.org with esmtps (TLS-1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.60) (envelope-from ) id 1Kumu1-0003Ms-SM for help-gnu-emacs@gnu.org; Tue, 28 Oct 2008 07:38:26 -0400 Original-Received: (qmail 17658 invoked by uid 1003); 28 Oct 2008 11:38:24 -0000 Original-Received: from [192.168.2.9] (localhost.geekisp.com [127.0.0.1]) by localhost.geekisp.com (tmda-ofmipd) with ESMTP; Tue, 28 Oct 2008 07:38:22 -0400 User-Agent: Thunderbird 2.0.0.17 (X11/20080929) In-Reply-To: X-Delivery-Agent: TMDA/1.1.11 (Ladyburn) X-Primary-Address: toby@dr-qubit.org X-detected-operating-system: by monty-python.gnu.org: OpenBSD 3.0-3.9 X-BeenThere: help-gnu-emacs@gnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: Users list for the GNU Emacs text editor List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Original-Sender: help-gnu-emacs-bounces+geh-help-gnu-emacs=m.gmane.org@gnu.org Errors-To: help-gnu-emacs-bounces+geh-help-gnu-emacs=m.gmane.org@gnu.org Xref: news.gmane.org gmane.emacs.help:59202 Archived-At: Barry Margolin wrote: > In article , > Toby Cubitt wrote: >> I'm trying to write a function in Elisp that generates a function at >> runtime, but hitting the limits of my Lisp abilities. Here's a first >> attempt at the kind of thing I'm trying to do: >> >> (defun wrap-insert-function (insfun) >> `(lambda (new-data old-cell) >> (setf (cell-data old-cell) >> (funcall ,insfun new-data (cell-data old-cell))) >> old-cell)) [snip] >> The problem is, because it's quoted, the `setf' macro never gets >> expanded by the byte-compiler. And that's no good, because the cl >> package where `setf' is defined shouldn't be loaded at runtime, and > > To do that you'd need lexical closures, but Elisp doesn't have them. > Since you're not generating the function until runtime, there's nothing > to compile at compile time. I can start to see how to do it with lexical closures. (How many times have I wished for them in Elisp! Bring on the lexbind branch...). But is there really no way to do this without closures? That seems strange. The above definition works fine, except for the setf issue. As you say, the run-time generated function obviously can't be compiled, because it doesn't exist at compile-time. But that's not what I asked for. I want the setf macro to be *expanded* at compile time into the setf method for `cell-data', and there *is* enough information at compile-time for that. I had another go, and came up with the following attempt (I can hear the Elisp experts whincing already - I know it's ugly!) (defmacro wrap-insfun-2 (f-2) (let ((comma-f `(nil ,f-2))) (setcar comma-f ',) (macroexpand-all `(lambda (new old) (setf (cell-data old) (,comma-f (cell-data new) (cell-data old))))))) (defmacro wrap-insfun-1 (f-1) `(eval (backquote ,(macroexpand-all `(wrap-insfun-2 ,f-1))))) (defun wrap-insfun (insfun) (wrap-insfun-1 insfun)) This works as long as `wrap-insfun-1' is not byte-compiled (I don't completely understand why it fails when byte-compiled, but I guess it's because `backquote' does't work on the byte-compiled expansion of `wrap-insfun-2'.) I was trying to have macros generate the body of the wrap-insfun function, so that I could force setf to be macro-expanded when compiled. Getting macros to generate code containing a backquote construct seems to be tricky, I guess because backquote expansion gets confused by the commas that are supposed to be part of the generated code, hence hiding this inside `comma-f'. Is generating a backquote construct possible using macros, in a robust way that byte-compiles properly, and without the ugly hack of making use of Emacs's internal old-style backquote implementation as I've done? Or is this simply impossible? Or is Elisp lacking some Lisp feature that would allow this? (reader macros?) > You don't need to generate a function on the fly for this. Do something > like this: > > (defun insert-with-function (insfun new-data old-cell) > (setf (cell-data old-cell) > (funcall insfun new-data (cell-data old-cell))) > old-cell) Hmmmm...the simplest ideas are always best. This does sound like a better approach. Thanks! I'm still curious about the above macro shenanigans though, since it's pushing at the boundaries of my understanding of Lisp and I'd like to understand these issues more fully. Toby