[net.emacs] MLisp local variables in UniPress

ksr@vax135.UUCP (Ken Roberts) (07/22/86)

There is an interesting subtlety about local variables and parameter
passing in the MLisp of Unipress (Gosling) Emacs.
Suppose I define the functions 'insert-times' and 'my-test':

(defun
    (insert-times   this-string  n
     ; Inserts 'n' (argument 2) copies
     ; of the string given by 'this-string' (argument 1).
        (setq this-string (arg 1))
        (setq n (arg 2))
        (while (> n 0)
            (setq n (- n 1))
            (insert-string this-string)
        )
    )
)

(defun
    (my-test   this-string
        (insert-times "Hello--" 2)
        (setq this-string "Hello--")
        (insert-times this-string 2)
    )
)

If I now invoke the function 'my-test', 
what will be inserted is not "Hello--Hello--Hello--Hello--",
but rather "Hello--Hello00".
This is because the expression for argument 1 is not evaluated
at the time of the invocation of 'insert-times'; but rather when
(arg 1) is reached.  By that time, the name 'this-string' has been
rebound, and initialized to 0 (by the new local declaration in
the function 'insert-times').

Conclusion:  Due to the way the 'arg' function is defined, local
variables are not entirely insulated from other variables
(local or global) that have the same name.

This is a problem, since the major selling point of local variables
is supposed to be that you are free to choose names for them
without bothering to check if those names are already being used
somewhere else.

Ken Roberts

mg@unirot.UUCP (Mike Gallaher) (07/29/86)

>    Conclusion:  Due to the way the 'arg' function is defined, local
>    variables are not entirely insulated from other variables
>    (local or global) that have the same name.
>
>    This is a problem, since the major selling point of local variables
>    is supposed to be that you are free to choose names for them
>    without bothering to check if those names are already being used
>    somewhere else.

This is indeed one of the most confusing subtleties of MLisp.  (It wasn't
even documented anywhere before V2.10!)  Once you understand what is going
on, it is not hard to avoid problems.  The MLisp coding guidelines
(doc/mlisp-std) recommend that local variable names be prefixed by some
string unique to the function it is defined in.  For instance, a function
called describe-moused-word might have a variable $dmw-count.  (The '$' is
a naming convention indicating that it is a local variable.)

Also, be careful about evaluating args that may depend on which buffer is
current, or where the region is, etc.  For instance, suppose you have

    (defun (foo	    
	(temp-use-buffer "*scratch*"
	    (setq @foo (arg 1))
	)
    ))
    (mark-whole-buffer)
    (foo (region-to-string))

foo will evaluate the argument, (region-to-string), within the buffer
*scratch*, not in the buffer from which foo was called.  Though you can
usually write the code so as not to depend on such things, the most general
way around this problem is to have functions evaluate their arguments and
store the results in local variables before doing anything else:

    (defun (foo	    $s
	(setq $s (arg 1))
	(temp-use-buffer "*scratch*"
	    (setq @foo $s)
	)
    ))

Once you come to properly love MLisp, you will realize that you wouldn't
want it to be any other way. :-)

Mike Gallaher
Unipress Software