2024年Clojure状态调查!中分享您的看法。

欢迎!请查阅关于页面以了解更多有关如何使用本站的信息。

+1
ClojureScript
命名空间在运行时加载的顺序与用户代码中命名空间被requird的顺序无关。这似乎会影响所有优化级别。这意味着开发人员不能依赖于具有副作用代码的给定命名空间在依赖于该副作用的另一个命名空间加载之前执行。

重现步骤

运行以下命令:


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)的一部分完成的,但似乎在{{cljs_deps.js}}发出之前,顺序可能会出错。

看起来对于给定的ClojureScript文件,{{cljs_deps.js}}的输出最终由{{cljs.compiler/emit-source}}的输出决定。那里的代码以与CLJS-1453之前在{{cljs.analyzer}}中的'ns解析方法相同的方式删除重复的依赖项。

附带的补丁为我狭窄的用例解决了问题,但我认为基本函数正在作为映射传递`:uses`和`:requires`,因此可能需要对完整的解决方案进行更多工作。

11 答案

0
回答者:

评论者:chancerussell

为我用markdown使工单变乱而道歉——我本以为在提交后会清理一下。

0
回答者:

评论者:mfikes

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

0
回答者:

评论者:thheller

附件中的补丁仍然使用{{deps}}映射,但这只会保持插入顺序,直到遇到数组映射阈值,并且一旦开始使用散列映射,顺序将不再保证。在shadow-cljs中,我通过维护一个附加的{{:deps}}向量以及常用的{{:requires}}映射来处理这个问题。

0
回答者:
_评论者:chancerussell_

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

我今晚将查看这个问题——看起来CLJS-1453的补丁也许有相同的问题。

话虽如此,我 Wonder 在一个ns形式同时包含:require和:use时,排序的语义应该是什么?
0
回答者:

评论者:thheller

看起来analyzer解析的'ns’和'ns**’已经维护了{{:deps}}向量子序,我们只需要在cljs.compiler/emit-source函数中使用它,而不是使用requires/uses映射的值。

0
回答者:

评论者:chancerussell

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

有一个问题——当两个都存在时,{{cljs.core}}和{{cljs.core.constants}}的顺序应该是什么?显然,前一个代码中没有保证顺序,但我希望这次弄对。

(测试在test-cljs-1882-constants-table-is-sorted中看起来表明应该先写cljs.core。)

0

评论者:mfikes

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

0

评论者:dnolen

{{cljs.core}}必须排在{{cljs.core.constants}}之前,因为没有定义关键字结构,cljs.core.contants将无法工作。如果不是这句话被保证,我们可能会听到很多关于高级构建失败的抱怨。

0

评论者:chancerussell

David,当你说“顺序被保证”的时候,是指{{cljs.core}}和{{cljs.core.constants}},还是整个依赖关系?当前的代码是用conj添加到集合中,这并不保证任何顺序,据我所知。

0

评论者:mfikes

呃,我弄错了:CLJS-3056.patch在Windows CI中失败(x)

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

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