help-gnu-emacs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: sending function arguments to recursive function calls


From: Pascal J. Bourguignon
Subject: Re: sending function arguments to recursive function calls
Date: Tue, 07 May 2013 16:55:04 +0200
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/24.3 (gnu/linux)

"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\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


reply via email to

[Prev in Thread] Current Thread [Next in Thread]