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

欢迎!请查看 关于 页面以了解此工作方式的更多信息。

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

如果喜欢为像 {{=}} 这样的核心函数编写规范,这些问题会变得严重,后者本身就是由规范使用的。我在编写 {{assoc}} 的规范时发现了这个错误。

可能的解决方案可能是在内部使用 {{(Object.)}} 值并仅将 {{:clojure.spec/invalid}} 用于 API 边界。但我还没有深入思考这个问题。

9 个答案

0
_评论由:akiel_ 添加

我有一个例子也出现了描述中的问题。由于应该返回 {{:clojure.spec/invalid}} 本身,因此不可能测试适用于 conformer 的谓词的返回值。


(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 来解决宏的问题。我认为通常最好使用 predicate 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 任何类型? :val 任何类型? :kvs ((s/* (s/cat :ks 任何类型? :vs 任何类型?))))
  :ret 是否为关联型?

(stest/instrument `assoc)

(s/conform 字符串? 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

评论者:borkdude

我在 speculative 中修改了 {{::any}} 规范如下

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

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

`

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