6

필자는 Lisp 변종 (Clojure 또는 Scheme의 보너스 포인트)에 익숙하기 때문에 그 중 가장 익숙한 예제를 선호하지만 기능상의 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 –

+0

@Alexey - 멋진 자료입니다! 저는 Scheme (The Little/Seasoned books를 통해 일하는 것)에 상당히 익숙하며, 이것이 존재한다는 것을 몰랐습니다. 감사합니다. – rcampbell

+0

질문에 대한 대답이 아니지만 QuickCheck 및 그 파생물 (ClojureCheck)을 살펴보십시오. 기본적으로 속성 기반 테스트이고 계약에서는 속성을 정의하므로 생성 된 테스트도 쉽게 얻을 수 있습니다. – Masse

답변

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

그래서 당신은 당신이 좋아하는 whereever 사전 및 사후 조건을 정의 이후에 다음 함수를 정의 할 수 있습니다 함수 논리 자체를 어지럽히 지 않고

조건 함수는 잘못된 것이 있으면 예외를 throw해야합니다.

관련 문제