2017-07-12 42 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) 

當然,爲每個用例編寫規範可能會退出如果你有很多人,那麼這是一種努力。