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

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

Re: questioning let


From: Pascal J. Bourguignon
Subject: Re: questioning let
Date: Wed, 24 Feb 2010 12:59:22 +0100
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/23.1 (darwin)

Andreas Roehler <andreas.roehler@online.de> writes:

> Hi,
>
> behaviour of the example code below puzzles me. Would
> expect setting of arg by external function, but inside
> `let', recognised. But remains `1'.
>
> (defun arg-setting ()
>   (interactive)
>   (let ((arg 1))
>     (message "%s" arg)
>     (arg-extern arg)
>     (message "%s" arg)))
>
> (defun arg-extern (arg)
>   (setq arg (1- arg)))
>
> Any help?


let is equivalent to lambda:

  (let ((a 1) (b 2)) (list a b)) <=> ((lambda (a b) (list a b)) 1 2)

defun is binding a lambda to a function cell:

  (defun f (a b) (list a b))
       <=> (setf (symbol-function 'f) (lambda (a b) (list a b)))

Therefore you can see that calling a function defined by defun is a let
in disguise.


If you transformed your code following these equivalences, you would
notice that you have actually TWO variables named arg, one as parameter
of the function arg-extern, and one as variable in the let in
arg-setting.

The setq in arg-extern will modify only the variable parameter of
arg-extern.  Because they have the same name, this variable hides the
one defined in the let of arg-setting.  There's no way to access it from
within arg-extern.


If they had a different name, you could modify a variable from an outer
dynamic scope from an inner dynamic scope, because in emacs all the
variables are dynamic.  But it is considered very bad form to do so:
this is a big side effect, and what's more, one that depends on the call
chain.  You should avoid side effects, to increase the readability and
debugability of your code.  Therefore you should avoid setq and setf.
Try to write pure function, never try to modify a variable.

One way to write your code would be:

(defun do-what-you-need-to-do-with (arg)
   )

(defun arg-binding ()
  (interactive)
  (let ((arg 1))
     (message "before arg = %s" arg)
     (let ((arg (arg-extern arg)))
       (message "after arg = %s" arg)
       (do-what-you-need-to-do-with arg))
     (message "original arg = %s" arg)))

(defun arg-extern (arg)
   (message "arg-extern before arg = %s" arg)
   (message "arg-extern returns = %s" (1- arg))
   (1- arg))

before arg = 1
arg-extern before arg = 1
arg-extern returns = 0
after arg = 0
original arg = 1


If you need a global variable (perhaps because you need to keep some
data across command invocations), the I would advise to distringuish it
from the other by giving it a name surrounded by stars: *var*.  Then, it
will have a different name, and won't be shadowed (inadvertantly) by
inner lets, defuns or lambdas.

(defvar *var* 42)

(defun arg-extern (arg)
   (message "arg-extern before arg = %s" arg)
   (setf *var* (1- arg))
   (message "arg-extern returns = %s" *var*)
   *var*)

(arg-binding)
*var*  --> 0


-- 
__Pascal Bourguignon__


reply via email to

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