Подтвердить что ты не робот

Как я могу получить Clojure: pre &: post, чтобы сообщить о своем неудачном значении?

(defn string-to-string [s1] 
  {:pre  [(string? s1)]
   :post [(string? %)]}
  s1)

Мне нравится: pre и: post conditions, они позволяют мне разобраться, когда я поставил "квадратные колышки в круглых отверстиях" быстрее. Возможно, это неправильно, но мне нравится использовать их в качестве своего рода плохой проверки типа mans. Это не философия, но это простой вопрос.

Кажется, что в приведенном выше коде я легко могу определить, что s1 является аргументом функции в условии :pre. Аналогично, % в :post является всегда возвращаемым значением функции.

Я хотел бы напечатать значение s1 или %, если одно из этих соответствующих условий завершится с ошибкой в ​​AssertionError. Поэтому я получаю что-то вроде

(string-to-string 23)

AssertionError Assert failed: (string? s1) 
(pr-str s1) => 23 

С AssertionError, содержащим одну строку для каждой переменной, которая была идентифицирована как из списка аргументов функции, и которая была указана в тесте с ошибкой. Мне также хотелось бы что-то подобное, когда возвращаемое значение функции завершится с ошибкой :post.

Это сделало бы тривиальным быстро определить, как я неправильно использовал функцию при попытке диагностики из AssertionError. Это по крайней мере сообщит мне, если значение nil или фактическое значение (которое является самой распространенной ошибкой, которую я делаю).

У меня есть некоторые идеи, что это можно сделать с помощью макроса, но мне было интересно, существует ли какой-либо безопасный и глобальный способ в принципе просто переопределить, что (defn и (fn и друзья делают так, что :pre и :post также печатает значения, которые приводят к сбою теста.

4b9b3361

Ответ 1

Вы можете перенести свой предикат с помощью макроса is из clojure.test

(defn string-to-string [s1] 
  {:pre  [(is (string? s1))]
   :post [(is (string? %))]}
 s1)

Затем вы получите:

(string-to-string 10)
;FAIL in [email protected] (scratch.clj:5)
;expected: (string? s1)
;actual: (not (string? 10))

Ответ 2

@octopusgrabbus вроде намекнул на это, предложив (try ... (catch ...)), и вы упомянули, что это может быть слишком шумно и по-прежнему завернуто в assert. Простым и менее шумным вариантом этого будет простой синтаксис (or (condition-here) (throw-exception-with-custom-message)), например:

(defn string-to-string [s1] 
  {:pre  [(or (string? s1)
              (throw (Exception. (format "Pre-condition failed; %s is not a string." s1))))]
   :post [(or (string? %)
              (throw (Exception. (format "Post-condition failed; %s is not a string." %))))]}
  s1)

Это, по сути, позволяет использовать предварительные и пост-условия с настраиваемыми сообщениями об ошибках - предварительные и пост-условия все еще проверяются, как и обычно, но ваше настраиваемое исключение оценивается (и таким образом бросается) перед AssertionError может случиться.

Ответ 3

Что-то вроде ниже, где clojure spec объясняет проблему? Это вызовет ошибку утверждения, которую вы можете поймать.

 (defn string-to-string [s1] 
  {:pre [ (or (s/valid?  ::ur-spec-or-predicate s1) 
              (s/explain ::ur-spec-or-predicate s1)]}
  s1)