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

欢迎!请在 关于 页面查看有关如何使用本页的更多信息。

0 浏览
Clojure
该工单包含两个补丁

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

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

是否应该有一个在 Clojure 级别查询 debug 是否启用的方法?(检查 assert 并不相同,因为 debug 最终将驱动其他功能)
断言现在默认关闭 - 这是一个变化!
{{local-bindings}} 的添加到 clojure.core 是否合适?

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. 一旦我们达成一致的方法,我将联系 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}}在必要时应该抛出{{IllegalArgumentException}},而函数的前置和后置条件可能应该抛出{{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; //Field $assertionsDisabled:Z
14: ifne 33
17: lload_1
18: lconst_0
19: lcmp
20: ifeq 33
23: new #7; //class java/lang/AssertionError
26: dup
27: ldc #8; //String X should be zero
29: invokespecial #9; //Method java/lang/AssertionError."":(Ljava/lang/Object;)V
32: athrow

即使我们100%确信断言移除是完全的,我仍然会为以下原因投票支持一个Clojure级别的独立开关

  1. 我确实需要禁用一些断言,而且我根本不需要Java互操作。可争议的是,其他人也会身处同境。
  2. 随着时间的推移,将会出现多个调试工具,并且对于Clojure用户来说,拥有一个顶层调试开关是非常便利的。
  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

链接:在 Google 中搜索 "java assert shirazi"。 (不提供链接,因为我不能在 10 秒内判断是否包含我的会话信息。)

0 浏览
作者

评论者:akiel

这个问题的最新进展是什么?我还想找出一种在生产中禁用断言的便捷方式。

0 浏览
作者

评论者:lopusz

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

0 浏览
作者

评论者:lopusz

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

0 浏览
作者
参考:[https://clojure.atlassian.net/browse/CLJ-250](https://clojure.atlassian.net/browse/CLJ-250)(由 stu 报告)
...