6

Tôi thích các ví dụ về biến thể Lisp (điểm thưởng cho Clojure hoặc Scheme) vì đó là những gì tôi quen thuộc nhất, nhưng bất kỳ phản hồi nào về DBC trong các phần lan chức năng sẽ có giá trị đối với cộng đồng lớn hơn.Bạn có thể triển khai thiết kế theo hợp đồng bằng Clojure cụ thể hoặc các ngôn ngữ chức năng nói chung như thế nào?

Dưới đây là một cách rõ ràng:

(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) 

Những gì tôi không thích về thực hiện này là logic hợp đồng làm lu mờ các chức năng cốt lõi; mục đích thực sự của hàm bị mất trong các kiểm tra có điều kiện. Đây là cùng một vấn đề tôi nêu ra trong this question. Trong một ngôn ngữ bắt buộc như Java, tôi có thể sử dụng chú thích hoặc siêu dữ liệu/thuộc tính được nhúng trong tài liệu để di chuyển hợp đồng ra khỏi triển khai phương pháp.

Có ai đã xem xét thêm hợp đồng vào siêu dữ liệu trong Clojure không? Các hàm bậc cao hơn sẽ được sử dụng như thế nào? tùy chọn khác là gì ở đó?

+2

Bạn đã nhìn như thế nào hợp đồng được thực hiện trong PLT-Đề án? Hãy xem. http://docs.plt-scheme.org/guide/contracts.html –

+0

@Alexey - Đó là một tài nguyên ngoạn mục! Tôi khá mới mẻ với Scheme (làm việc thông qua các cuốn sách Little/Seasoned) và tôi không biết điều này tồn tại, vì vậy cảm ơn bạn. – rcampbell

+0

Không trực tiếp trả lời cho câu hỏi của bạn, nhưng hãy xem QuickCheck và các dẫn xuất của nó (ClojureCheck). Đó là thử nghiệm cơ bản dựa trên tài sản, và trong hợp đồng bạn xác định các thuộc tính, vì vậy bạn có thể dễ dàng kiểm tra được tạo ra quá. – Masse

Trả lời

3

tôi có thể tưởng tượng một cái gì đó như thế này trong 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))

Một phiên dụ:

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

Vì vậy, bạn có thể xác định chức năng, rồi sau đó xác định trước và sau điều kiện bất cứ nơi nào bạn thích , mà không làm lộn xộn chính hàm logic.

chức năng điều kiện phải loại trừ một ngoại lệ nếu có điều gì đó sai.

Các vấn đề liên quan