Clojure.spec - 为什么有用,什么时候用

Clojure.spec - Why is it useful and when is it used

我最近看了Rich Hickeys talk at Cojure Conj 2016,虽然很有趣,但我并没有真正理解clojure.spec中的要点,也不知道你什么时候用它。似乎大多数想法,例如 conform、valid 等,在 Clojure 中已经具有类似的功能。

我现在只学习 clojure 大约 3 个月所以这可能是由于缺乏 programming/Clojure 经验。

clojure.spec 和 cljs.spec 的工作方式与 Clojure 和 Cljs 相似,尽管它们并非 100% 相同,但它们基于相同的基本原则。

  • 您是否厌倦了编写程序文档?
  • 补考的前景是否会导致拖延?
  • 当老板说"test coverage"的时候,你会害怕吗?
  • 您是否忘记了数据名称的含义?

为了顺利表达硬规格,需要Clojure.Spec


Clojure.spec 为您提供了一种统一的方法来记录、指定和自动测试您的程序,并验证您的实时数据。

它几乎窃取了它的每一个想法。它不会做任何你不能为自己做的事情。

但在我几乎不了解的情况下,它改变了规范的经济性,使它值得正确地做。改变游戏规则? - 很有可能。

在上周的 clojure/conj 会议上,可能有一半的演示文稿以某种方式介绍了规范,而且它甚至还没有结束 alpha。 spec 是 clojure 的 主要 特性;它就在这里,而且功能强大。

以静态类型检查为例,它被许多人誉为一种安全网,也是许多编程语言的定义特征。它的局限性非常大,因为它只擅长编译时,而且它只检查类型。另一方面,spec 验证并符合 args return 的任何谓词(不仅仅是类型),并且还可以验证 relationships两者之间。所有这些都在函数代码的外部,将函数的逻辑与代码的验证和文档分开。

关于工作流程:

relationship-checking 相对于仅 type-checking 的优势的一个典型示例是计算字符串子字符串的函数。类型检查确保 (subs s start end) 中的 s 是字符串,而 startend 是整数。但是,必须在函数内进行额外的检查以确保 startend 是正整数,end 大于 start,并且结果子字符串不是比原来的字符串大。例如,所有这些东西都可以指定(如果其中一些有点多余甚至不准确,请原谅我):

(s/fdef clojure.core/subs

        :args (s/and (s/cat :s string? :start nat-int? :end (s/? nat-int?))
                     (fn [{:keys [s start end]}]
                       (if end
                         (<= 0 start end (count s))
                         (<= 0 start (count s)))))

        :ret string?

        :fn (fn [{{:keys [s start end]} :args, substring :ret}]
              (and (if end
                     (= (- end start) (count substring))
                     (= (- (count s) start) (count substring)))
                   (<= (count substring) (count s)))))

使用满足上述args规范的示例数据调用函数:

(s/exercise-fn `subs)

或 运行 1000 次测试(这可能会失败几次,但保持 运行ning 并且它会起作用——这是由于 built-in 生成器无法满足 :args 谓词的第二部分;如果需要,可以编写自定义生成器):

(stest/check `subs)

或者,想实时查看您的应用是否在 运行 期间对 subs 进行了无效调用?只是 运行 这个,如果函数被调用但不符合规范,你会得到一个规范异常:

(stest/instrument `subs)

我们还没有将其集成到我们的工作流程中,并且不能投入生产,因为它仍处于 alpha 阶段,但首要目标是编写规范。我将它们放在同一个命名空间中,但目前放在不同的文件中。

我预见我们的工作流程是 运行 使用这个(在 clojure 规范指南中找到)对规范函数的测试:

(-> (stest/enumerate-namespace 'user) stest/check)

然后,最好打开所有功能的检测,运行 应用程序在负载下,我们通常会对其进行测试,并确保 "real world" 数据正常工作。

您还可以使用 s/conform 在函数本身中解构复杂数据,或使用 s/valid 作为 运行ning 函数的前置条件和 post- 条件。我不太喜欢这个,因为它在生产系统中是开销,但这是可能的。

没有极限,我们才刚刚触及表面!在接下来的几个月和几年里,规格将会很酷!