2024 Clojure调查问卷中分享您的想法!

欢迎!请查看关于页面,了解这一功能的一些更多信息。

0
Clojure
该工单包含两个补丁

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

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

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

15 个答案

0

评论者:stu

忽略旧补丁。请考虑以下实现,然后移动工单到stu状态

  1. RT将检查系统属性{{"clojure.debug"}}
  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 - 按照与 #3 相同的原因,不应该在 not 中包含 with-bindings -我们不希望人们设置 **debug** 或 **assert**
#8 - 是的,用辅助函数包装它

#6 - 我最大的保留意见是,这还没有遵循 Maven 的最佳实践

0

评论者:stuart.sierra

系统属性可以通过 Maven 传递,因此我不认为这会是个问题。

然而,我更希望 * } 默认为 true。

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 text: "where not to use assertions"))。这意味着当需要时,{{derive}} 应该抛出 {{IllegalArugmentException}},fn 的前和后条件可能应该抛出 {{IllegalStateException}} -- 或者,无论如何,通过 {{assert}} 抛出其他非 {{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或pre/post条件,最佳实践等是正交且派生的。目前我们还没有在没有检查的情况下运行的能力。我们需要在以下两者之间做出选择:在编译时(调试构建)或运行时(track -ea)消除它们,或者同时消除。然后我们可以看看这如何在assert/pre-post中反映出来,并重新审视已有的使用案例。“哪些地方不要使用断言”文档对此进行了分类处理,但在不考虑成本的情况下,这似乎是不现实的,我认为。

如果有人能研究一下Java自身如何生成和优化断言,我将非常感激。

0
评论者:cemerick

很遗憾,字节码问题超出了我的能力。

以下是一些额外的想法,你可能已经在考虑了

{{assert}}作为一个宏,对于它所做的事情是非常合适的。问题是,“编译时”在Clojure中是一个棘手的概念:有代码加载时间、AOT-编译时间和递归AOT-编译时间。考虑到这一点,完全有可能将包含各种库和命名空间的使用过{{assert}}的代码片段或根本没有由{{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用户来说,有一个顶层的debug开关是方便的。
  3. 可以通过命令行标志启用或禁用Java,还可以作为单独的特性进行。我们可以在后面将其作为(小型的)破坏性更改添加到我们的assert中,或者有一个单独的java-assert交互形式。我在这个决定上犹豫不决。
  4. 我认为从非Java-assertion-form抛出一个{{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

评论者:lopuz

我对这个问题的任何新闻都感兴趣。
方便地在运行时启用/禁用断言(最好通过 -ea/-da 选项)将是一个非常棒的特性!

0

评论者:lopuz

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

0
...