分享您的想法,请参加2024年Clojure现状调查!

欢迎!请查看关于页面,了解更多关于这个平台的信息。

0
Clojure
此工单包含两个补丁

#  一个补丁,当clojure.lang.RT加载时设置&nbsoptionexpressed*assert*,基于系统属性clojure.debug的存在
# 在assert中扩展错误消息以包括{{local-bindings代码gt; (一个封装了隐式&代码>&envatement新宏)

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

# 是否应该有一个简单的Clojure级方式来查询是否启用了调试?(检查assert并不相同,因为最终调试应该驱动其他功能)
# 默认情况下断言现在将关闭 - 这是一个变化!
# 在clojure.core中添加{{local-bindings}}这个名字酷吗?

15 答案

0

评论者:stu

忽略旧补丁。请考虑以下实现,审查后,将该工单移至stu等待

  1. RT将检查系统属性{#"clojure.debug"},默认为false
  2. 属性将为当前设置根绑定,并添加一个新标志。 (调试构建不仅可以和断言驱动,而且可以驱动其他功能。)
  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 传输,因此我预计这不会成为问题。

然而,我宁愿 * } 保持默认的 True 状态。

0

评论者:cemerick

SS关于这种方法对 Maven 没有问题的观点是正确的。此外,构建可以轻松设置,总是发出两个 jar 文件,一个是“正常”的,一个是“调试”的。

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


我想提出几个可能的相关担忧(考虑到上面的背景下对断言的思考),其中一些或全部可能仅仅是我对各个领域的缺乏理解的结果。

查看 {{assert}} 在 {{core.clj}} 中的使用情况(据我所知仅有两个地方:验证 {{derive}} 的参数和检查 {{fn}} 中的预和后条件),看起来使它 {{false}} 是不明智的。即,非 {{Named}} 的值将能够进入层次结构,并且预和后条件将简单地被忽略。

我了解到,断言(这里指的是 JVM 构造,Clojure 从中重新使用 {{AssertionError}}),不应该用来验证公共 API 函数的参数,或者用来验证函数的正常操作任何方面(即(链接:http://download.oracle.com/javase/1.4.2/docs/guide/lang/assert.html#usage 文本:“不应该使用断言的地方”))。这暗示了当必要时,{{derive}} 应该抛出 {{IllegalArugmentException}},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()](http://download.oracle.com/javase/1.5.0/docs/api/java/lang/Class.html#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用户来说,设置一个顶级的debug切换很方便。
  3. Java通过命令行标志进行禁/启用仍然是一种可能的方法。我们可以稍后将其作为一个(小的)破坏性更改添加到我们的assert,或者有一个单独的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
...