6

我寧願例子是在一個Lisp變型(加分Clojure的或計劃),因爲那是我最熟悉的,但當然在功能lanugages關於DBC任何反饋將是有價值的,更大的社。如何在Clojure特定功能語言或一般功能語言中實現按合同設計?

這裏有一個明顯的方式:

(defn foo [action options] 
    (when-not (#{"go-forward" "go-backward" "turn-right" "turn-left"} action) 
       (throw (IllegalArgumentException. 
        "unknown action"))) 
    (when-not (and (:speed options) (> (:speed options) 0)) 
       (throw (IllegalArgumentException. 
        "invalid speed"))) 
    ; finally we get to the meat of the logic) 

我不喜歡這個實現是契約邏輯掩蓋的核心功能;該功能的真正目的在條件檢查中丟失。這與我在this question中提出的問題相同。在像Java這樣的命令式語言中,我可以使用嵌入在文檔中的註釋或元數據/屬性將合同移出方法實現。

有沒有人看着Clojure中添加合同的元數據?如何使用高階函數?還有什麼其他選擇?

+2

你看合同如何在PLT-方案來實現?看一看。 http://docs.plt-scheme.org/guide/contracts.html – 2009-12-08 08:19:23

+0

@Alexey - 這是一個壯觀的資源!我對Scheme很新穎(通過The Little/Seasoned書籍工作),我不知道這是存在的,所以謝謝。 – rcampbell 2009-12-08 10:55:14

+0

不直接回答你的問題,但看看QuickCheck及其衍生產品(ClojureCheck)。它基本上是基於屬性的測試,並且在您定義屬性的合同中,因此您可以輕鬆地獲得生成的測試。 – Masse 2011-08-05 11:27:30

回答

3

我能想象這樣的事情用Clojure:

(defmacro defnc 
    [& fntail] 
    `(let [logic# (fn [email protected](next fntail))] 
    (defn ~(first fntail) 
     [& args#] 
     (let [metadata# (meta (var ~(first fntail)))] 
     (doseq [condition# (:preconditions metadata#)] 
      (apply condition# args#)) 
     (let [result# (apply logic# args#)] 
      (doseq [condition# (:postconditions metadata#)] 
      (apply condition# result# args#)) 
      result#))))) 

(defmacro add-pre-condition! 
    [f condition] 
    `(do 
    (alter-meta! (var ~f) update-in [:preconditions] conj ~condition) 
    nil)) 

(defmacro add-post-condition! 
    [f condition] 
    `(do 
    (alter-meta! (var ~f) update-in [:postconditions] conj ~condition) 
    nil))

一個例子會話:

user=> (defnc t [a test] (a test)) 
\#'user/t 
user=> (t println "A Test") 
A Test 
nil 
user=> (t 5 "A Test") 
java.lang.ClassCastException: java.lang.Integer (NO_SOURCE_FILE:0) 
user=> (add-pre-condition! t (fn [a _] (when-not (ifn? a) (throw (Exception. "Aaargh. Not IFn!"))))) 
nil 
user=> (t 5 "A Test") 
java.lang.Exception: Aaargh. Not IFn! (NO_SOURCE_FILE:0) 
user=> (t println "A Test") 
A Test 
nil

所以,你可以定義功能,可在以後定義前置和後置條件等。無論您喜歡,而不會混淆功能邏輯本身。如果事情是錯

條件函數應該拋出一個異常。