Work with the dynamic realm of Emacs Lisp in the old days

In the old days, Emacs did not support lexical reach. I wonder how people at that time faced a certain trap of a dynamic scale.

Suppose Alice writes the command my-insert-stuff , which relies on the fp-repeat function defined in fp.el (which, suppose, is a library that provides many functions for functional programming written by Bob) and suppose that fp-repeat used to repeat calling the function many times.

Alice's init.el content:

 (require 'fp) (defun my-insert-stuff () (interactive) ;; inserts "1111111111\n2222222222\n3333333333" to current buffer (dolist (i (list "1" "2" "3")) (fp-repeat 10 (lambda () (insert i))) (insert "\n"))) 

Some of the contents of fp.el from Bob:

 (defun fp-repeat (n func) "Calls FUNC repeatedly, N times." (dotimes (in) (funcall func))) 

Alice soon discovers that her team is not working as she expects. This is because Alice uses i and uses Bob i . In the old days, what could Alice and / or Bob do to prevent such a clash?

Maybe Bob could change the docstring to

 "Calls FUNC repeatedly, N times. Warning: Never use i, n, func in FUNC body as nonlocal variables." 
+4
source share
3 answers

Alice made sure not to use non-local variables in lambda bodies, knowing that lambda does not create lexical closures.

In Emacs Lisp, this simple policy is actually sufficient to avoid most dynamic scope problems, because in the absence of concurrency, local bindings of let dynamic variables are basically equivalent to lexical bindings.

In other words, Emacs Lisp developers of the "old days" (who are not so old, given the number of dynamically reached Emacs Lisp are still around), could not write such a lambda . They would not even want to, because Emacs Lisp was not a functional language (and still does not exist), so loops and explicit iteration are often preferred for higher-order functions.

For your specific example, Alice from the “old days” just wrote two nested loops.

+5
source

The way Emacs dealt with the problem is to follow a strict convention: Elisp programmers writing higher-order functions (like your fp-repeat ) should have used unusual variable names when a beam of light informed them that the function could be used by others, and when the ray of light did not work, they had to fulfill their daily prayers (always a good idea in the Church of Emakov).

+4
source

In addition to what Lunanorne and Stefan said:

In the specific example you specified, funarg passed to fp-repeat does NOT really need the i variable.

That is, there is no need to do anything with i AS A VARIABLE. That is, it is not necessary to use i as a specific SYMBOL, the value of which should be determined at a certain time or in a specific context (environment), when and where the function is called.

This whole function is really needed by VALUE i when and where the function is defined. Using a variable in this particular case is redundant - only its value is required.

Thus, another way of flashing the needle is to replace the value of a variable in the definition of a function, i.e. in lambda expression:

  (defun my-insert-stuff () (interactive) (dolist (i (list "1" "2" "3")) (fp-repeat 10 `(lambda () (insert ',i))) (insert "\n"))) 

It works great. There is no way to capture a variable because there is no variable.

The disadvantage is that during compilation there is also no function: LIST is created, car - lambda , etc. And this list is then evaluated at runtime, interpreted as a function.

Depending on the particular use case, this may be a useful method. Yes, that means you have to distinguish between contexts in which you really need to use a variable (that the function uses VARIABLE i , not just the value).

+3
source

Source: https://habr.com/ru/post/1497198/


All Articles