たぶんあなたはレコードを探していますか?
(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では、レコードとアクセサー関数がそれを提供します。