это выполнимо, может быть, не очень практично, но хорошо для самообразования:
давайте начнем с создания функции, которая будет специальным обязательным случаем.
скажем, мы хотим для передачи векторов длины 2 или 3, где вектор 2 будет представлять пару ключ-значение простой карты привязки, например [:as abc]
или [a :a]
, а вектор размером 3 будет равен kv-default triple: [a :a "my default"]
. Пример использования:
(bindings-preproc [['a 1 "asd"]
['b 2 "ddd"]
[:or {'x 10}]
[:as 'whole]])
, в результате чего
{a 1, b 2, :or {x 10, a "asd", b "ddd"}, :as whole}
эта функция может выглядеть следующим образом:
(defn bindings-preproc [decls]
(let [defaults (into {} (keep (fn [decl]
(when (and (not (keyword? (first decl)))
(= 3 (count decl)))
(let [[nm _ default] decl]
[nm default])))
decls))
all-kvs (apply assoc {} (mapcat (partial take 2) decls))]
(update all-kvs :or merge defaults)))
(эта ошибка не включает проверяет ради иллюстративной простоты)
Следующим шагом является использование его внутри макросов привязки. Идея сделать bindings-preproc
макросом не удалась, потому что формы привязки проверяются на достоверность до , когда оцениваются внутренние макросы.
Но все же у нас есть функция, которая поможет, а именно теги считывателя . Они используются, например, когда вы используете синтаксис #inst
. Поскольку эти теги читателя обрабатываются во время чтения, прежде чем какие-либо макросы будут расширены, мы можем подключить наш препроцессор.
(здесь я буду использовать реальное обновление ссылок, чтобы продемонстрировать его из repl, но в реальных проектах Вы бы объявили эти теги в специальном файле)
user> (alter-var-root
#'default-data-readers
assoc 'my/reader #'user/bindings-preproc)
;;=> {uuid #'clojure.uuid/default-uuid-reader,
;; inst #'clojure.instant/read-instant-date,
;; my/reader #'user/bindings-preproc}
, поэтому теперь мы можем попытаться заставить его работать:
(defn f [#my/reader [[a :a 10]
[b :b 20]
[z :z]
[:keys [k1 k2 k3]]
[[c1 c2 & cs] :c]
[:or {z 101
k3 :wooo}]
[:as whole]]]
{:a a :b b :c1 c1 :c2 c2 :cs cs :z z :k1 k1 :k2 k2 :k3 k3 :whole whole})
user> (f {:a 1000 :c [:one]})
;;=> {:cs nil,
;; :c2 nil,
;; :z 101,
;; :c1 :one,
;; :k3 :wooo,
;; :b 20,
;; :whole {:a 1000, :c [:one]},
;; :k1 nil,
;; :k2 nil,
;; :a 1000}
user> (let [a 10
b 20
#my/reader [[x :x 1]
[y :y 2]
[z :z 100]] {:z 432}]
[a b x y z])
;;=> [10 20 1 2 432]