2024 Clojure状态调查中分享您的想法!

欢迎!请查看关于页面,了解更多关于其工作原理的信息。

+1
ClojureScript
命名空间在运行时加载的顺序与在用户代码中{{require}}的顺序无关。这似乎会影响所有优化级别。这意味着开发人员不能依靠某个命名空间中的副作用代码在依赖于该副作用的其他命名空间加载之前运行。

重现步骤

运行以下命令


clj --main cljs.main --compile-opts '{:target :nodejs :main a}' --compile a


将编译{{a.cljs}}中定义的{{a}}命名空间。该命名空间通过{{ns}}宏的{{:require}}形式按字母顺序需要命名空间{{b}}到{{g}}。

编译后观察以下内容

* 输出{{out/cljs_deps.js}}文件中{{a.js}}的goog.addDependency调用包括其依赖项的任意顺序。例如,在我的机器上的一次运行结果为


goog.addDependency("../a.js", ['a'], ['cljs.core', 'e', 'c', 'g', 'b', 'd', 'f']);


* 运行编译后的代码的{{node out/main.js}}给出的控制台输出表明,运行时的加载顺序与{{out/cljs_deps.js}}中反映的顺序相同


e的主体

c的主体

g的主体

b的主体

d的主体

f的主体


分析

看起来确保编译时顺序的工作已作为[CLJS-1453](https://dev.clojure.org/jira/browse/CLJS-1453)的一部分完成,但似乎在 CJLS-1453 之前,镀铬分析器中的'ns 解析方法之前,相同的依赖项会被消重。

似乎由特定ClojureScript文件产生的{{ cljs_deps.js }}的输出最终由{{ cljs.compiler/emit-source }}的输出决定。那里的代码以类似于CLJS-1453之前在{{ cljs.analyzer }}中的'ns 解析方法的方式去除重复的依赖项。

附加的补丁解决了我的特定用例的问题,但我相信底层函数是将`:uses`和`:requires`作为映射传递的,因此可能需要进行更多工作以实现完整的解决方案。

11 回答

0

评论由:chancerussell

为打扰票单而道歉——以为提交后能清理掉。

0

评论由:mfikes

CLJS-3056.patch通过CI和Canary测试(/)

0

评论由:thheller

附加的补丁仍然使用{{deps}}映射,该映射只能维护插入顺序,直到达到数组映射阈值,一旦开始使用哈希映射,顺序将不再保持。在shadow-cljs中,我通过除了常规的{{:requires}}映射外,还维护一个额外的{{:deps}}向量来处理此问题。

0
_评论由:chancerussell_

这可能是最合适的解决方案——我不想尝试修改现有代码中映射的类型。

我今晚将查看这个问题——看起来CLJS-1453的补丁可能也存在同样的问题。

话虽如此,我想知道当ns形式同时包含:require和:use时,排序的语义应该是什么?
0

评论由:thheller

啊,看起来在解析器中已维护了一个{{:deps}}向量,这是在'ns和'ns**中的。只需在cljs.compiler/emit-source函数中使用它,而不是取requires/uses映射的值。

0

评论由:chancerussell

托马斯是对的,{{:deps}}向量已经存在,并且处于预期的顺序,因此我已提交一个新的补丁,使用了该向量。

一个问题——当同时存在{{cljs.core}}和{{cljs.core.constants}}时,它们的顺序应该是怎样的?显然,前一种代码并没有保证顺序,但我想确保这一点。

(似乎test-cljs-1882-constants-table-is-sorted中的测试表明clfj.core应该排在前面。)

0

评论由:mfikes

CLJS-3056.patch 于 2019 年 3 月 24 日下午 12:58 通过 CI 和 Canary 测试 (/)

0

由 dnolen 发表的评论:

{{cljs.core}}必须在{{cljs.core.constants}}之前,因为如果不定义关键字结构,{{cljs.core constants}}将无法工作。否则我们肯定会听到很多关于构建失败的自豪,因为排序绝对是保证的。

0

评论由:chancerussell

大卫,当你说“排序绝对是保证的”,是指{{cljs.core}}和{{cljs.core.constants}}吗,还是指整个依赖?当前的代码是在向集合中{{conj}},这并不保证保留任何顺序,据我所知。

0

评论由:mfikes

哎呀,我错了:CLJS-3056.patch 导致 Windows CI 失败 (x)

特别是,它导致在{{cljs-2077-test-loader}}中发生堆栈溢出

ERROR in (cljs-2077-test-loader) (core.clj:6077) 397未捕获的异常,不在断言中。 398预期:nil 399实际:java.lang.StackOverflowError: null 400在 clojure.core$get_in.invokeStatic (core.clj:6077) 401 cljs.module_graph$deps_for.invokeStatic (module_graph.cljc:147) 402 cljs.module_graph$deps_for.invoke (module_graph.cljc:147) 403 clojure.lang.AFn.applyToHelper (AFn.java:160) 404 clojure.lang.AFn.applyTo (AFn.java:144) 405 clojure.core$apply.invokeStatic (core.clj:657) 406 clojure.core$memoize$fn__6665.doInvoke (core.clj:6288) 407 clojure.lang.RestFn.invoke (RestFn.java:436) 408 cljs.module_graph$deps_for$fn__7089.invoke (module_graph.cljc:151) 409 clojure.core$map$fn__5665.invoke (core.clj:2745) 410 clojure.lang.LazySeq.sval (LazySeq.java:40) 411 clojure.lang.LazySeq.seq (LazySeq.java:49) 412 clojure.lang.RT.seq (RT.java:528) 413 clojure.core$seq__5202.invokeStatic (core.clj:137) 414 clojure.core$apply.invokeStatic (core.clj:652) 415 clojure.core$mapcat.invokeStatic (core.clj:2775) 416 cljs.module_graph$deps_for.invokeStatic (module_graph.cljc:147) 417 cljs.module_graph$deps_for.invoke (module_graph.cljc:147) 418 clojure.lang.AFn.applyToHelper (AFn.java:160) 419 clojure.lang.AFn.applyTo (AFn.java:144) 420 clojure.core$apply.invokeStatic (core.clj:657)

CI 构建链接: https://ci.appveyor.com/project/mfikes/clojurescript/builds/24478525

0
参考: https://clojure.atlassian.net/browse/CLJS-3056(由 chancerussell 报告)
...