Just use an uninterned symbol that cannot be reached outside of the let
.
You need that uninterned symbol two times, one time for the let
and one time within foo
.
The purpose of the additional outer let
-wrapper is to provide that uninterned symbol via make-symbol
.
(let ((f-uninterned (make-symbol "f")))
(eval
`(let ((,f-uninterned load-file-name))
(defun foo () ,f-uninterned))
'lexical))
For the locally used symbol f-uninterned
it is irrelevant whether lexical or dynamical binding is in effect.
So, with this snippet of Elisp, it is even irrelevant whether the buffer is loaded with lexical or dynamical binding. The eval
with non-nil second argument ensures lexical binding for the contained backquoted form.
You get the same effect with the following elisp code. It works because a function argument (in our case the f
of the lambda
) is an unbound symbol:
;; -*- lexical-binding: t -*-
(funcall (lambda (&optional f)
(setq f load-file-name)
(defun foo () f)))
…or similar:
;; -*- lexical-binding: t -*-
(funcall (lambda (f)
(defun foo () f))
load-file-name)
Note, that using uninterned symbols is a well-known technique for macro programming, where it is even more critical, since there you are fighting local variables within user-defined functions (see the explanation about Macros in the Elisp manual).
If you are writing your own library, say mylib
, an alternative would be to prefix the name of the lexically bound symbol with the library name such as in mylib-f
.
When you are writing such a library you did already make sure that the library name is unique. Otherwise you would have problems on a higher level than that of your question.
Assuming the library name is unique, you have control over what the symbol mylib-f
is used for.
When I wrote the second funcall
solution I thought that this would allow an easy macro definition. Furthermore, your comment on dlet
inspired me to search for an lexical equivalent llet
. Voila see the comment in cconv.el
.
I think they dismissed that solution because it is only working with lexical binding.
I give here a more complete definition of llet
and an additional suitable llet*
:
;; -*- lexical-binding: t -*-
(defmacro llet (bindings &rest body)
"Do like `let' but always use lexical binding.
This only works in libraries with lexical binding."
(declare (indent 1)
(debug ((&rest [&or symbolp (symbolp form)])
body)))
`(funcall (lambda ,(mapcar
(lambda (binding)
(if (consp binding)
(car binding)
binding))
bindings)
,@body)
,@(mapcar
(lambda (binding)
(when (consp binding)
(cadr binding)))
bindings)))
(defmacro llet* (bindings &rest body)
"Do like `let*' but always use lexical binding.
This only works in libraries with lexical binding."
(declare (indent 1)
(debug ((&rest [&or symbolp (symbolp form)])
body)))
(if bindings
`(llet ,(list (car bindings))
,(macroexpand
`(llet* ,(cdr bindings)
,@body)))
`(progn ,@body)))