请在2024年Clojure调查中分享您的看法!

欢迎!有关如何工作的更多信息,请参阅关于页面。

+1 投票
Clojure
h2. 问题

为了进行运行时转换,spec 需要遍历两次以去除分叉信息:{{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] 将使符合规范与 specs 分离

尽管如此,当运行 {{s/conform}} + {{s/unform}} 时,我们两次遍历 specs,这在性能上是不理想的。以下是一个示例,使用了2013年晚期的2.5 GHz i7 MacBook Pro,并且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}} 参数,这将是一个以下之一的Keyword

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

公共API可以保持不变(+可选的CLJ-2116附加参数),并新增一个{{s/coerce}}来调用{{s/walk*}}并使用{{:coerce}}。



单次扫描验证与强制转换。快乐的运行时。

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 提出)
...