From 3d5d9f8f533743ad58de50e1a4333c0792e09f66 Mon Sep 17 00:00:00 2001 From: Andreas Roehler Date: Wed, 24 Feb 2010 22:08:47 +0100 Subject: [PATCH] questioning let --- code/elbb.el | 135 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 135 insertions(+) diff --git a/code/elbb.el b/code/elbb.el index 0d40d49..efcfba8 100644 --- a/code/elbb.el +++ b/code/elbb.el @@ -1,3 +1,138 @@ +* questioning let + +Andreas Roehler 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? + +From: David Kastrup +Date: Wed, 24 Feb 2010 19:10:38 +0100 +To: help-gnu-emacs@gnu.org +Subject: Re: questioning let + +>> The argument binding in arg-extern is the innermost one and consequently +>> the only affected one. If you make the function argument-less, it will +>> likely work as expected by you, affecting the binding in arg-setting. +> +> That works, thanks a lot! +> However, stored in some eil.el, get a compiler warning than: +> +> +> In arg-extern: +> eil.el:9:9:Warning: reference to free variable `arg' +> eil.el:9:17:Warning: assignment to free variable `arg' +> +> Would think a useless warning, as the compiler should know being inside a let (?) + +The warning is completely accurate since arg-extern can be called from +outside arg-setting, in which case it will assign to a global variable +called "arg". + +Whether or not some let-binding might be effective at the point of +calling arg-extern is unknown to the compiler. + +In a Lisp variant with lexical binding (like Common Lisp or Scheme), +arg-extern has no way to fiddle with the let-binding of arg-setting: it +is completely inaccessible by name outside of arg-setting itself. + +-- +David Kastrup + +From: pjb@informatimago.com (Pascal J. Bourguignon) +Date: Wed, 24 Feb 2010 12:59:22 +0100 +To: help-gnu-emacs@gnu.org +Subject: Re: questioning let + +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__ + * real tab completion in shell-mode To: help-gnu-emacs@gnu.org -- 2.11.4.GIT