是否使用clojure.spec进行强制惯用?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了是否使用clojure.spec进行强制惯用?相关的知识,希望对你有一定的参考价值。

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

(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]))

以上是单一的/无意的是真的吗?如果是这样,什么是适当的/惯用的。预期用途的界限在哪里?

答案

Clojure.spec is not intended for coercion

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

Cognitect clojure.core团队的Alex Miller重申了Clojure邮件列表20 February 2018的官方立场:

我们建议您不要使用构象异构体进行强制攻击。

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

但是......如果没有规范,如何强制?答案是,简单的旧Clojure功能,就像我们一直在做的一样。再次来自Alex Miller(16 December 2016):

如果您真的需要对所有属性进行批量强制,看起来您可以在使用规范验证地图之前明确地执行此操作,这可能是更好的方法。我不确定在使用普通的Clojure函数显式转换地图时,您购买的是什么规格?

...为什么不?

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

另一答案

更新:

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


您可以使用规范进行强制,但重要的是您还要使用非强制版本。如果您强制执行规范,则同时执行两项操作,违反了SRP。因此建议使用简单的验证,然后您可以在其上另一个,所以稍后您可以选择是否要使用强制版本或简单验证版本。

另一个选择(我更喜欢)是根据并行运行的规范来设置强制引擎。如果你看看specs如何从specs(check here)推断出生成器,你可以看到你可以使用spec形式来推导其他东西,所以这个别的东西可以是你的强制引擎。

我写过一篇文章,我在那里解释如何做到这一点,你可以在这里找到它(只是跳到强制规范部分):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)))

此处还有一个更详细的版本(只是代码),其中包含一个二级注册表,因此您可以设置特定的coercers以匹配相同的spec关键字:https://gist.github.com/wilkerlucio/08fb5f858a12b86f63c76cf453cd90c0

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

另一答案

虽然规格设计仍然充实,但对于一个明确的答案来说还有点太早了。所以我使用这个定义,源自标准lib如何使用符合:


强制是隐含的并且自动转换为预期下游的形状。

符合采用已具有预期形状的值,并生成从值和规范一起导出的编程信息(因此符合)。根据规范,符合性结果不保证有效。例如基于s/or的规范或基于Regex的规范。


简而言之:合规不是习惯性的强制,而是其他类似的东西。

我希望有时候强制规范可以成为一个独立的功能。

以上是关于是否使用clojure.spec进行强制惯用?的主要内容,如果未能解决你的问题,请参考以下文章

如何Clojure.Spec引用类型(如原子)?

是否值得惯用编程?一个 ES6 示例 [关闭]

在 Delphi 中进行异步套接字编程的惯用方法是啥?

如何在 u32 和 usize 之间进行惯用转换?

是否有一种惯用的方式来操作 Ruby 中的 2 个数组?

使用指向类私有方法的指针的命名参数惯用语