Может быть, вы ищете записи?
(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
Проблемы типа «разрушение неправильной части списка» могут возникать из-за попытки закодировать что-то вроде «персонажа» в списке;тогда вам нужно запомнить такие вещи, как «почтовый индекс является четвертым элементом в списке« address »на третьей позиции в списке« person »».
В «классическом» Lisp вы можете решить это, написав метод доступафункции, в Clojure вы можете использовать записи.
Опечатки вызовут проблемы на любом языке программирования, лучшее, что вы можете сделать, это попытаться поймать их рано.
Java IDE с автозаполнением может перехватить некоторые опечатки, пока вы еще печатаете, а статически типизированный язык перехватит многие из них во время компиляции, но в динамическом языке вы не найдете их до времени выполнения,Некоторые считают это недостатком динамических языков (включая Python, Ruby и т. Д.), Но, учитывая их популярность, многие программисты считают, что приобретенная гибкость и сохраненный код важнее, чем потеря автодополнения IDE и ошибки времени компиляции.
Принцип одинаков в обоих случаях: более ранние исключения лучше, так как для поиска причины требуется меньше кода.В идеале трассировка стека приведет вас прямо к опечатке.В Clojure записи и функции доступа дают вам это.