12

我已经看到在各种要点中使用 clojure 构象器来强制数据,但也有一个印象(我不记得在哪里)强制(例如如下)不是构象器的惯用用法。

(s/def :conformers/int
  (s/conformer (fn [x]
                 (cond
                  (integer? x) x
                  (re-matches #"\d+" x) (edn/read-string x)
                 :else :cljs.spec.alpha/invalid))))

(s/def :data.subscription/quantity :conformers/int)

(s/def :data/subscription (s/keys :req-un [:data.subscription/quantity]))

以上是否是单调的/无意的?如果是这样,什么是合适的/惯用的用法。预期用途的边界在哪里?

4

3 回答 3

6

更新:

现在我已经发布了一个库来处理这个问题,请查看:https ://github.com/wilkerlucio/spec-coerce


您可以将规范用于强制,但重要的是您还拥有它的非强制版本。如果你对你的规范强制强制,你会同时做两件事,违反 SRP。所以建议有一个简单的验证,然后你可以在它上面再做一个,所以以后你可以选择是使用强制版本还是简单验证。

另一种选择(我更喜欢)是让强制引擎基于并行运行的规范。如果您查看规范如何从规范中推断生成器(请查看此处),您会发现您可以使用规范形式推导出其他内容,因此其他内容可以成为您的强制引擎。

我写了一篇文章,我解释了如何做到这一点,你可以在这里找到它(只是跳到强制规范部分):https ://medium.com/@wilkerlucio/implementing-custom-om-next-parsers- f20ca6db1664

从文章中提取的代码供参考:

(def built-in-coercions
  {`int?     #(Long/parseLong %)
   `nat-int? #(Long/parseLong %)
   `pos-int? #(Long/parseLong %)
   `inst?    clojure.instant/read-instant-timestamp})

(defn spec->coerce-sym [spec]
  (try (s/form spec) (catch Exception _ nil)))

(defn coerce [key value]
  (let [form (spec->coerce-sym key)
        coerce-fn (get built-in-coercions form identity)]
    (if (string? value)
      (coerce-fn value)
      value)))

这里还有一个更详细的版本(只是代码),其中包括一个辅助注册表,因此您可以设置特定的强制器以匹配相同的规范关键字:https ://gist.github.com/wilkerlucio/08fb5f858a12b86f63c76cf453cd90c0

这样您就不会强制执行强制转换,从而使您的验证更快,并让您更好地控制何时强制执行(这通常应该只发生在系统的边界处)。

于 2017-07-19T14:57:29.433 回答
4

Clojure.spec 不用于强制

根据编写 clojure.spec 的团队,将其用于强制是不习惯的。继续承担您自己的设计和工程风险。

Cognitect clojure.core 团队的 Alex Miller 在2018 年 2 月 20 日重申了 Clojure 邮件列表中的官方立场:

我们建议您不要使用conformers 进行强制。

他暗示了他们的原因:稍后在同一个线程中,他谈到了一个在规范之上构建强制的库,它“将所需输出的规范与强制函数相结合,使实际数据的规范隐含。” 这种合并不是 clojure.spec 预期用途的一部分。

但是......如果没有规范,如何强制?答案是,普通的旧 Clojure 函数,就像我们一直在做的那样。再次来自 Alex Miller(2016 年 12 月 16 日):

如果您确实需要对所有属性进行全面强制,似乎您可以在使用规范验证地图之前明确地执行此操作,这可能是更好的方法。我不确定使用普通 Clojure 函数显式转换地图的规范是什么?

...为什么不?

诸如规范之类的编程合同代表了在系统边界使用的各方之间的协议。这些规范/合同/协议旨在用于验证和错误检查、测试(尤其是生成)和文档(尤其是错误时)。关于数据应该是什么的协议绝对不同于将不符合要求的数据转换为符合要求的数据的行为。它们是两个不同的问题,尽管它们可能经常彼此靠近。这两个概念的这种相邻性使得不要混淆它们尤为重要。

于 2018-03-01T18:33:59.907 回答
3

虽然规格设计仍在充实,但要给出明确的答案还为时过早。所以我使用这个定义,源自标准库如何使用符合:


强制是隐式和自动转换为下游预期的形状。

一致性采用一个已经具有预期形状的值,并产生从该值和规范一起派生的程序化信息(因此是一致性。根据规范,不保证符合结果有效。示例是 s/or基于规范或基于正则表达式的规范。


简而言之:遵从不是惯用的强制,而是看起来相似的其他东西。

我希望规范中的强制有时会成为一个单独的功能。

于 2017-07-20T16:49:28.343 回答