7

私が最もよく知っているのはLispバリアント(ClojureまたはSchemeのボーナスポイント)の例であることが望ましいですが、機能言語でのDBCに関するフィードバックは、もちろん、より大きなコミュニティにとって価値があります。

明らかな方法は次のとおりです。

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

この実装について私が気に入らないのは、コントラクトロジックがコア機能を覆い隠していることです。関数の真の目的は、条件付きチェックで失われます。これは、この質問で提起したのと同じ問題です。Javaのような命令型言語では、ドキュメントに埋め込まれたアノテーションまたはメタデータ/属性を使用して、メソッドの実装からコントラクトを移動できます。

Clojureのメタデータにコントラクトを追加することを検討した人はいますか?高階関数はどのように使用されますか?他にどのようなオプションがありますか?

4

2 に答える 2

4

Clojure は既に pre および post 条件をサポートしていますが、残念ながら十分に文書化されていません:

Clojure で引数を検証するには、関数またはマクロを使用する必要がありますか?

于 2009-12-09T16:00:57.103 に答える
3

Clojure では次のようなことが想像できます。

(defmacro defnc
  [& fntail]
  `(let [logic# (fn ~@(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

したがって、関数を定義してから、関数ロジック自体を乱雑にすることなく、好きな場所で事前条件と事後条件を定義できます。

何か問題がある場合、条件関数は例外をスローする必要があります。

于 2009-12-08T12:54:12.650 に答える