请在2024 年 Clojure 发展调查中分享您的想法!

欢迎使用!有关如何使用本站的信息,请参阅关于页面。

0
Clojure
此票包括两个补丁

# 在clojure.lang.RT加载时根据系统属性clojure.debug设置*assert*,
# 在assert中扩展错误消息以包括{{local-bindings}}(一个包裹隐式&env的新宏)

在批准这些补丁之前需要考虑的事项

# 是否应该有一个简单的Clojure级查询以确定是否启用了调试?(检查assert不是同一个,因为调试最终将驱动其他功能)
# assertion现在默认是关闭的 - 这是一个变化!
# 将{{local-bindings}}名添加到clojure.core酷吗?

15 个答案

0

评论由:stu发表

忽略旧补丁。请考虑以下实现,然后请将票据移至stu等待状态

  1. RT将检查系统属性{?"clojure.debug"},默认为false
  2. 属性将为当前根绑定设置一个标志。(调试构建将驱动比仅仅assert其他事情)
  3. 是否需要在Compile.java中将}或}作为线程局部绑定推入,或者它们只能在编译clojure时根绑定?
  4. 将添加*}绑定到{{clojure.main/with-bindings}}。其他地方需要吗?
  5. build.xml 不应需要更改 -- 系统属性将流经(而且 build.xml 可能很快就不会存在了)
  6. 一旦我们达成一致,我将ping Maven插件和Lein所有者,以便他们通过设置流经
  7. 更佳的断言消息将是一个单独的工单
  8. **debug* 和 **unchecked-math 之间的交互是什么?将检查更改为 ?
0

评论者:richhickey

#3 - 只有根绑定
#4 - 不应该在 with-bindings 中,原因与 #3 相同 - 我们不想让人们设置 **debug** 或 **assert**
#8 - 是的,在这个函数中使用辅助函数包裹它

#6 - 我最大的保留意见是这还没有根据 Maven 最佳实践进行确定

0

评论者:stuart.sierra

系统属性可以通过Maven传递,所以我预计这不会成问题。

然而,我宁愿默认情况下 * } 保持不变。

0

评论者:cemerick

SS关于这种方法不会对Maven造成任何问题。此外,构建可以轻松设置以始终生成两个jar,一个是"普通"的,另一个是"调试"的。

我建议,虽然 {{clojure.debug}} 可能会有广泛的影响,但应该提供额外的属性,以便对将来可能出现的额外"调试"相关的参数化进行细粒度的控制。


我想提出一些可能不太相关的担忧(在考虑了上述上下文中的断言后),其中一些或所有可能是我对某些领域缺乏理解的结果。

从 {{core.clj}} 中查看 {{assert}} 的使用(据我所知只有两个地方:验证 {{derive}} 的参数和检查 {{fn}} 的前件和后件条件),似乎不智地将它们设置为 {{false}}。即非-{{Named}} 值将能够进入层次中,并且前件和后件条件将被简单地忽略。

我的理解是,断言(在此指的是JVM构造,Clojure从中重用 {{AssertionError}})不应用于验证公共API函数的参数,或用于验证函数正常操作方面的任何方面(即(链接:http://download.oracle.com/javase/1.4.2/docs/guide/lang/assert.html#usage 文本:"在哪里不使用断言"))。这意味着当需要时, {{derive}} 应该抛出 {{IllegalArgumentException}},并且fn前件和后件条件可能应该抛出 {{IllegalStateException}} - 或者,无论如何,通过 {{assert}} 抛出其他不是 {{AssertionError}} 的事情。这将与大多数在核心中使用 {{assert-args}} 而不是 {{assert}} 的函数更好地匹配,前者抛出 {{IllegalArgumentException}} 而不是 {{AssertionError}}。

这使我产生了疑问:{{assert}}(和 * })是Clojure构造,还是伪互操作形式?

如果是前者,那么它的大致语义可以是我们想要的任何东西,但似乎不应该抛出{{AssertionError}}。

如果是后者,那么在JVM上出现{{AssertionError}}是合适的,但是我们需要注意,断言可以在运行时启用或禁用(而无需在不同版本的Clojure构建中切换),理想情况下使用宿主定义的开关(例如{{-ea}}及其朋友),并且可能不应该是类似 * 这样的东西。我不知道现在这是否可能或实用(我猜想这需要非平凡的编译器更改)。


希望上述内容到这个时候还没有过时。提前感谢您的耐心。 :-)

0

评论者:richhickey

感谢Chas的有用意见。目前还没有得出结论。我认为我们应该退后一步,看看这里的目标,然后再着手解决问题。作为一个动态语言,有很多我们可能想要验证关于我们程序的事情,其中验证的成本是我们不愿意在生产环境中支付的。

作为一个宏,assert有一个很好的特性,即在编译时,如果**assert**不为真,它生成nil,根本没有条件测试。因此,现在它更像是一种静态条件编译。

Java的assert确实有通过-ea等开关的运行时切换能力,如您所说。我尚未查看Java断言生成的字节码,但如果运行时开销可以忽略不计,Clojure的assert可能做类似的事情。很可能HotSpot为检查某个标志而跳过断言字节码有特殊代码。我只是不确定这个标志是(链接: http://download.oracle.com/javase/1.5.0/docs/api/java/lang/Class.html#desiredAssertionStatus() 文本:Class.desiredAssertionStatus)。

这变成assert或预/后条件,最佳实践等的变化是正交且派生的。目前我们没有在没有检查的情况下运行的功能。我们需要在编译期间(调试构建)或运行时(跟踪 -ea)或两者都消失它们之间做出选择。然后我们可以看看这在assert/pre-post中的反映,并重新审查现有的两者使用。关于“哪里不要使用断言”的文档对其进行了分类处理,但在我看来,没有考虑到它们的成本似乎是不切实际的。

如果有人能查看Java自身如何生成和优化assert,我将非常感激。

0
_由cemerick发表的评论_

不幸的是,字节码问题超出了我的薪酬范围……

以下是一些额外的想法,供您参考,也许您已经考虑过了。

{{assert}}是一个宏,它所执行的功能完全符合其定义。问题是,在Clojure中,“编译时”是一个复杂的概念:存在代码加载时间、AOT编译时间和递归AOT编译时间。因此,在生产环境中部署的应用程序可能包含各种库和命名空间中{{assert}}的使用而产生的零散代码或无代码,具体取决于这些库和命名空间何时加载、AOT编译或它们的依赖项何时AOT编译,以及每个时间点的{{\*assert\*}}的值。当然,这种情况也适用于所有这种依赖于上下文相关状体结果的结果宏(我想这是{{clojure.contrib.logging}}的一个大问题,这使得它只能在一段时间内与log4j一起使用)。

JVM断言机制真正吸引人的是它可以根据特定运行时对每个包进行参数化,如果需要的话。将这个概念重构成使{{assert}}可以根据{{\*ns\*}}敏感似乎很简单,但前面提到的编译时间复杂性仍然存在,拥有两个独立控制的断言设施听起来并不有趣。

我几乎不知道CLR,但它似乎没有提供类似于运行时可控制的断言。
0

评论由:stu发表

我所能找到的最好(有些过时)的证据表明,编译器根据{{desiredAssertionStatus}}的返回值设置一个特殊的类静态final字段{{$assertionsDisabled}}。HotSpot对此没有任何特别操作,死代码消除只是简单地让它消失。代码确实是这样的编译的

11: getstatic #6; //字段 $assertionsDisabled:Z
14: ifne 33
17: lload_1
18: lconst_0
19: lcmp
20: ifeq 33
23: new #7; //类 java/lang/AssertionError
26: dup
27: ldc #8; //字符串 X应该为零
29: invokespecial #9; //方法 java/lang/AssertionError."":(Ljava/lang/Object;)V
32: athrow

即使我们100%确信断言删除是完全的,我还是会为以下原因支持单独的Clojure级别开关:

  1. 我确实需要禁用一些断言,而且我完全不使用Java互操作。其他人可能也会这样认为。
  2. 随着时间的推移,将会出现多个调试工具,并且对于Clojure用户来说,有一个顶层调试开关是方便的。
  3. 通过命令行标志实现Java的启用/禁用仍然是一个附加功能。我们可以稍后将其作为一个(较小的)破坏性更改添加到我们的断言中,或者将有一个单独的java-assert互操作形式。我就这个问题拿不定主意。
  4. 我相信从一个非Java断言形式中抛出{{AssertionError}}是完全合适的。我们不信仰静态异常层次结构的世界,生产中的断言失败是无论你如何称呼它都是关键的失败。即使是Scala也会这样做:) http://daily-scala.blogspot.com/2010/03/assert-require-assume.html

Rich: 等待你同意,继续进行这项工作。

0

评论者:richhickey

编译器在静态初始化代码中设置$assertionsDisabled时?类加载器有特殊的支持吗?你有找到的最佳日期证据的链接吗?

0

评论由:stu发表

  1. 是的,在静态初始化代码中
  2. 根据Brian Goetz(上周私人通讯)所说,类加载器中没有特殊支持。但代码删除很好:"禁用断言的运行时成本应确实为零,对于编译代码而言"。
0

评论由:stu发表

链接:谷歌“java assert shirazi”。(不发布链接,因为我无法在10秒内判断它是否包含我的会话信息。)

0

评论者:akiel

关于这个问题有什么新的进展吗?我还想找一个方便的方法在生产中禁用断言。

0

评论者:lopuz

我对这个问题也有兴趣。
在运行时启用/禁用断言的方便方式(最好是通过 -ea/-da 选项)将是一个非常棒的功能!

0

评论者:lopuz

顺便说一下,有通过clojars提供的可用于运行时切换断言的库
https://github.com/pjstadig/assertions
这对我有很大帮助。

0
...