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}} 时,我们遍历规格两次 - 这在性能上不是最优的。以下是一个示例,使用的是 Late 2013 年的 MacBook Pro 2.5 GHz i7,JVM 以 {{-server}} 运行。


(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}} 参数,它将是以下之一的关键字

* {{: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
by

评论者:ikitommi

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

0
by

评论者:marco.m

你好,有什么消息吗?

0
by

评论者:alexmiller

在下一个批量实施更改之前没有计划查看这个问题,因此还需要一段时间。

0
by

评论者: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有第一个/强制转型的示例,希望能突出支持强制转化的重要性。

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

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