2024 年 Clojure 状态调查! 中分享您的看法。

欢迎!请参阅 关于 页面了解有关此功能的更多信息。

+1 投票
Clojure
h2. 问题

为了进行运行时强制转换,规范需要遍历两次以除去分支信息:{{s/conform}} + {{s/unform}}。这引入了额外的延迟(见下面的示例)。

h2. 建议方案

新的通用 {{s/walk*}} 支持通用规范遍历。

h2. 当前状态

* {{s/valid?}} 允许我们快速检查一个值是否符合规范
** [https://dev.clojure.org/jira/browse/CLJ-2115] 可以通过一次遍历来返回错误。
对于强制转换,我们执行 {{s/conform}} + {{s/unform}} / {{s/explain}}
** [https://dev.clojure.org/jira/browse/CLJ-2116] 将符合规范的规范分离出来

尽管如此,当运行 {{s/conform}} + {{s/unform}} 时,我们仍然需要遍历规范两次 - 这在性能上并不是最优的。以下是示例,使用配置为 {{-server}} 运行的 JVM 的 2013 年晚期 MacBook Pro (2.5 GHz i7)。


(require '[clojure.spec.alpha :as s])

(s/def ::id int?)
(s/def ::name string?)
(s/def ::languages (s/coll-of #{:clj :cljs} :into #{}))
(s/def ::street string?)
(s/def ::zip string?)
(s/def ::number int?)

(s/def ::address (s/keys
                   :req-un [::street ::zip ::number]))

(s/def ::user (s/keys
                :req [::id]
                :req-un [::name ::address]
                :opt-un [::languages]))

(def value {::id 1
            :name "Liisa"
            :languages #{:clj :cljs}
            :address {:street "Hämeenkatu"
                :number 24
                :zip "33200"}})

; 2.0 µs
(cc/quick-bench
  (s/conform ::user value))

; 6.2 µs
(cc/quick-bench
  (s/unform ::user (s/conform ::user value)))


尽管 {{s/conform}} 相对较快,但当我们运行 {{s/unform}} 时,延迟会增加到样本的三倍。由于我们知道我们根本不感兴趣于分支信息,我们可以只是不发出那些信息。

h2. 建议

{{s/walk*}} 用于替换 {{s/confrom*}} 和 {{s/unform*}},甚至可能 {{s/explain*}}。它将需要额外的 {{mode}} 参数,这将是一个以下之一的 Keyword:

* {{:validate}} - 在第一个失败的规范上返回 false
* {{:conform}} - 与当前的 {{s/conform*}} 相似,也许还可以返回 {{s/explain}} 结果?
* {{:unform}} - 与当前的 {{s/unform*}} 相似
* {{:coerce}} - {{s/conform*}} + {{s/unform*}},可以优化(例如,如果没有分支信息,只需返回值)

公共API可以保持不变(+可选额外参数CLJ-2116),并新增{{s/coerce}}来调用{{s/walk*}}带有{{:coerce}}的方法。

h2. 结果

单遍验证与强制约束。运行时快乐。

5 答案

0

评论者:ikitommi

已重命名问题。而不是关键字参数,应该使用一个函数来遍历规范以支持任意的遍历应用。

0

评论者:marco.m

你好,有什么新闻吗?

0

评论者:alexmiller

在下一批实现变更之前,没有计划查看这个问题,所以还需要一段时间。

0

评论者:ikitommi

spec-tools中现在有三个~this的不同版本

1) spec-walker:使用s/form遍历规范和值,并调用带有两者的回调函数,返回新的值。它可以用于如约束之类的功能。它不进行验证,需要单独调用s/valid?s/explain-data(总共2-3次调用)

2) spec-visitor:仅遍历形式并返回新的形式。用于像spec => json-schema转换之类的功能。不转换值。

3) 使用重载的s/conform**包装规范,返回新值或错误。这是三者中最令人难过的一个,因为所有东西都需要被包装,但仍是目前唯一一个知道如何正确遍历正则表达式规范的工具。捆绑变换+验证,就像在Schema中一样。

...

https://cljdoc.org/d/metosin/spec-tools/0.9.0/doc/spec-coercion提供了第一个/强制约束的示例,希望能够突出支持约束的重要性。

这个问题是否在(近期)路线图上?我们能帮忙吗?如果不是在路线图上,是否有用spec进行运行时值转换的建议方法?

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