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

欢迎!请查阅 关于页面 获取此工作方式的更多信息。

0
Spec
{{(clojure.spec/valid? :clojure.spec/any :clojure.spec/invalid)}} 返回 {{false}}

如果像使用 spec 自身的 = 等核心函数编写规范,则此问题会变得更加严重。我在编写 assoc 的规范时观察到此错误。

可能的解决方案可能是在内部使用 (Object.) 望哨并仅将 :clojure.spec/invalid 在 API 边界处。

9 个答案

0
_评论者:akiel_

我还有一个例子,其中描述了这个问题。无法测试适合 conformer 的谓词的返回值,因为它应该返回 {{:clojure.spec/invalid}} 本身。


(ns coerce
  (:require [clojure.spec :as s]))

(s/fdef parse-long
  :args (s/cat :s (s/nilable string?))
  :ret (s/or :val int? :err #{::s/invalid}))

(defn parse-long [s]
  (try
    (Long/parseLong s)
    (catch Exception _
      ::s/invalid)))

0
_评论者:akiel_

在移除 {{:clojure.spec/any}} 并引入 {{any?}} 的情况下,alpha 10 没有任何变化。
0

由 seancorfield 发表的评论:

另一个从 Slack 出来的例子,与此相关:

(if-let [a 1] ::s/invalid)

因为 {{::s/invalid}} 会使 {{if-let}} 规范认为 {{then}} 形式不符合规范,所以会失败编译(宏展开)。

解决方案

(if-let [a 1] '::s/invalid)

0

由 ambrosebs 发表的评论:

来自野外的另一个例子: https://github.com/pjstadig/humane-test-output/pull/23

宏重写

(is (= ::s/invalid ..))

变为

(let [a ::s/invalid] ...)

导致了一些非常奇怪的错误。

0

akiel 发表的评论:

可以通过直接不在代码中使用 ::s/invalid 来解决宏的问题。我认为通常最好使用 s/invalid? 断言。

而不是这样写:

(= ::s/invalid ...)

应该使用:

(s/invalid? ...)

但我不知道如何解决在需要验证的数据中有 ::s/invalid 的问题。identical? 函数的规范是一个好的例子。

(s/fdef clojure.core/identical? :args (s/cat :x any? :y any?) :ret boolean?)

world 不会工作。

0

jcr 发表的评论:

请使用 sumtypes 而不是“魔法”值来指示失败或成功。例如:

(s/conform any? ::s/invalid) ;=> [:ok ::s/invalid] (s/conform int? ::s/invalid) ;=> [:failure #::s{:problems ... :spec ... :value ...}]

请注意,返回值应该是 clojure.lang.MapEntry 的实例,以便能够使用 {{key}} 和 {{val}} 操作。然而,如果不想在失败时返回 explain-map,则返回向量 {{[:ok value]}} 和 {{[:failure]}}(没有第二个元素)也可以正常工作。

由于 spec 当前处于 alpha 阶段,还有时间修复 API。

相关:CLJ-2115

0
_borkdude_ 发表的评论:

这也在像Cursive这样的工具REPL中存在。重现


(ns assoc.core
  (:require
   [clojure.spec.alpha :as s]
   [clojure.spec.test.alpha :as stest]))

(s/fdef clojure.core/assoc
  :args (s/cat :map (s/nilable associative?)
               :key any? :val any? :kvs (s/* (s/cat :ks any? :vs any?)))
  :ret associative?))

(stest/instrument `assoc)

(s/conform string? 1)


在Cursive REPL中评估此命名空间给出


打印返回值错误(ExceptionInfo)在clojure.spec.test.alpha/spec-checking-fn$conform! (alpha.clj:132)。
调用#'clojure.core/assoc未符合规范。


该问题也可以通过评估以上内容和以下内容来重现,而不需要Cursive:


(defn tooling-repl
  ([] (tooling-repl {}))
  ([state]
   (let [l (read-line)
         evaled (eval (read-string l))]
     (prn evaled)
     (recur (assoc state
                   :*1 evaled
                   :*2 (get state :*1))))))

;; 现在在REPL中输入:(s/conform string? 1)
(with-redefs [read-line (constantly "(s/conform string? 1)")]
  (tooling-repl))
0
by

由borkdude发表的评论

我按照以下方式修改了speculative中的{{::any}}规范:

`
(s/def ::any
(s/with-gen

(s/conformer #(if (s/invalid? %) ::invalid %))
#(s/gen any?)))

`

0
by
参考资料:https://clojure.atlassian.net/browse/CLJ-1966 (akiel报告)
...