Спецификация Clojure для предикатной функции

Я хочу написать спецификацию того, что означает, что функция является предикатом. Похоже, что в мире Clojure есть три способа рассказать о том, что такое предикат, хотя большинство, похоже, согласны с тем, что они должны заканчиваться знаком вопроса.

  1. Функция, которая принимает один аргумент и возвращает значение true или false.
  2. Функция, которая принимает один аргумент и возвращает true, false или nil.
  3. Функция, которая принимает один аргумент и возвращает истинное или ложное значение.

Билет Jira о том, что такое Predicate.

РЕДАКТИРОВАТЬ: предикат также может принимать несколько аргументов, как показано на примере contains?.


person Rovanion    schedule 18.04.2017    source источник
comment
Будь проще; придерживаться (1)!   -  person Alan Thompson    schedule 18.04.2017


Ответы (2)


Я думаю, что наиболее точной спецификацией предиката является:

(s/fspec :args (s/cat :v any?) :ret any?)

Хотя предикаты обычно возвращают значение true/false, для них этого не требуется — единственный требуемый контракт заключается в том, что он принимает одно значение и возвращает значение, которое будет рассматриваться как логическое значение истинности.

person Alex Miller    schedule 20.04.2017

Если я правильно понял clojure.spec/fdef, это позволяет нам делать спецификации, подобные описанным в вопросе.

(spec/fdef ::predicate-1
           :args (spec/cat :arg any?)
           :ret  boolean?)

Что мы можем проверить, передав несколько примеров, которые, как мы знаем, должны пройти или не пройти тест:

(spec/valid? ::predicate-1 boolean?)       => true
(spec/valid? ::predicate-1 (fn [a] 5))     => false
(spec/valid? ::predicate-1 (fn [a] true))  => true
(spec/valid? ::predicate-1 (fn [a b] true))=> false
(spec/valid? ::predicate-1 #(= 10 %))      => true
(spec/valid? ::predicate-1 (fn [a] nil))   => false

Для определения №. 2:

(spec/fdef ::predicate-2
           :args (spec/cat :arg any?)
           :ret  (spec/nilable boolean?))

(spec/valid? ::predicate-2 (fn [a] nil))   => true

И для №. 3 любая функция, которая принимает один аргумент, действительна, поскольку все в clojure либо истинно, либо ложно.

(spec/fdef ::predicate-3
           :args (spec/cat :arg any?)
           :ret  any?)

(spec/valid? ::predicate-3 identity)       => true
(spec/valid? ::predicate-3 str)            => true

Одна интересная вещь, которую мы тогда, кажется, можем сделать, это заставить спецификацию генерировать для нас такие функции:

(let [p (gen/generate (spec/gen ::pedicate-1))]
  (clojure.string/join
   " " [(p 0) (p 1) (p -1) (p nil) (p 'a) (p :a) (p (fn [a] a))]))
=> "false true true false true false false"

И из этого мы, возможно, можем попытаться угадать, что делает сгенерированная функция. Но без возможности увидеть источник нам будет трудно проверить, была ли наша догадка верной или нет.

person Rovanion    schedule 18.04.2017
comment
Верно ли мое впечатление, что вы публикуете вопросы, на которые сразу же отвечаете (и не в первый раз)? Ваш ответ был опубликован в ту же секунду, что и ваш вопрос... - person Piotrek Bzdyl; 18.04.2017
comment
@PiotrekBzdyl Да. Когда вы задаете вопрос на SO, вам предоставляется возможность ответить на вопрос самостоятельно в стиле вопросов и ответов, как это написано. - person Rovanion; 18.04.2017
comment
Да, я знаю, что вы сами сможете ответить на свои вопросы, как только найдете решение. Это первый раз, когда я увидел, как кто-то использует SO как своего рода блог для одновременного документирования вопроса и ответа (так что это не публикация вопроса, чтобы кто-то мог помочь ответить, включая человека, который спросил) и вот почему я в замешательстве. - person Piotrek Bzdyl; 28.04.2017
comment
Сообщество clojure отвечает на большинство вопросов, которые у кого-либо возникали в последнее время через slack. Некоторые особенно хорошие клоюристы передают любые вопросы, которые они задают там, в stackoverflow таким образом, чтобы следующий человек, у которого возникла проблема, мог легче найти ответ. - person Psetmaj; 13.06.2017