Elisp与 nix-shell的 shebang:如何开启词法绑定?
我目前的项目涉及一个Elisp(Emacs-Lisp)解释型脚本,并且为了可重复性,包含一个 nix-shell shebang 与一个固定的 pinned nixpkgs。
我的脚本还涉及一个 named-let 结构,这需要启用 词法绑定。
两者都可以相对容易地分别实现,下面的最小可复现示例(MRE)中有演示:
- 在不使用nix-shell的情况下启用词法绑定:
#!/usr/bin/env -S emacs -f main -Q --script
;;; -*- lexical-binding: t -*-
(defun main ()
(named-let adder-rec ((msg "Hello World!"))
(message msg)))
;; $ ./with_named_let.el
;; Hello World!
- 在不需要任何词法绑定要求的前提下使用nix-shell shebang:
#!/usr/bin/env nix-shell
#! nix-shell -i "emacs -f main -Q --script" --pure
#! nix-shell -p emacs
#! nix-shell -I nixpkgs=https://github.com/NixOS/nixpkgs/archive/a0374025a863d007d98e3297f6aa46cc3141c2f0.tar.gz
;;; -*- lexical-binding: t -*-
(defun main ()
(message "Hello World!"))
;; $ ./with_nix_shell.el
;; Hello World!
正如 文档 所述,启用词法模式其实就是让第一行被解释为形如 ;;; ... -*- lexical-binding: t -*- 的一行。
但当两者混用时,词法绑定似乎不再被启用:
#!/usr/bin/env nix-shell
#! nix-shell -i "emacs -f main -Q --script" --pure
#! nix-shell -p emacs
#! nix-shell -I nixpkgs=https://github.com/NixOS/nixpkgs/archive/a0374025a863d007d98e3297f6aa46cc3141c2f0.tar.gz
;;; -*- lexical-binding: t -*-
(defun main ()
(named-let adder-rec ((msg "Hello World!"))
(message "Hello world!")))
;; $ ./with_both.el
;;
;; Error: error ("Eager macro-expansion failure: (error \"`named-let' requires lexical binding\")")
;; error("Eager macro-expansion failure: %S" (error "`named-let' requires lexical binding"))
;; internal-macroexpand-for-load((defalias 'main #'(lambda nil (named-let adder-rec ((msg "Hello World!")) (message "Hello world!")))) t)
;; #<subr F616e6f6e796d6f75732d6c616d626461_anonymous_lambda_101>(#<buffer *load*> "/tmp/with_both.el")
;; load-with-code-conversion("/tmp/with_both.el" "/tmp/with_both.el" nil t #<subr F616e6f6e796d6f75732d6c616d626461_anonymous_lambda_101>)
;; command-line--load-script("/tmp/with_both.el")
;; command-line-1(("-scriptload" "./with_both.el" "-f" "main"))
;; command-line()
;; normal-top-level()
;; Eager macro-expansion failure: (error "`named-let' requires lexical binding")
为什么会这样?我该如何解决?
解决方案
我从另一份文档页面找到了答案,nix-shell文档中“Use as a #!-interpreter”小节:
除了第一行之外,额外的#! nix-shell行不需要出现在文件的开头。这使得在不把它们放在文件开头的语言中,可以把它们包裹在块注释中,例如ECMAScript、Erlang、PHP或 Ruby。
原来从Emacs的角度看,启用词法绑定的那一行并不在第2 行。
所以我只需要把启用词法绑定的那一行直接挪到“真实”的shebang之后(第2 行)。其余的 #! 行并不是某种shebang的扩展,而是nix-shell特有的行。至少在某种程度上,Emacs似乎会忽略它们,尽管它的实际工作方式对我来说仍然相当模糊。
这是修正后的代码:
#!/usr/bin/env nix-shell
;;; -*- lexical-binding: t -*-
#! nix-shell -i "emacs -f main -Q --script" --pure
#! nix-shell -p emacs
#! nix-shell -I nixpkgs=https://github.com/NixOS/nixpkgs/archive/a0374025a863d007d98e3297f6aa46cc3141c2f0.tar.gz
#! nix-shell --quiet
(defun main ()
(named-let adder-rec ((msg "Hello World!"))
(message "Hello world!")))
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。