Будет ли работать мультиселектор для того, что вы делаете? Api Doc здесь.
Clojure уже есть (недокументировано, возможно, будет изменено) поддержка предварительных и пост-условий на fn
s.
user> (defn divide [x y]
{:pre [(not= y 0)]}
(/ x y))
user> (divide 1 0)
Assert failed: (not= y 0)
[Thrown class java.lang.Exception]
Хотя вид уродливый.
Я бы, вероятно, написал макрос только для того, чтобы кратко сообщить, какие тесты завершились неудачно (цитировать и распечатывать тест буквально). Код CL, с которым вы связались, выглядит довольно неприятно с этим огромным оператором case. На мой взгляд, здесь лучше использовать мультиметоды. Вы можете сами довольно легко собрать что-то подобное.
(defmacro assert* [val test]
`(let [result# ~test] ;; SO`s syntax-highlighting is terrible
(when (not result#)
(throw (Exception.
(str "Test failed: " (quote ~test)
" for " (quote ~val) " = " ~val))))))
(defmulti validate* (fn [val test] test))
(defmethod validate* :non-zero [x _]
(assert* x (not= x 0)))
(defmethod validate* :even [x _]
(assert* x (even? x)))
(defn validate [& tests]
(doseq [test tests] (apply validate* test)))
(defn divide [x y]
(validate [y :non-zero] [x :even])
(/ x y))
Затем:
user> (divide 1 0)
; Evaluation aborted.
; Test failed: (not= x 0) for x = 0
; [Thrown class java.lang.Exception]
user> (divide 5 1)
; Evaluation aborted.
; Test failed: (even? x) for x = 5
; [Thrown class java.lang.Exception]
user> (divide 6 2)
3
Несколько мыслей.
У меня такое ощущение, что это зависит от сложности и количества проверок, а также от характера функций.
Если вы выполняете очень сложные проверки, вы должны вывести валидаторы из функций. Причина в том, что вы можете использовать более простые для создания более сложных.
Например, вы пишете:
Если вы просто выполняете огромное количество простых проверок, и ваша проблема - многословие, ( например, у вас есть 50 функций, для которых требуются ненулевые целые числа), тогда макрос, вероятно, имеет больше смысла.
Еще одна вещь, которую следует учитывать, это то, что оценка функции - Clojure нетерпелив. В некоторых случаях вы можете повысить производительность, не оценивая некоторые параметры, если вы знаете, что функция завершится ошибкой, или если некоторые параметры не нужны на основе значений других параметров. Например, каждый? предикату не нужно оценивать каждое значение в коллекции.
Наконец, обратиться к «другим, о которых вы не думали». Clojure поддерживает общую диспетчерскую p на основе функции диспетчеризации. Эта функция может отправлять соответствующий код или сообщения об ошибках на основе любого количества факторов.
Случай, когда вам нужен макрос , может быть, если вы хотите изменить язык для автоматического добавления тестов к любой функции, определенной в блоке, например:
(with-function-validators [test1 test2 test4]
(defn fun1 [arg1 arg2]
(do-stuff))
(defn fun2 [arg1 arg2]
(do-stuff))
(defn fun3 [arg1 arg2]
(do-stuff)))