From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!not-for-mail From: Stefan Monnier Newsgroups: gmane.emacs.help Subject: Re: defining a setter function with gv.el Date: Mon, 27 Aug 2012 16:28:32 -0400 Message-ID: References: <87ehmsyj2b.fsf@googlemail.com> <87vcg4cij6.fsf@googlemail.com> NNTP-Posting-Host: plane.gmane.org Mime-Version: 1.0 Content-Type: text/plain X-Trace: ger.gmane.org 1346099343 25640 80.91.229.3 (27 Aug 2012 20:29:03 GMT) X-Complaints-To: usenet@ger.gmane.org NNTP-Posting-Date: Mon, 27 Aug 2012 20:29:03 +0000 (UTC) Cc: emacs help To: Ivan Kanis Original-X-From: help-gnu-emacs-bounces+geh-help-gnu-emacs=m.gmane.org@gnu.org Mon Aug 27 22:29:03 2012 Return-path: Envelope-to: geh-help-gnu-emacs@m.gmane.org Original-Received: from lists.gnu.org ([208.118.235.17]) by plane.gmane.org with esmtp (Exim 4.69) (envelope-from ) id 1T65vg-0004Jq-Uf for geh-help-gnu-emacs@m.gmane.org; Mon, 27 Aug 2012 22:29:01 +0200 Original-Received: from localhost ([::1]:51329 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1T65ve-0005pW-IX for geh-help-gnu-emacs@m.gmane.org; Mon, 27 Aug 2012 16:28:58 -0400 Original-Received: from eggs.gnu.org ([208.118.235.92]:42305) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1T65vU-0005mK-Gf for help-gnu-emacs@gnu.org; Mon, 27 Aug 2012 16:28:54 -0400 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1T65vG-0001mj-BF for help-gnu-emacs@gnu.org; Mon, 27 Aug 2012 16:28:48 -0400 Original-Received: from chene.dit.umontreal.ca ([132.204.246.20]:41772) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1T65vG-0001lz-6Y for help-gnu-emacs@gnu.org; Mon, 27 Aug 2012 16:28:34 -0400 Original-Received: from faina.iro.umontreal.ca (lechon.iro.umontreal.ca [132.204.27.242]) by chene.dit.umontreal.ca (8.14.1/8.14.1) with ESMTP id q7RKSWhP011184; Mon, 27 Aug 2012 16:28:32 -0400 Original-Received: by faina.iro.umontreal.ca (Postfix, from userid 20848) id A1842B40D4; Mon, 27 Aug 2012 16:28:32 -0400 (EDT) In-Reply-To: <87vcg4cij6.fsf@googlemail.com> (Ivan Kanis's message of "Mon, 27 Aug 2012 20:10:53 +0200") User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/24.1.50 (gnu/linux) X-NAI-Spam-Flag: NO X-NAI-Spam-Threshold: 5 X-NAI-Spam-Score: 0 X-NAI-Spam-Rules: 1 Rules triggered RV4323=0 X-NAI-Spam-Version: 2.2.0.9309 : core <4323> : streams <806939> : uri <1203597> X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 132.204.246.20 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:86562 Archived-At: >> ;; (defun alist-get (key alist) >> ;; "Get the value associated to KEY in ALIST." >> ;; (declare >> ;; (gv-expander >> ;; (lambda (do) >> ;; (macroexp-let2 macroexp-copyable-p k key >> ;; (gv-letplace (getter setter) alist >> ;; (macroexp-let2 nil p `(assoc ,k ,getter) >> ;; (funcall do `(cdr ,p) >> ;; (lambda (v) >> ;; `(if ,p (setcdr ,p ,v) >> ;; ,(funcall setter >> ;; `(cons (cons ,k ,v) ,getter))))))))))) >> ;; (cdr (assoc key alist))) >> > Good grief, does it have to be so complicated for such a simple need? I think so, yes. CL's define-expander-macro would be a bit worse, but otherwise comparable. The expander macro itself above is: (macroexp-let2 macroexp-copyable-p k key (gv-letplace (getter setter) alist (macroexp-let2 nil p `(assoc ,k ,getter) (funcall do `(cdr ,p) (lambda (v) `(if ,p (setcdr ,p ,v) ,(funcall setter `(cons (cons ,k ,v) ,getter))))))))))) and the code we want to generate for a `setf' is along the lines of: (let* ((k KEY) (a ALIST) (p (assoc k (alist-getter a))) (v VAL)) (if p (setcdr p v) (alist-setter a (cons (cons k v) (alist-getter a))))) so it's not too far from the optimum (all those let-binding of KEY to `k' etc... are needed if KEY is an expression that could have side-effects so we need to make sure it's evaluated exactly once). Note that the above expander can be used for things like (push VAL (alist-get KEY ALIST)) as well, where we want to generate code like (let* ((k KEY) (a ALIST) (p (assoc k (alist-getter a))) (v VAL)) (if p (setcdr p (cons v (cdr p))) (alist-setter a (cons (cons k (cons v (cdr p))) (alist-getter a))))) Note that it doesn't work quite right for `letf', where (letf (((alist-get KEY ALIST) VAL)) BODY) gets expanded to: ELISP> (macroexpand '(letf (((alist-get KEY ALIST) VAL)) BODY)) (let* ((p (assoc KEY ALIST)) (vnew VAL) (old (cdr p))) (unwind-protect (progn (if p (setcdr p vnew) (setq ALIST (cons (cons KEY vnew) ALIST))) BODY) (if p (setcdr p old) (setq ALIST (cons (cons KEY old) ALIST))))) So to be correct for `letf' we could use something like: (defun alist-get (key alist) "Get the value associated to KEY in ALIST." (declare (gv-expander (lambda (do) (macroexp-let2 macroexp-copyable-p k key (gv-letplace (getter setter) alist (macroexp-let2 nil p `(assoc ,k ,getter) (funcall do `(cdr ,p) (lambda (v) `(if ,p (setcdr ,p ,v) ,(funcall setter `(cons (setq ,p (cons ,k ,v)) ,getter))))))))))) (cdr (assoc key alist))) But for most uses this extra `setq' would be at best useless and might make the code less efficient (a var that's never `setq'd can be captured into a closure much more cheaply because we can just keep a copy of its value, rather than keeping a reference to the variable itself and looking up its value everytime). -- Stefan