我怎样才能得到Clojure:pre&;:发布以报告其失败值?

我怎样才能得到Clojure:pre&;:发布以报告其失败值?,clojure,clojurescript,Clojure,Clojurescript,我喜欢:前置条件和:后置条件,它们让我能够更快地确定何时将“方钉放入圆孔”。也许这是错误的,但我喜欢用它们作为一种穷人的类型检查。这不是哲学,这是一个简单的问题 在上面的代码中,我似乎可以很容易地确定s1是:pre条件下的函数参数。类似地,:post条件中的%始终是函数返回值 我想要的是,当断言错误中的任一条件失败时,打印s1或%的值。所以我得到了类似于 (defn string-to-string [s1] {:pre [(string? s1)] :post [(string?

我喜欢:前置条件和:后置条件,它们让我能够更快地确定何时将“方钉放入圆孔”。也许这是错误的,但我喜欢用它们作为一种穷人的类型检查。这不是哲学,这是一个简单的问题

在上面的代码中,我似乎可以很容易地确定
s1
:pre
条件下的函数参数。类似地,
:post
条件中的
%
始终是函数返回值

我想要的是,当断言错误中的任一条件失败时,打印
s1
%
的值。所以我得到了类似于

(defn string-to-string [s1] 
  {:pre  [(string? s1)]
   :post [(string? %)]}
  s1)
AssertionError为每个变量都包含一行,这些变量被标识为来自函数参数列表,并且在失败的测试中被引用。当函数的返回值不满足
:post
条件时,我也希望得到类似的结果

这使得在尝试从AssertionError进行诊断时,快速发现我是如何误用函数变得微不足道。它至少会让我知道该值是
nil
还是实际值(这是我最常见的错误)


我有一些想法,这可以通过宏来实现,但我想知道是否有任何安全和全局的方法可以基本上重新定义
(defn
(fn
和朋友们这样做,
:pre
:post
也可以打印值)这将导致测试失败。

您可以使用
clojure.test中的宏包装谓词

(string-to-string 23)

AssertionError Assert failed: (string? s1) 
(pr-str s1) => 23 
然后你会得到:

(defn string-to-string [s1] 
  {:pre  [(is (string? s1))]
   :post [(is (string? %))]}
 s1)

@octopusgrabbus提出了
(try…(catch…)
,暗示了这一点,您提到这可能太吵了,并且仍然包装在断言中。一个更简单、噪音更小的变体是一个简单的
(或者(这里的条件)(使用自定义消息抛出异常))
语法,如下所示:

(string-to-string 10)
;FAIL in clojure.lang.PersistentList$EmptyList@1 (scratch.clj:5)
;expected: (string? s1)
;actual: (not (string? 10))

这本质上允许您在自定义错误消息中使用前置和后置条件——前置和后置条件仍会像正常情况一样进行检查,但会对自定义异常进行评估(并因此引发)在断言错误发生之前。

类似于下面clojure规范解释问题的地方?这将抛出一个断言错误,您可以捕获该错误

(defn string-to-string [s1] 
  {:pre  [(or (string? s1)
              (throw (Exception. (format "Pre-condition failed; %s is not a string." s1))))]
   :post [(or (string? %)
              (throw (Exception. (format "Post-condition failed; %s is not a string." %))))]}
  s1)

Clojure规范可用于在参数上进行断言,从而在无效输入上产生异常,并提供解释失败原因的数据(必须启用断言检查):

(需要“[clojure.spec.alpha:as s]”
“默认情况下,断言检查处于关闭状态-这可以在REPL
;使用s/check断言或通过设置系统属性启动
;;clojure.spec.check-asserts=true”
;;
引用自https://clojure.org/guides/spec#_using_spec_for_validation
(s/检查断言为真)
(定义字符串到字符串[s1]
{:pre[(s/assert字符串?s1)]
:post[(s/断言字符串?%)]]
s1)
(字符串到字符串nil)=>#错误{:原因“规范断言失败\n尼尔-失败:字符串?\n”,
:data#:clojure.spec.alpha{:问题[{:路径[],:pred clojure.core/string?,:val nil,:via[],:in[]},
:spec#object[clojure.core$string_QMARK____5395 0x677b8e13“clojure.core$string_QMARK___5395@677b8e13"],
:值为零,
:失败:断言失败}
异常中的
[:data:value]
键显示失败的值。
[:data:problems]
键显示spec认为该值无效的原因。(在本例中,问题是直截了当的,但当嵌套贴图和多个spec组合在一起时,此解释非常有用。)

一个重要的警告是,当给定的有效输入返回该输入时,S/Asvest,但<<代码>:“预< /代码>和<代码>:POST < /COD>条件检查真实性。如果验证条件需要考虑伪值是有效的,那么您需要调整验证表达式,否则<代码> S/AsStudio会成功,但是

:pre
:post
中的真实性检查将失败

(定义字符串或零到字符串[s1]
{:pre[(s/assert(s/or:string?:nil nil?)s1)]
:post[(s/断言字符串?%)]]
(表1)
(字符串或nil到字符串nil)=>AssertionError
以下是我用来避免这个问题的方法:

(定义字符串或零到字符串[s1]
{:pre[(do(s/assert(s/or:string?:nil nil?)s1)true)]
:post[(s/断言字符串?%)]]
(表1)
(字符串或零到字符串零)=>“”

编辑:启用断言检查。

我可能遗漏了有关您的问题的某些内容,但是try..catch允许在:pre或:post中使用吗?您不能以这种方式报告问题吗?我假设在:pre和:post中任何有效的形式都可以(但不确定).我想指出的是:pre和:post中的表单似乎被插入到了断言中。因此,您可以(尝试(catch…),但最终会成为(assert(尝试(catch…));这是可以的(尽管有点嘈杂&也只能在实际断言之前打印).但我的目标是实际修改底层资产错误本身,只需添加相关符号和(pr str值)作为AssertionError中的一条消息。clojure规范示例:非常好,我喜欢。这是一个合理的折衷方案,让我达到了90%。不过,我认为如果在clojure 1.7+中,他们只是让它自动打印出所有立即变量的值,那就更好了(认识到refs、惰性序列和其他我没有考虑的可能会导致问题)。由于这在编码和查看输出方面都有点笨拙,我认为clojure的核心可以简化这一点
 (defn string-to-string [s1] 
  {:pre [ (or (s/valid?  ::ur-spec-or-predicate s1) 
              (s/explain ::ur-spec-or-predicate s1)]}
  s1)