在上周的 clojure/conj 会议上,可能有一半的演示文稿都以某种方式介绍了规范,而且它甚至还没有结束 alpha。spec是clojure的一大特色;它会留在这里,而且它很强大。
以静态类型检查为例,它被许多人誉为一种安全网,也是许多编程语言的定义特征。它非常有限,因为它只适用于编译时,并且只检查类型。另一方面,spec 验证并符合任何谓词(不仅仅是类型)的 args、return,并且还可以验证两者之间的关系。所有这些都在函数的代码之外,将函数的逻辑与代码的验证和文档分开。
关于工作流程:
与仅类型检查相比,关系检查的好处的一个典型示例是计算字符串的子字符串的函数。类型检查确保 in(subs s start end)
是s
一个字符串并且start
和end
是整数。但是,必须在函数内进行额外的检查,以确保start
和end
是正整数,即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 次测试(这可能会失败几次,但继续运行它会起作用——这是由于内置生成器无法满足:args
谓词的第二部分;如果需要,可以编写自定义生成器):
(stest/check `subs)
或者,想查看您的应用subs
在实时运行时是否调用无效?只需运行它,如果调用该函数并且不满足规范,您将获得规范异常:
(stest/instrument `subs)
我们还没有将它集成到我们的工作流程中,并且由于它仍然是 alpha 版本,所以不能用于生产,但第一个目标是编写规范。我将它们放在同一个命名空间中,但目前放在单独的文件中。
我预见我们的工作流程是使用这个(在 clojure 规范指南中找到)运行规范函数的测试:
(-> (stest/enumerate-namespace 'user) stest/check)
然后,最好为所有功能打开检测,并像我们通常测试它一样在负载下运行应用程序,并确保“真实世界”数据正常工作。
您还可以s/conform
用于解构函数本身中的复杂数据,或s/valid
用作运行函数的前置条件和后置条件。我对此不太感兴趣,因为它在生产系统中是开销,但这是一种可能性。
天空是极限,我们只是触及了表面!在接下来的几个月和几年里,很酷的事情会随着规范而来!