From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!not-for-mail From: "Pascal J. Bourguignon" Newsgroups: gmane.emacs.help Subject: Re: sending function arguments to recursive function calls Date: Tue, 07 May 2013 16:55:04 +0200 Organization: Informatimago Message-ID: <878v3qu9fb.fsf@kuiper.lan.informatimago.com> References: <0F54256BD7B94384AC4DDA919D502C20@us.oracle.com> NNTP-Posting-Host: plane.gmane.org Mime-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Trace: ger.gmane.org 1367941505 26959 80.91.229.3 (7 May 2013 15:45:05 GMT) X-Complaints-To: usenet@ger.gmane.org NNTP-Posting-Date: Tue, 7 May 2013 15:45:05 +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 07 17:45:06 2013 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 1UZk4e-00069V-M0 for geh-help-gnu-emacs@m.gmane.org; Tue, 07 May 2013 17:45:04 +0200 Original-Received: from localhost ([::1]:34363 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1UZk4e-0004NM-77 for geh-help-gnu-emacs@m.gmane.org; Tue, 07 May 2013 11:45:04 -0400 Original-Path: usenet.stanford.edu!fu-berlin.de!uni-berlin.de!individual.net!not-for-mail Original-Newsgroups: gnu.emacs.help Original-Lines: 146 Original-X-Trace: individual.net foRBW6wmgwOvyihmCcfOkApIlRNww4kwJ/6xuZv2jWyPOE6AfY Cancel-Lock: sha1:OWNhYTI0NTA4NDE3NjBlMmI3YmZhNDA0MDIyNTA1MWY5ZTE2YjhjMA== sha1:2QJhthMU3+dXCf1KPhp85TSF+ck= Face: iVBORw0KGgoAAAANSUhEUgAAADAAAAAwAQMAAABtzGvEAAAABlBMVEUAAAD///+l2Z/dAAAA oElEQVR4nK3OsRHCMAwF0O8YQufUNIQRGIAja9CxSA55AxZgFO4coMgYrEDDQZWPIlNAjwq9 033pbOBPtbXuB6PKNBn5gZkhGa86Z4x2wE67O+06WxGD/HCOGR0deY3f9Ijwwt7rNGNf6Oac l/GuZTF1wFGKiYYHKSFAkjIo1b6sCYS1sVmFhhhahKQssRjRT90ITWUk6vvK3RsPGs+M1RuR mV+hO/VvFAAAAABJRU5ErkJggg== X-Accept-Language: fr, es, en User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/24.3 (gnu/linux) Original-Xref: usenet.stanford.edu gnu.emacs.help:198272 X-Mailman-Approved-At: Tue, 07 May 2013 11:44:52 -0400 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:90540 Archived-At: "Drew Adams" writes: >> > An alternative to using a lexical binding here would be to >> > simply use this: >> > `(lambda () (interactive) (text-scale-adjust (abs ',inc))) >> >> If this alternative works whatever lexical-binding, it seems superior >> to me. Isn't it? > > Yes. That's my opinion, anyway. In this case (and in many others), there is no > _logical_ reason to pass a variable and have that variable be looked up > lexically when the lambda is invoked. All that is needed is the variable's > value at the time the lambda is _constructed_. Anything else is overkill. > > But logic is not the only thing involved here. Disadvantages of substituting > the value can include: (a) backquote syntax, or its equivalent, is less readable > and (b) the byte compiler might not be able to compile the lambda in a > performant way. > > I'm no expert on Emacs byte compilation, but it's possible that the byte > compiler just punts on a dynamically constructed sexp such as the one above, > even though it might be able to figure out that the result in this case is > straightforward. Dunno. Well, the thing is that emacs lisp accepts as function any list starting with the symbol lambda, and when calling it, it just interprets it. Since the function is quoted not by function, then it won't be compiled (unless explicitely requested). (defun make-scale-adjuster (inc) `(lambda () (interactive) (text-scale-adjust (abs ',inc)))) (byte-compile 'make-scale-adjuster) --> #[(inc) "\301\302\303\304\305\306DDDF\207" [inc lambda nil (interactive) text-scale-adjust abs quote] 7] (make-scale-adjuster 42) --> (lambda nil (interactive) (text-scale-adjust (abs (quote 42)))) (eql (make-scale-adjuster 1) (make-scale-adjuster 2)) --> nil (functionp (make-scale-adjuster 33)) --> t (funcall (make-scale-adjuster 33)) --> (((#:t keymap (67108912 . #[0 #1="\301\302\300!!\207" [33 text-scale-adjust abs] 3 #2=" (fn)" nil]) (67108925 . #[0 #1# [33 text-scale-adjust abs] 3 #2# nil]) (67108907 . #[0 #1# [33 text-scale-adjust abs] 3 #2# nil]) (67108909 . #[0 #1# [33 text-scale-adjust abs] 3 #2# nil]) (48 . #[0 #1# [33 text-scale-adjust abs] 3 #2# nil]) (61 . #[0 #1# [33 text-scale-adjust abs] 3 #2# nil]) (43 . #[0 #1# [33 text-scale-adjust abs] 3 #2# nil]) (45 . #[0 #1# [33 text-scale-adjust abs] 3 #2# nil])))) ;; Oops, I shouldn't have done that… You could compile the lambda: (defun make-scale-adjuster (inc) (byte-compile `(lambda () (interactive) (text-scale-adjust (abs ,inc))))) which makes you lose that compilation time each time you callmake-scale-adjuster: (make-scale-adjuster 42) --> #[nil "\300\301\302!!\207" [text-scale-adjust abs 42] 3 nil nil] but hopefully, you'll get back this time when calling the generated function: (loop with scale-adjuster = (make-scale-adjuster 42) repeat a-lot do (funcall scale-adjuster)) Nonetheless, each time you call make-scale-adjuster, it has to return duplicated code: (make-scale-adjuster 42) --> #[nil "\300\301\302!!\207" [text-scale-adjust abs 42] 3 nil nil] (make-scale-adjuster 33) --> #[nil "\300\301\302!!\207" [text-scale-adjust abs 33] 3 nil nil] while only one constant changes… (setf lexical-binding t) (defun make-scale-adjuster (inc) (lambda () (interactive) (text-scale-adjust (abs inc)))) (make-scale-adjuster 42) --> (closure ((inc . 42) t) nil (interactive) (text-scale-adjust (abs inc))) (make-scale-adjuster 33) --> (closure ((inc . 33) t) nil (interactive) (text-scale-adjust (abs inc))) (byte-compile 'make-scale-adjuster) --> #[(inc) "\300\207" [#[nil "\301\302!!\207" [inc text-scale-adjust abs] 3 nil nil]] 1] (make-scale-adjuster 42) --> #[nil "\301\302!!\207" [inc text-scale-adjust abs] 3 nil nil] (make-scale-adjuster 33) --> #[nil "\301\302!!\207" [inc text-scale-adjust abs] 3 nil nil] (eq (make-scale-adjuster 42) (make-scale-adjuster 33)) --> t Here when compiling the function the closure inside is compiled at the same time, and when calling the compiled function, the same closed function is returned. (It is a little strange that they're eq, since they should be different closures, when calling them we get different results, but until emacs lisp becomes Common Lisp, we can't expect too much of it, can we). > Lexical binding was added to Emacs relatively recently. With time, perhaps it > and dynamic binding will become better roommates. And certainly, with time, > users will become more used to lexical binding and to looking out for > lexical/dynamic binding gotchas. Too bad in 2013, users must suffer those gotchas that have been debugged in the 70's by scheme users, and integrated in the 80's by all lispers (thru the common lisp). > For one thing, you will no doubt be reflexively looking for a local-variable > binding declaration in the file, right? ;-) And you will be looking for free > variables, and when you notice one you will ask yourself whether it will be > evaluated using lexical or dynamic binding. IOW, you will internalize things > like this help thread. A good trick is to wrap all your special^W dynamic variables in stars: (defvar *dynamic-variable* 'dyn) (let ((lexical-variable 'lex)) (defun f () (list *dynamic-variable* lexical-variable))) -- __Pascal Bourguignon__ http://www.informatimago.com/ You can take the lisper out of the lisp job, but you can't take the lisp out of the lisper (; -- antifuchs