如何在Clojure中指定高阶函数参数?

如何在Clojure中指定高阶函数参数?,clojure,higher-order-functions,clojure.spec,Clojure,Higher Order Functions,Clojure.spec,假设我有一个函数,它接受一个函数并返回一个函数,该函数将给定的任何参数应用于传入函数,并将结果放入向量中(这是一个节点示例,但希望能说明我的观点) 我认为box函数的规范如下所示 (spec/fdef box :args (spec/cat :function (spec/fspec :args (spec/* any?) :ret any?)) :ret (spec/fspec :args (spec/*

假设我有一个函数,它接受一个函数并返回一个函数,该函数将给定的任何参数应用于传入函数,并将结果放入向量中(这是一个节点示例,但希望能说明我的观点)

我认为box函数的规范如下所示

(spec/fdef box
  :args (spec/cat :function (spec/fspec :args (spec/* any?)
                                        :ret any?))
  :ret (spec/fspec :args (spec/* any?)
                   :ret (spec/coll-of any? :kind vector? :count 1)))
如果我然后仪器盒功能

(spec-test/instrument)
和clojure.core/+的呼叫盒,我得到一个异常

(box +)
ExceptionInfo Call to #'user/box did not conform to spec:
In: [0] val: ([]) fails at: [:args :function] predicate: (apply fn),  Cannot cast clojure.lang.PersistentVector to java.lang.Number
:clojure.spec.alpha/args  (#function[clojure.core/+])
:clojure.spec.alpha/failure  :instrument
:clojure.spec.test.alpha/caller  {:file "form-init4108179545917399145.clj", :line 1, :var-scope user/eval28136}
  clojure.core/ex-info (core.clj:4725)
如果我正确地理解了错误,那么它将采取任何措施?谓词并为测试生成PersistentVector,clojure.core/+显然不能使用它。这意味着我可以通过将box的参数函数spec更改为

(spec/fspec :args (spec/* number?)
            :ret number?)
但如果我想对clojure.core/+和clojure.string/lower-case都使用box,该怎么办

注意:为了让规范在我需要的REPL中工作

:dependencies [[org.clojure/clojure "1.9.0-alpha16"]]
:profiles {:dev {:dependencies [[org.clojure/test.check "0.9.0"]]}}
:monkeypatch-clojure-test false
在project.clj和以下导入中

(require '[clojure.spec.test.alpha :as spec-test])
(require '[clojure.spec.alpha :as spec])

我认为不能用clojure.spec来表示这个函数的类型。您需要能够编写以下内容(此处使用Haskell样式的签名)

也就是说,能够“捕获”输入函数f的规范并将其部分包含在输出规范中非常重要。但据我所知,clojure.spec中没有这样的东西。您还可以看到,clojure.spec没有定义spec,例如,
clojure.core/map
,这也会有同样的问题。

正如所说,高阶函数返回值的类型(spec)取决于您给出的参数。如果您提供了一个可以对数字进行操作的函数,那么HOF返回的函数也可以对数字进行操作;如果它在字符串上工作,那么字符串,等等。因此,您需要以某种方式继承/反映(spec of the)参数函数,以便为HOF提供正确的输出规范,我不知道如何实现

在任何情况下,我都会选择为不同的用例创建单独的函数(别名):

(def any-box box)

(def number-box box)
然后,您可以单独指定这些参数:

(spec/fdef any-box ;... like your original spec for box

(spec/fdef number-box
  :args (spec/cat :function (spec/fspec :args (spec/* number?)
                                        :ret number?))
  :ret (spec/fspec :args (spec/* number?)
                   :ret (spec/coll-of number? :kind vector? :count 1)))
规范与仪器的工作符合预期:

(spec-test/instrument)

(number-box +)
(any-box list)
当然,如果您有很多用例,那么为每个用例编写规范可能是一项相当大的工作

(spec/fdef any-box ;... like your original spec for box

(spec/fdef number-box
  :args (spec/cat :function (spec/fspec :args (spec/* number?)
                                        :ret number?))
  :ret (spec/fspec :args (spec/* number?)
                   :ret (spec/coll-of number? :kind vector? :count 1)))
(spec-test/instrument)

(number-box +)
(any-box list)