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

欢迎!请参阅关于页面了解有关该功能的一些更多信息。

0
Clojure
此工单包含两个补丁

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

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

# 是否应该有一个简单的 Clojure 级别的方法来查询是否启用了调试?(检查 assert 并不是同一回事,因为调试最终应该驱动其他功能)
# 默认情况下断言现在将是关闭的——这是一个变化!
# 将 {{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. 一旦我们达成一致,我将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**在编译期间不成立时,它会产生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/前置/后置条件中,并重新审视现有对它们的用法。“哪里不使用断言”这一文档对它们进行了分类处理,但在不考虑它们成本的情况下,在我看来这是不切实际的。

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

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; //class java/lang/AssertionError
26: dup
27: ldc #8; //字符串 X 应为零
29: invokespecial #9; //方法 java/lang/AssertionError."":(Ljava/lang/Object;)V
32: athrow

即使我们完全确信断言删除是彻底的,我还是会投票支持一个单独的Clojure级开关,以下是原因

  1. 我真正并且迫切需要禁用一些断言,我根本不需要Java互操作。或许别人也会处于同样的境地。
  2. 随着时间的推移,会出现多种调试工具,对于Clojure用户来说,有一个顶层调试开关是方便的。
  3. 通过命令行标志Java禁用/启用仍然可能作为一个单独的功能。我们可以在稍后将其作为对我们的assert的一个(小)破坏性更改添加,或者有一个单独的java-assert互操作形式。我在这个问题上处于左右为难的状态。
  4. 我认为从一个非Java-assertion-form抛出{{AssertionError}}是完全可以的。我们不相信一个静态异常层次结构的世界,并且断言在生产中是至关重要的失败,无论您怎么称呼它。Even Scala does it :-) 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

评论者:лопusz

我对这个问题的新消息也感兴趣。
在运行时启用/禁用断言的便捷方法(最好是使用 -ea/-da 选项)将是一个很好的特性!

0

评论者:лопusz

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

0
...