我绝非编译器专家,因此不知道“提升”这个名字是否适用于这种情况。不知道你想要找什么术语,谷歌就变得相当困难。
我正在编写一个纯ClojureScript实现来进行DOM处理。它有点像Svelte和React的结合。其中,我有一个宏,它将常见的Hiccup形式重新写为创建一个“创建”函数和一个“更新”函数。为了使此宏高效工作,我想能够创建ns级别的“变量”。
下面是一个示例
(defn card [{:keys [title body]}]
(<> [:div.card
[:div.card-title title]
[:div.card-body body]
[:div.card-footer
[:div.card-actions
[:button "ok"]
[:button "cancel"]]]]))
所以,<>宏(称为“片段”)将其重新写为两个函数。一个用于创建初始DOM,另一个用于更新实际可以更改的部分。这样做是为了尽量减少“diffing”(差异比较)。
关于这个话题就不再过多展开。基本上,它会生成以下内容
(defn card [{:keys [title body]}]
(fragment-create "dummy-id" [title body]
(fn create-fn [state] ...)
(fn update-fn [state] ...)))
问题在于,每次调用card
函数时,它都会重新创建函数。由于函数在JS中没有“身份”,运行时无法判断这是否还是之前相同的identical?
片段,或者是否已被更新(通过REPL或热重载)。运行时必须依靠一些唯一的标识符。即使在运行时可能决定重新使用之前的片段并将其立即丢弃,JS运行时也总是在每次调用时分配函数。
所以,我希望生成的东西更接近以下从原始代码的结果
(def fragment-123
(fragment-create
(fn create-fn [state] ...)
(fn update-fn [state] ...)))
(defn card [{:keys [title body]}]
(fragment-use fragment-123 [title body]))
因此,"handler"片段只创建一次(目前使用deftype,也可以只是一个映射)。然后,每次调用card
函数时都会使用它。
在没有"提升"支持的情况下,代码只能以内联形式生成def
。
(defn card [{:keys [title body]}]
(do (def fragment-123
(fragment-create
(fn create-fn [state] ...)
(fn update-fn [state] ...)))
(fragment-use fragment-123 [title body])))
这显然是问题。因此,它需要检查片段是否已经定义,等等。目前使用reify
来完成这项工作,代码看起来真的很不悦。
我在shadow-cljs中将此作为实验实现,需要使用此功能的宏可以调用一个特殊函数(目前是来自&env
的,也可以是一个绑定),它基本上允许它们在没有工作时将某个形式“添加”到它目前正在处理的顶部形式。
编译器是否应该原生支持这一点?
我知道Clojure也没有它,但它通过创建类并在需要时访问它们来进行,因此某种程度上是在这样做。
实施的方案可能有所不同。我只是试图确定自己是否疯了,竟然想最初做这件事。之前的方法运行良好,只是代码量增加了,因为不能依赖于 identical?
,所以增加了一些额外的检查。