all messages for Emacs-related lists mirrored at yhetil.org
 help / color / mirror / code / Atom feed
From: "Pascal J. Bourguignon" <pjb@informatimago.com>
To: help-gnu-emacs@gnu.org
Subject: Re: sending function arguments to recursive function calls
Date: Tue, 07 May 2013 16:55:04 +0200	[thread overview]
Message-ID: <878v3qu9fb.fsf@kuiper.lan.informatimago.com> (raw)
In-Reply-To: mailman.25279.1367935468.855.help-gnu-emacs@gnu.org

"Drew Adams" <drew.adams@oracle.com> 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\306\bDDDF\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\b!!\207" [inc text-scale-adjust abs] 3 nil nil]] 1]

  (make-scale-adjuster 42)
  --> #[nil "\301\302\b!!\207" [inc text-scale-adjust abs] 3 nil nil]
  (make-scale-adjuster 33)
  --> #[nil "\301\302\b!!\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


  parent reply	other threads:[~2013-05-07 14:55 UTC|newest]

Thread overview: 21+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2013-05-04 13:01 sending function arguments to recursive function calls Gauthier Östervall
2013-05-04 15:30 ` Drew Adams
2013-05-07 11:25   ` Gauthier Östervall
2013-05-07 14:04     ` Drew Adams
2013-05-08 12:21       ` Stefan Monnier
2013-05-09  8:35         ` Gauthier Östervall
2013-05-09 12:23           ` Stefan Monnier
2013-05-12 13:19             ` Gauthier Östervall
2013-05-13 14:55               ` Stefan Monnier
2013-05-17 12:20                 ` Gauthier Östervall
2013-05-17 12:26                   ` Dmitry Gutov
2013-05-17 14:31                     ` Drew Adams
2013-05-19 16:57                       ` Dmitry Gutov
2013-05-21 16:34                         ` Drew Adams
     [not found]                       ` <mailman.70.1368982677.22516.help-gnu-emacs@gnu.org>
2013-05-19 20:59                         ` Pascal J. Bourguignon
2013-05-20 19:31                           ` Dmitry Gutov
     [not found]                           ` <mailman.94.1369078320.22516.help-gnu-emacs@gnu.org>
2013-05-20 19:55                             ` Pascal J. Bourguignon
2013-05-07 14:32     ` Pascal J. Bourguignon
     [not found]     ` <mailman.25279.1367935468.855.help-gnu-emacs@gnu.org>
2013-05-07 14:55       ` Pascal J. Bourguignon [this message]
2013-05-08 12:25         ` Stefan Monnier
2013-05-05  1:22 ` Stefan Monnier

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=878v3qu9fb.fsf@kuiper.lan.informatimago.com \
    --to=pjb@informatimago.com \
    --cc=help-gnu-emacs@gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this external index

	https://git.savannah.gnu.org/cgit/emacs.git
	https://git.savannah.gnu.org/cgit/emacs/org-mode.git

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.