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']);


* 运行编译后的代码会产生的控制台输出与{{out/cljs_deps.js}}中反映的顺序相同


e的内容

c的内容

g的内容

b的内容

d的内容

f的内容


分析

看起来在编译时确保顺序的一些工作已经包含在[CLJS-1453](https://dev.clojure.org/jira/browse/CLJS-1453), 但在 {{cljs_deps.js}} 被生成之前,顺序可能已经混乱。

{{cljs_deps.js}}中给定ClojureScript文件的输出最终由{{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 的补丁似乎可能存在相同的问题。

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

评论者:thheller

看起来分析器解析 '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 投票
by

评论者:mfikes

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

0 投票
by

评论者:dnolen

{{cljs.core}}必须在{{cljs.core.constants}}之前,因为如果没有定义关键字构造函数,{{cljs.core.contants}}将不会工作。在其他情况下,我们绝对保证了顺序,否则我们一定会听到有关高级构建失败的很多投诉。

0 投票
by

评论者:chancerussell

大卫,当你说“顺序绝对是有保证的”,你是指只是{{cljs.core}}和{{cljs.core.constants}},还是整个依赖关系?当前的代码是用{{conj}}连接到集合中,据我所知,这并不保证任何顺序。

0 投票
by

评论者:mfikes

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

特别是,它导致了{{cljs-2077-test-loader}}中的栈溢出

ERROR在{{cljs-2077-test-loader}}(core.clj:6077)397未在断言中捕获的异常。398预期:nil399实际:java.lang.StackOverflowError:null400在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 报告)
...