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

欢迎!请参阅关于页面,了解更多关于如何使用本站的详细信息。

0
Clojure
此问题包含两个补丁

# 当clojure.lang.RT载入时根据系统属性clojure.debug设置{{*assert*}} (根据系统属性clojure.debug存在与否)
# 扩 Большение сообщений об ошибках в assert для включения {{local-bindings</code> (一个包装了隐式 <code>&env的新宏))

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

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

15 个回答

0

评论由:stu发起

忽略旧补丁。请考虑以下实现,然后将其移动到stu的待处理事项中

  1. RT将检查系统属性{{"clojure.debug"}},默认为false
  2. 属性将调整当前 的根绑定,并添加一个新的 标志。(调试构建将不仅驱动assert,还会驱动其他事物)
  3. 在编译clojure时,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 文本:“where not to use assertions”)。这表明当必要时应抛出 {{IllegalArugmentException}} 的 {{derive}},fn 的预条件和后条件应可能抛出 {{IllegalStateException}} -- 或者无论如何,应该通过 {{assert}} 抛出其他而不是 {{AssertionError}}。这将与大多数在核心中使用 {{assert-args}} 而不是 {{assert}} 的函数相匹配,前者会抛出 {{IllegalArgumentException}} 而不是 {{AssertionError}}。

这让我产生了这样的疑问:{{assert}}(和 * })是否是Clojure结构,还是一种准互操作形式?

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

如果是后者,那么在JVM上使用{{AssertionError}}是合适的,但我们需要注意,断言可以在运行时被启用或禁用(而不必在不同的Clojure构建之间切换),理想情况下使用宿主定义的开关(例如{{-ea}}和相关命令)以及不太可能使用像 * 这样的东西。我不知道目前这个是否可能或可行(我认为这可能需要对编译器进行非平凡的修改)。


希望上述内容目前还不是过时的话题。提前感谢您的耐心。;-)

0

由 richhickey 发表的评论

感谢Chas的有用意见。目前还没有得出结论。我认为我们应该退后一步,看看这里的目标,然后再继续寻找解决方案。作为一个动态语言,关于我们的程序,我们可能想要验证许多事情,其中检查的成本是我们不愿意在生产环境中支付的成本。

作为宏,assert具有一个很好的属性,即在编译时如果**assert**不被认为是真实的,它生成nil,根本不会有条件测试。因此,目前它更像是静态条件编译。

Java的assert确实可以通过-ya作为您所说的来启用运行时的切换。我没有查看Java断言生成的字节码,但Clojure的assert可能做类似的事情,如果运行时开销可以忽略不计。HotSpot可能针对检查单个标志的情况具有省略断言字节码的特殊代码。我只是不确定这个标志是(链接:http://download.oracle.com/javase/1.5.0/docs/api/java/lang/Class.html#desiredAssertionStatus() 文本:Class.desiredAssertionStatus)。

这变成assert或前置/后置条件的更改、最佳实践等是正交和派生的。目前我们没有在没有检查的情况下运行的功能。我们需要在编译(调试构建)或运行时(跟踪-ya)之间进行选择,或者两者都要。然后我们再看看这是如何在assert/pre-post中体现出来的,重新审视现有的使用情况。"哪里不使用断言"文档分类处理了它们,但在不考虑它们的成本的情况下,在我看来这似乎不太现实。

如果有人能看看Java是如何生成和优化assert的,我将感激不尽。

0
_由cemerick发表评论_

不幸的是,字节码问题已经超出了我的薪资水平...

一些额外的想法供您参考

{{assert}}作为一个宏,对于它的用途来说是完全有意义的。但是,“编译时”在Clojure中是一个棘手的概念:有代码加载时间、AOT编译时间以及递归AOT编译时间。考虑到这一点,对于一个部署到生产环境的应用程序来说,它可能包含各种代码或没有由{{assert}}使用的代码拼凑在一起,这些代码或命名空间pod署随库和命名空间何时加载、AOT编译或它们的依赖AOT编译,以及{{*assert*}}在每个这样的时间点的值。当然,这对于所有这样宏的结果都依赖于相关状态的情况来说也是如此(我认为这曾是{{clojure.contrib.logging}}的一个大问题,使其在一段时间内只能与log4j一起使用)。

JVM断言机制的真正吸引人之处在于它可以通过参数化,使得在特定运行时和在每个包的基础上使用。如果需要的话,让{{assert}}对{{\*ns\*}}敏感来重新实现这一概念似乎很简单,但已经提到的编译时间复杂度仍然存在,拥有两个独立控制的断言设施听起来并不有趣。

我对CLR几乎一无所知,但看起来它并没有提供任何类似运行时可控断言的功能。
0

评论由:stu发起

我能找到的最好(过时)的证据表明,编译器根据{{desiredAssertionStatus}}的返回值设置一个特殊的类静态最终字段{{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应该为零
29: invokespecial #9; //Method java/lang/AssertionError."":(Ljava/lang/Object;)V
32: athrow

即使我们100%确信断言删除是彻底的,我仍然会因为以下原因而投票支持一个单独的Clojure级别的开关

  1. 我确实有禁用某些断言的迫切需求,我根本不需要Java互操作。可以说其他人也将陷入同样的困境。
  2. 随着时间的推移,将有多个调试设施,并且为Clojure用户提供顶级调试开关是很方便的。
  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发起

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

0

评论者:akiel

关于这个问题有什么新消息吗?我还在寻找一种方便的方式来在生产环境中禁用断言。

0

评论者:lopuz

我对这个问题的任何新信息都感兴趣。
通过 -ea/-da 选项在运行时启用/禁用断言的便捷方式将是一个极好的功能!

0

评论者:lopuz

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

0
参考:[https://clojure.atlassian.net/browse/CLJ-250](https://clojure.atlassian.net/browse/CLJ-250)(由 stu 报告)
...