2017-07-12 28 views
2

比方说,我有一个函数,它接受一个函数并返回一个函数,该函数应用它传递给函数的任何参数,并将结果放入一个向量中(这是一个不好的例子,但希望能够说明我的观点)。如何在Clojure中指定高阶函数参数?

(defn box [f] 
    (fn [& args] 
    [(apply f args)])) 

我认为存储箱功能的规格看起来像这样

(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/+显然不能使用。这意味着我可以得到它通过改变框的参数功能规格工作是

(spec/fspec :args (spec/* number?) 
      :ret number?) 

,但如果我想用框都clojure.core/+和clojure.string /小写呢?

N.B.要获得规范在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]) 

回答

3

我不认为你可以表达这种功能与clojure.spec类型。您需要type variables能够写类似(这里使用一个Haskell风格的签名)

box :: (a -> b) -> (a -> [b]) 

也就是说,你能够“捕获”输入函数f的规格,包括部分很重要它在你的输出规范中。但据我所知,clojure.spec中没有这样的东西。你也可以看到clojure.spec的list of specs for built-in functions没有定义一个规范,例如clojure.core/map,它会有相同的问题。

0

@amalloy's answer一样,高阶函数返回值的类型(spec)取决于您给出的参数。如果您提供了一个可以对数字进行操作的函数,那么HOF返回的函数也可以对数字进行操作;如果它适用于字符串,则字符串等等。所以,你需要以某种方式继承/反思参数(spec)的参数,为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) 

当然,为每个用例编写规范可能会退出如果你有很多人,那么这是一种努力。