3

AFAIK turning on lexical binding only affects the current buffer. But if I add lexical scoped code to my crusty old init file which have load and require statements in it (not everything is autoloaded) then turning on lexical scoping in the init file can affect the loaded and required files too which can cause problems if those codes are not lexically scoped.

Is this correct?

  • The lexical scoping is in effect only for the file where it is declared (e.g. your init file). In most cases you won't have a problem. That said, if your init file defines stuff that you use in other places, or vice versa, and if the behavior of stuff you use depends on using dynamic scoping then you might not get what you expect. Generally speaking, you need to check code that uses stuff whose definitions you convert from depending on dynamic scoping to depending on lexical scoping. But again, in most cases you need not worry. – Drew Jun 21 at 17:56
  • The help says "Non-nil means that the code in the current buffer should be evaluated with lexical binding." Current buffer. So when the init file is loaded that is "the current buffer". If I require a package from the current buffer and the current buffer is set to lexically scope then does it affect the required package? – Tom Jun 21 at 18:08
  • 1
    "If I require a package from the current buffer and the current buffer is set to lexically scope then does it affect the required package?" No. But yes, variable lexical-binding is buffer-local. The most common way of setting it is as a file-local variable. In any case, you should read (elisp)Using Lexical Binding and C-h v lexical-binding. – Drew Jun 21 at 21:27
1

The lexical-binding settings of the files loaded with load and require are not affected by the (buffer-local) lexical-binding setting of the init file.

Warning: But, a change of lexical-binding in the init file can cause errors there.

Example for a change through lexical-binding t:

(setq var-a 0)

(defun a ()
  (message "a: %s" var-a))

(defun b ()
  (let ((var-a 1))
    (a)))

Calling (b) gives 0 with lexical binding and 1 with dynamical binding.

You need a defvar for var-a before (defun b...) the to declare a as special (i.e., always dynamically bound).

The defvar can also be indirect through a require of the package where var-a already has a defvar.


You are hard to convincesmiley.

Let's have a look.

  1. We leave no room for speculations. We check the lexical binding directly by effect and not through testing the variable lexical-binding. Testing lexical-binding would give the same result.
  2. Note that the macro lexical-p is expanded during the evaluation of the loaded files.

.emacs init file:

;;; -*- lexical-binding: t -*-

(defmacro lexical-p ()
  "Return non-nil in buffers with lexical binding."
  '(let* ((ret t)
          (code (lambda ()
                  ret)))
     (let ((ret nil))
       (funcall code))))

(message "Load file name: %s, Lexical binding: %s" load-file-name (lexical-p))

(add-to-list 'load-path "/tmp")

(load "loaded.el")

(require 'required)

The load file loaded.el:

(message "Load file name: %s, Lexical binding: %s" load-file-name (lexical-p))

The library required.el:

(message "Load file name: %s, Lexical binding: %s" load-file-name (lexical-p))
(provide 'required)

The messages at start of emacs:

Load file name: /home/naehring/.emacs, Lexical binding: t
Loading /tmp/loaded.el (source)...
Load file name: /tmp/loaded.el, Lexical binding: nil
Loading /tmp/loaded.el (source)...done
Load file name: /tmp/required.el, Lexical binding: nil
  • My question is about setting lexical-binding to t in the init.file at the top and then the init.file requires and loads some packages. Can those packages can break if they are not prepared for lexical scoping? – Tom Jun 21 at 18:03
  • @Tom I address your comment with an example in my answer. – Tobias Jun 21 at 22:53
  • Thanks. I was unsure, because I created a simple .el file which only printed the value of lexical-binding. Then in scratch I set lexical-binding to t and then evaluated loading that file (load "test.el"). And it printed t which confused me. But now I realize t was the result of the evaluated load function which I confused with the print output. Silly mistake. B) – Tom Jun 22 at 5:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.