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时将{{}}或{{}}作为线程局部绑定推送,或者是否只有在编译clojure时才能根绑定?
  4. 将添加*绑定到{{clojure.main/with-bindings}}。其他任何地方都需要吗?
  5. build.xml不需要更改——系统属性将会流动(而且build.xml可能很快就不会存在了)
  6. 一旦我们就方法达成一致,我将ping Maven插件和Lein所有者,让他们通过设置传递。
  7. 更清晰的断言信息将是另一个工单。
  8. **debug* 与 **unchecked-math** 之间的交互是什么?将检查改为 }}?
0
by

评论人:richhickey

#3 - 仅限根绑定
#4 - 不应该在 with-bindings 中 是出于与 #3 相同的原因——我们不想让人们对 **debug** 和 **assert** 进行设置
#8 - 可以将其封装在一个辅助函数中

#6 - 我最大的保留意见是这还没有根据 Maven 最佳实践进行设计

0
by

评论人:stuart.sierra

系统属性可以通过 Maven 传递,所以我预计这不会成为问题。

但我 prefer * } 默认保持为 true。

0
by

评论人:cemerick

SS关于这个方法对 Maven 没有问题。此外,构建可以轻松设置以始终发出两个jar包,一个“常规”的,一个“调试”的。

我建议,虽然 clojure.debug 可能会有广泛的影响,但应该提供额外的属性,以便对可能在未来提供的每个额外的“调试”相关的参数化进行细粒度控制。


我想提出一些可能相关的关注点(在上述上下文中思考了一段时间之后),其中一些或所有这些可能只是我对各个领域缺乏理解的简单结果。

查看在 core.clj 中 {{assert}} 的使用位置(据我所知只有两个地方:验证 {{derive}} 的参数和检查 {{fn}} 中的预条件和后条件),似乎不合理默认使其为 {{false}}。即非 {{Named}} 值将能够进入层次结构,而且预条件和后条件将简单地被忽略。

我理解断言(这里指 JVM 构造,Clojure 重新使用 {{AssertionError}}),不应该用于验证公共API函数的参数,也不应该用于验证函数正常操作的任何方面(即(link: http://download.oracle.com/javase/1.4.2/docs/guide/lang/assert.html#usage text: "where not to use assertions"))。这意味着当需要时,{{derive}} 应该抛出 {{IllegalArgumentException}},而 fn 的预条件和后条件可能应该抛出 {{IllegalStateException}} — 或者,无论如何,通过 {{assert}} 抛出其他不是 {{AssertionError}} 的东西。这将更好地与 core 中的大多数函数使用 {{assert-args}} 而不是 {{assert}} 匹配,前者抛出 {{IllegalArgumentException}} 而不是 {{AssertionError}}。

这导致我提出问题:{{assert}}(和 * })是否是 Clojure 构造,还是伪互操作形式?

如果是前者,则可以大致有我们想要的任何语义,但是似乎它不应该抛出 {{AssertionError}}。

如果是后者,那么在JVM上抛出{{AssertionError}}是合适的,但我们需要注意,断言可以在运行时启用和禁用(无需切换Clojure的不同版本),理想情况下使用宿主定义的开关(例如{{-ea}}等)以及不太可能像*这样的东西。我不知道这一点是否可行或实用(我认为这需要非琐碎的编译器更改)。


希望上面的内容现在还不是过去式。提前感谢您的耐心。;-)

0

评论人:richhickey

感谢Chas的有用意见。目前还没有得出任何结论。我认为我们应该退后一步,看看这里的目标,然后再考虑解决方案。作为动态语言,我们可能有很多想要验证的程序,而这些检查的成本是我们不愿意在生产中承担的。

断言作为宏,具有这样一个好处:如果**断言**在编译期间不为真,它生成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

即使我们百分之百确定断言的移除是彻底的,我仍然会投票支持一个单独的Clojure级别开关,以下是一些原因

  1. 我真正迫切需要禁用某些断言,我一点也不需要Java互操作。
  2. 随着时间的推移,将会出现多个调试工具,对于Clojure用户来说,有一个顶级的
    调试开关是非常方便的。
  3. 通过命令行标志启用的Java仍然是可能的一个单独的特性。我们可以在assert中加入此作为(小的)破坏性更改,或者在java-assert互操作形式中将其分开。我在此问题上摇摆不定。
  4. 我认为从一个非Java断言形式抛出{{AssertionError}}是完全可以的。我们不相信是静态异常层次结构的世界,在生产中的断言是一个关键失败,无论你称它为何。

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
参考: https://clojure.atlassian.net/browse/CLJ-250(由 stu 报告)
...