我绝不是编译器专家,所以我不知道“提升”这个名字是否真的合适。如果你不知道你正在查找的术语,那么在Google上查找是很难的。
我正在编写一个纯ClojureScript实现来处理DOM。有点像Svelte和React的结合。在这个实现中,我有一个宏,它可以重写常见的Hiccup形式来创建一个“create”函数和一个“update”函数。为了让这个宏有效地工作,我想要创建ns级别的“vars”。
这是一个可能的示例
(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
函数时,它都会重新创建函数。因为在JavaScript中函数没有“身份”,运行时无法区分这只是之前相同的identical?
片段,还是已经更新了(通过REPL或热重载)。它必须依赖某个独特的标识符来区分。JavaScript运行时也会在每次调用时都分配内存,尽管运行时可能决定重用先前的片段并立即丢弃它们。
因此,我希望生成的东西更接近以下从原始代码中得到的
(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,但也可以只是使用一个map)。然后,每当调用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
调用,可能是一个绑定),它基本上允许它们将一个形式“ prepend”到它们目前正在工作的顶级形式上。
这应该是编译器原生支持的功能吗?
我知道Clojure也没有这个功能,但是它通过创建类并在需要时访问它们来做这个事情,它好像也在这样做。
实现可能会有所不同。我只是想确定我是否一开始就想要这样做的想法是疯狂的。之前的实现工作正常,但最终产生了更多代码,其中包括许多额外的检查,因为不能依赖于 identical?
。