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

欢迎!请查看关于页面,了解更多关于如何使用本站的信息。

0票数
Clojure
此工单包含两个补丁

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

在批准这些补丁之前需要注意的事项

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

15 答案

0票数

评论者:stu

忽略旧补丁。请审查以下实现,然后移动工单以等待Stu

  1. RT将检查系统属性{"clojure.debug"},默认为false
  2. 属性将设置当前的根绑定,并添加一个新的标记。(调试构建将驱动比断言更多的事物。)
  3. Compile.java是否需要推送{}或}作为线程局部绑定,或者它们只能在编译clojure时作为根绑定?
  4. 将在c-grow bind中添加 * } 绑定{{clojure.main/with-bindings}}。还有其他地方需要吗?
  5. build.xml不必改变--系统属性将流动通过(并且build.xml可能在不久的将来不再存在。)
  6. 一旦我们达成一致,我将ping Maven插件和Lein所有者,以便他们通过设置进行流程管理。
  7. 更好的断言信息将是另一个条目。
  8. **debug**和**unchecked-math**之间的交互是什么?将检查更改为 }}?
0票数

评论者为:richhickey

#3 - 只有根绑定
#4 - 因为本与#3相同的原因,**debug**和**assert**不应该在with-bindings中使用!我们不想让用户设置!
#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函数的参数,或用于验证函数正常操作的任何方面(即(link: 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** 不可用,它会生成 nil,没有条件测试。因此,目前它更像是静态条件编译。

Java 断言确实可以通过 -ea 来进行运行时切换,如你所言。我没有查看 Java 断言生成的字节码,但 Clojure 断言可能做得类似,如果运行时开销可以忽略不计。HotSpot 很可能有特殊的代码来跳过一些标签的单个检查下的断言字节码。我只是不太确定这个标签是否是(链接:http://download.oracle.com/javase/1.5.0/docs/api/java/lang/Class.html#desiredAssertionStatus() 文本:Class.desiredAssertionStatus)。

无论这是否在断言或前/后条件、最佳实践等方面引起变化都是正交和派生的。当前我们并没有提供一个在没有检查的情况下运行的功能。我们需要在编译时(调试构建)或运行时(跟踪 -ea)或两者之间做出选择。然后我们可以看看这如何在断言/前/后条件中得到体现,并重新审视两者的现有使用。关于“哪里不要使用断言”的文档对该问题进行了分类处理,但考虑到成本,在我看来似乎并不现实。

如果有人能调查 Java 本身是如何生成和优化断言的,我将不胜感激。

0票数
_由 cemerick 发表的评论_

不幸的是,字节码问题仍然超出了我的专业范围...

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

{{assert}}是一个宏,对于它所做的事情来说,完全是有意义的。问题是,“编译时”在Clojure中是一个棘手的概念:这里有代码加载时间、AOT编译时间和递归AOT编译时间。鉴于这一点,完全可能存在一个部署到生产环境中的应用程序,其中的代码是修补过的,包含了各种库和命名空间中的{{assert}}使用情况,这取决于那些库和命名空间何时加载、AOT编译或它们的依赖项何时AOT编译,以及在此期间每个时间点的{{\*assert\*}}值。当然,所有此类宏的结果都依赖于上下文相关状态的情况也是如此(我认为这曾是{{clojure.contrib.logging}}的一个大问题,使其在一段时间内只能与log4j一起使用)。

JVM断言机制真正吸引人的是,如果需要,它可以针对特定运行时的每个包进行参数化。将这个概念重新实现,以便{{assert}}可以对{{\*ns\*}}敏感,看起来很简单,但前面提到的编译时复杂性问题仍然存在,有两个独立控制的断言设施的想法并不令人愉快。

我几乎对CLR知之甚少,但它似乎不提供任何类似运行时控制的断言。
0票数

评论者:stu

我所能找到的最好(有些过时)的证据表明,编译器根据{{desiredAssertionStatus}}的返回值设置一个特殊的类静态最终字段{{$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通过命令行标志启用/禁用仍然是一个可能的功能。我们可以在稍后将其作为对assert的(小型)破坏性更改添加,或者有一个独立的java-assert互操作形式。我在这一点上有些举棋不定。
  4. 我认为从一个非Java-assertion-form抛出{{AssertionError}}是完全可行的。我们不相信存在静态异常层次的世界,并且在生产环境中,无论你称其为断言、require还是assume,都是关键的失败。即使是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票数

评论者:lopusz

我也想知道这个问题的最新动态。
运行时启用/禁用断言的方便方法(最好通过 -ea/-da 选项)将是一个很酷的功能!

0票数

评论者:lopusz

顺便说一句,有个库支持运行时切换断言,可以通过clojars访问:
https://github.com/pjstadig/assertions
这个帮助了我很多。

0票数
...