在你的想法请访问 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}} 时,我们遍历规范两次 - 从性能的角度来看并不理想。以下是一个示例,使用的是 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*}} 类似
将强制类型转换、符合性判断和普通类型转换组合成一个操作,或许可以进行优化(例如,如果没有分支信息,只需要返回值即可)。

公共API可以保持不变(添加可选额外参数CLJ-2116),并增加一个新的强制类型转换,用以调用递归。

2. 结果

单次遍历验证与强制类型转换。运行时满足预期。

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:只遍历表单,并返回新表单。用于例如规范到JSON架构转换等。不转换值。

3) 使用覆盖了 s/conform** 的包装规范,返回新值或错误。在三个中这是最令人沮丧的,因为几乎所有都需要被包裹起来,但仍然是目前唯一能够正确递归遍历正则表达式规范的方法。组合了转换和验证,就像Schema一样。

...

https://cljdoc.org/d/metosin/spec-tools/0.9.0/doc/spec-coercion 具有第一个/强制类型转换的示例,希望突出为什么支持强制类型转换很重要。

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

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