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

欢迎!有关如何运作的更多信息,请参阅关于页面。

0
Clojure
此票据包含两个补丁

# 当clojure.lang.RT加载时,根据系统属性clojure.debug设置&nbsUnser  s능* &ptest,基于系统属性clojure.debug的存在
# 在assert中展开错误信息以便包括{{local-bindings}}{{/code>(一个新宏,它封装了隐式的{{&env}}))

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

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

15 答案

0

评论者:stu

忽略旧补丁。考虑以下实现方案,请审查后再将票据移至stu等待中

  1. RT将检查系统属性{{{{"clojure.debug"}}}}, 默认为false
  2. 属性将设置当前root绑定,并提供一个新{{标志。》(调试构建可以驱动除断言外的其他功能。)
  3. Compile.java是否需要将{{}}或{{}}推送到线程本地绑定,或者它们是否只能在编译clojure时被设置为root绑定?
  4. 将添加* {{}}绑定到 clojure.main/with-bindings>。还有其他任何地方?
  5. build.xml无需更改 —— 系统属性将通过(而且build.xml可能很快就不会再使用)。
  6. 一旦我们达成一致的方法,我将ping Maven插件和lein所有者,以便他们通过该设置流动
  7. 更好的断言消息将是一个单独的工单
  8. **debug*和**unchecked-math**之间的交互是什么?将检查更改为 }}?
0

评论由:richhickey

#3 - 仅限root绑定
#4 - 不应在with-bindings中出现,原因与#3相同 - 我们不想人们设置!**debug**或**assert**
#8 - 是的,把它包裹在一个辅助函数中

#6 - 我最大的疑虑是这还没有根据Maven最佳实践进行告知

0

评论由:stuart.sierra

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

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

0

评论由:cemerick

SS 关于此方法不会对Maven造成任何问题的说法是正确的。此外,构建可以很容易地设置为始终生成两个jar文件,一个是“正常”的,一个是“调试”的。

我建议,虽然{{clojure.debug}}可能具有广泛的影响,但也应提供额外的属性,以提供对将来可能出现的每个额外的“调试”相关参数化进行细粒度控制的手段。


我想提出一些可能有些不相关的问题(在那段关于断言的思考之后),其中一些或全部可能是由于我在各个领域的理解不足造成的。

从where {{assert}} is used in {{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可能能够做类似的事情,如果运行时的开销是可以忽略的。JetSpot很可能有一段特殊的代码来避免执行断言字节码,前提是检查了某个标志。我只是不确定这个标志是不是(链接:http://download.oracle.com/javase/1.5.0/docs/api/java/lang/Class.html#desiredAssertionStatus() 文本:Class.desiredAssertionStatus)。

这转变为断言或前置/后置条件、最佳实践等的变化是正交的,并且是派生的。目前我们还没有一个可以在不进行检查的情况下运行的功能。我们需要在它们在编译时(调试构建)或运行时(track - ea)消失或两者都消失之间做出选择。然后我们可以看看这对断言/前置/后置的影响,以及重新审视两者现有的用途。"不建议使用断言的地方"文档按类别处理它们,但在没有考虑它们的成本的情况下,在我看来这似乎是不切实际的。

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

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

链接:在Google上搜索“java assert shirazi”。(不发布链接,因为我在10秒内无法判断是否包含我的会话信息。)

0

评论者:akiel

关于这个问题有什么新的进展吗?我还想找到一种在生产中禁用断言的方便方法。

0

评论者:lopuz

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

0

评论者:lopuz

顺便说一下,有一个可以在clojars中通过clojars启用/禁用断言的库
https://github.com/pjstadig/assertions
这对我帮助很大。

0
...