这些语言又不会像 Emacs Lisp 那样分别有 S-exp interpreter 和 bytecode interpreter 各自为政(比如闭包变量的优化问题),大部分有 S-exp interpreter 的都是 bootstrap 用一下就丢了。或者说根本问题是 Emacs Lisp 编译后和编译前的语义不完全相同。且 cl-eval-when
加剧了这种撕裂,但并没有带来实际的好处。
byte compile 和 eval-when 没关系。
假设有一个 CL 编译器完全没有编译,所谓的编译就是做 macroexpand 到最底层的 primitive(这样的 CL 实现是符合标准的,其实也是 SICL 的早期实现)
在这个实现完成后,把 eval 改成编译 primitive sexp compiler 的,只要这个编译器实现正确,对这个实现的行为是不影响的。
macro 就是 macro,和 eval 是否采用编译器是无关的。
progn 在 CL 里面是宏定义里也能生效的,emacs 的实现的确和 CL 不同,实际上不是 eval 和 compiler 分开的问题,而是 emacs 没把宏展开和 bytecomp 正确分开的问题,很多 special form 依赖于 bytecomp 的行为。
所以 Emacs 就是这个问题 无解
直接的作用是阻止 require 在编译时 eval。默认行为 require 是 (eval compile load) 的。
至于为啥要阻止编译时加在这个包,有没有更好办法,我就不知道了。
感谢两个小伙伴的耐心回复和热烈讨论。 不一致的根源在于忽略了源码的第一个判断条件
(if (and (macroexp-compiling-p)
(not cl--not-toplevel) (not (boundp 'for-effect))) ;Horrible kludge.
从源码层面看eval和compile, load是两条分支线下的结果。如果第一个条件不满足,会直接返回(cons 'progn body)
。
需要把下文
- 顶层形式
-
(cl-eval-when (compile) body ...)
在编译阶段,body
在顶层形式编译时求值; -
(cl-eval-when (load) body ...)
在加载阶段,body
在顶层形式编译后求值; -
(cl-eval-when (eval) body ...)
等同于progn
。
中的第4条修正为(cl-eval-when (eval) body ...)
在解释阶段等同于progn
。
重新梳理一下:
- 顶层形式
-
(cl-eval-when (compile) body ...)
在编译阶段,body
在顶层形式编译时求值; -
(cl-eval-when (load) body ...)
在加载阶段,body
在顶层形式编译后求值; -
(cl-eval-when (eval) body ...)
在解释阶段等同于progn
; -
(cl-eval-when (load eval) body ...)
在任意上下文中都等同于progn
。
由上面5条引出的新问题是
-
progn
应该如何理解?其作为顶层形式具有什么作用? - 在关于magit的源码实验中,是否保留
(cl-eval-when (load eval) body...)
对速度无影响,即是否阻止require在编译时eval没有意义,那么这句源码存在的意义是什么?
用来给 macro 返回多个表达式用。
有意义,可以减少编译用时。
感谢您的耐心解答哈。
From: Jonas Bernoulli <[email protected]>
Subject: Re: cl-eval-when -- A workaround for recursive require?
To: Stefan Monnier <[email protected]>, Zhu Zihao <[email protected]>
Cc: [email protected]
Date: Sun, 01 May 2022 20:36:15 +0200 (5 minutes, 39 seconds ago)
Flags: seen
Maildir: /all_but_last/Inbox
Stefan Monnier <[email protected]> writes:
> Zhu Zihao [2022-04-27 18:52:07] wrote:
>> A breif summary: magit.el use a `cl-eval-when` block with load time and
>> eval time only evaluation to require its sub-components, while each
>> sub-component use `(require 'magit)` to use procedure in different
>> sub-components. This hack seems to be a hack to avoid recursive require.
>
> This kind of setup is quite common, but the resulting cyclic
> dependencies tend to be a nightmare.
Yeah, I got a bit lazy there, but I am working on it now.
Jonas
我觉得可以终结本帖了
2 个赞