10

データ API がないため、私の clojure アプリは非常に急速に構造的に結合されることがわかりました。例外がスローされるかエラーが発生するため、入力を間違えると名前を持つキーを持つマップがあります。また、リストを分解するときに間違いを犯しやすいことにも気付きました (たとえば、リストの間違った部分を分解するなど)。

Java の世界から来た私は通常、IDE を使用して、最小限の順序付けられていないデータ オブジェクトから「適切な」データを取得するのに役立てています。しかし、clojure マップの受け渡しは、これとは逆のパラダイムのようです。

型システムやideコードの補完がない場合、クロジュリアンはどのように防御的にコーディングしますか?

4

2 に答える 2

6

たぶんあなたはレコードを探していますか?

(require '[clojure.set :as cset])

(defrecord Person [name age address phone email])

  ;; Make a keyword-based constructor to verify 
  ;; args and decouple ordering.
(let [valid #{:name :age :address :phone :email}]
  (defn mk-person[& args]
    (let [h (apply hash-map args)
          invalid (cset/difference (set (keys h)) valid)]       
      (when-not (empty? invalid)
        (throw (IllegalArgumentException. (pr-str invalid))))
      ; any other argument validation you want here
      (Person. 
        (:name h) (:age h) (:address h) (:phone h) (:email h)))))

=> (def p (mk-person :name "John" :email "john@hotmail.com"))
#:user.Person{:name "John", :age nil, :address nil, :phone nil, 
              :email "john@hotmail.com"}

関数(例外)またはキーワード(例外ではない)を使用してデータにアクセスすることにより、タイプミスした名前の例外を使用するかどうかを選択できるようになりました。

=> (.fax p) 
java.lang.IllegalArgumentException: 
    No matching field found: fax for class user.Person
=> (:fax p)
nil

このアプローチでは、既存のメソッドと競合するフィールド名を回避する必要があります。(@Jouniからのコメントを参照してください。)

または、ルックアップ用のキーワードと無効なキーをチェックするアクセサー関数を使用して、フィールド名の制限を回避することもできます。

(defn get-value [k rec]
  (let [v (k rec ::not-found)]
    (if (= v ::not-found)
      (throw (IllegalArgumentException. (pr-str k)))
    v)))

=> (get-value :name p)
"John"
=> (get-value :fax p)
IllegalArgumentException: :fax

「リストの間違った部分を破壊する」タイプの問題は、リスト内の「人」のようなものをエンコードしようとすることから生じる可能性があります。次に、「郵便番号は、「個人」リストの3番目の「住所」リストの4番目の要素です」などのことを覚えておく必要があります。

'classical' Lispでは、アクセサ関数を記述することでそれを解決できますが、Clojureではレコードを使用できます。

タイプミスはどのプログラミング言語でも問題を引き起こします。あなたができる最善のことは、それらを早期に発見することです。

オートコンプリートを備えたJavaIDEは、入力中にいくつかのタイプミスをキャッチする可能性があり、静的にタイプされた言語はコンパイル時にそれらの多くをキャッチしますが、動的言語では実行時までそれらを見つけることができません。これを動的言語(Python、Rubyなどを含む)の欠点と考える人もいますが、その人気を考えると、IDEのオートコンプリートやコンパイル時のエラーが失われるよりも、柔軟性が得られ、コードが節約されることが重要であると考えるプログラマーがかなりいます。

原則はどちらの場合も同じです。原因を見つけるために通過するコードが少ないため、初期の例外の方が適しています。理想的には、スタックトレースはタイプミスに直接つながるでしょう。Clojureでは、レコードとアクセサー関数がそれを提供します。

于 2011-10-07T07:31:37.650 に答える
6

「スキーマ」(キーだけでなく、値の型など) のバリデーター関数を記述してから、コードの事前および事後条件内で thm を使用します。これらの構文はほとんど知られていないため、ここで簡単に復習します。

(defn foo [x y] ; works with fn too
  {:pre [(number? x) (number? y)]
   :post [(number? %) (pos? %)]}
  (+ (* x x) (* y y)))

それらは依存しassertているため、無効にすることができます。(doc assert)詳細については。

于 2011-10-07T12:25:00.357 に答える