将 Clojure 应用程序作为 jar 运行时,是否应该默认启用断言?
我们使用 leiningen 构建一个 uberjar 然后java -jar ...运行它,我刚刚发现这*assert*是真的。
我找不到这个动态变量应该保持什么默认值,但我认为它是假的(并且只在 REPL 开发环境中设置为真)。
这导致了一个令人讨厌的问题,future其中前提条件断言错误导致未来线程死亡,我们不知道发生了什么(因为我们只捕获了Exceptionnot Throwable)。
我浏览了的源代码,clojure.lang.RT但我无法弄清楚*assert*var在哪里设置为 true。
我还发现选项:global-vars中leinigen示例项目(这是在提到如何禁用Clojure的断言,包括先决条件?)。这看起来很合理,但我认为对于“生产”构建,我会*assert* false自动获得。
编辑:我错过了T用于*assert*在RT.java. 但是,仍然很好奇为什么这是默认设置以及关闭断言的推荐方法是什么(如果甚至推荐的话)。
从源代码来看,clojure.core/assert是一个*assert*在编译时计算的宏:
(defmacro assert
"Evaluates expr and throws an exception if it does not evaluate to
logical true."
{:added "1.0"}
([x]
(when *assert*
`(when-not ~x
(throw (new AssertionError (str "Assert failed: " (pr-str '~x)))))))
([x message]
(when *assert*
`(when-not ~x
(throw (new AssertionError (str "Assert failed: " ~message "\n" (pr-str '~x))))))))
Run Code Online (Sandbox Code Playgroud)
这意味着alter-var-root和binding都不能改变 的行为assert,因为它们在运行时运行,在assert已经评估之后*assert*。因此,中的:global-vars选项project.clj:
:global-vars {*warn-on-reflection* false
*assert* false }
Run Code Online (Sandbox Code Playgroud)
是唯一可行的解决方案,因为它在宏扩展之前进行评估。使用此测试代码:
(dotest
(spyxx *assert*)
(assert false "Don't do that!")
(println "past the assertion"))
Run Code Online (Sandbox Code Playgroud)
我们得到结果:
----------------------------------
Clojure 1.9.0 Java 10.0.1
----------------------------------
lein test tst.demo.core
*assert* => <#java.lang.Boolean false>
past the assertion
Ran 2 tests containing 0 assertions.
0 failures, 0 errors.
lein test 32.28s user 0.56s system 349% cpu 9.387 total
Run Code Online (Sandbox Code Playgroud)