Clojure Spec - проблема со спецификацией / или вложенная в спецификацию / и - PullRequest
0 голосов
/ 24 июня 2018

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

Выэто можно увидеть в напечатанном значении «v» здесь (надуманный пример):

(spec/valid? (spec/and (spec/or :always-true (constantly true))
                       (fn [v]
                         (println "v:" v)
                         (constantly true)))
         nil)
v: [:always-true nil]
=> true

Я думаю, что это может быть преднамеренно из строки документации spec / и:

Принимает предикаты / спецификации, например,

(с / и даже? # (<% 42)) </p>

Возвращает спецификацию, которая возвращает согласованное значение. Последовательные согласованные значения распространяются через остальную часть предикатов.

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

Ситуация становится еще хуже, если у вас есть несколько спецификаций или веток:

(spec/valid? (spec/and (spec/or :always-true (constantly true))
                       (spec/or :also-always-true (constantly true))
                       (fn [v]
                         (println "v:" v)
                       (constantly true)))
         nil)
v: [:also-always-true [:always-true nil]]
=> true

Я что-то упустил здесь?

1 Ответ

0 голосов
/ 24 июня 2018

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

ИМО, альтернативы этому поведению менее привлекательны:

  • Отменить соответствующие теги s/or по умолчанию . Мы всегда можем отказаться от него, если захотим, но мы бы не хотели, чтобы clojure.spec принимал это решение за нас. Спецификация предполагает, что мы хотим знать , какая ветвь s/or соответствует.
  • Не передавать согласованные значения в s/and, за счет компоновки спецификации / предиката.

К счастью, мы можем сбросить теги s/or, если это необходимо. Вот два варианта:

  • Оберните s/or в s/noncomforming. Благодаря приведенному ниже комментарию glts, напоминающему мне об этой (недокументированной) функции!

    (s/valid?
      (s/and
        (s/nonconforming (s/or :s string? :v vector?))
        empty?)
      "")
    => true
    
  • s/and спецификации s/or с s/conformer, который отбрасывает тег.

    (s/valid?
      (s/and
        (s/and (s/or :s string? :v vector?)
               ;; discard `s/or` tag here
               (s/conformer second))
        empty?)
      [])
    => true
    

    Если вам часто это нужно, вы можете уменьшить шаблон с помощью макроса:

    (defmacro dkdc-or [& key-pred-forms]
      `(s/and (s/or ~@key-pred-forms) (s/conformer second)))
    

Ситуация становится еще хуже, если у вас есть несколько спецификаций / или веток

Если вы пишете спецификацию для данных, которая допускает альтернатив (например, s/or, s/alt), и вы "перетекаете" действительные альтернативы в последующие предикаты (s/and) IMO, как правило, полезно иметь эти знания в последующих предикатах. Мне было бы интересно увидеть более реалистичный вариант использования для этого типа спецификации, потому что, возможно, есть лучший способ его спецификации.

...