Clojure 规范:如何将元组指定为函数参数?

Clojure Spec: How can I spec a tuple as a function argument?

我在 Clojure 中有一个函数接受 2 元素向量作为参数:

(defn influence [[school value]])

我想使用我已注册的现有规范为该函数的参数编写规范:

(s/fdef influence :args (s/cat :arg (s/cat :school ::school, :value ::value))

但是,这不起作用,嵌套的 s/cat 调用在顶层运行,并将 ::school 的规范应用于整个参数列表。还有一个名为 s/tuple 的函数,它可能建议您可以执行

(s/fdef ->influence :args (s/cat :influence (s/tuple ::school ::value)))

但这也行不通。规范似乎以某种方式混淆并尝试使 规范名称 符合规范:

val: :my.ns/school fails spec: :my.ns/school at: [:args :school] predicate...

您可以像这样使用 2 个参数的常规函数​​开始:

(ns tst.demo.core
  (:use tupelo.core tupelo.test)
  (:require
    [clojure.spec.alpha :as s]
    [clojure.spec.test.alpha :as stest]
    ))

(defn name-age
  [name age]
  (format "name=%s  age=%d" name age))

(s/fdef  name-age
  :args (s/cat
          :name string?
          :age pos-int? ) )

(dotest
  (spyx (name-age "joe" 42))
  (stest/instrument `name-age)
  (throws? (spyx (name-age "jill" :24))))

结果

(name-age ["joe" 42]) => "name=joe  age=42"

然后将其重写为 1 个 arg 的 fn,即一个元组:

(ns tst.demo.core
  (:use tupelo.core tupelo.test)
  (:require
    [clojure.spec.alpha :as s]
    [clojure.spec.test.alpha :as stest] ))

(s/def ::name string?)
(s/def ::age pos-int?)
(s/def ::na-tup (s/cat :name-arg ::name :age-arg ::age))

(defn name-age
  [na-tup]
  (s/valid? ::na-tup  na-tup)
  (let [[name age] na-tup]
    (s/valid? ::name name)
    (s/valid? ::age age)
    (format "name=%s  age=%d" name age)))

(s/fdef  name-age
  :args (s/cat
          :name ::name
          :age ::age ) )

(dotest
  (spyx (name-age ["joe" 42]))
  (stest/instrument `name-age)
  (throws? (spyx (name-age ["jill" :24]))) )

如果需要,您还可以获得一些帮助来解构 name-age 函数中的数据:

(s/conform ::na-tup na-tup) => 
    {:name-arg "joe", :age-arg 42}

实时代码示例

我研究了 Clojure 规范文档并将它们变成了 may be seen here 的实时单元。可能会在某个时候将它们分解成一个单独的回购协议。