Как принимать только заказанные коллекции в спецификации

Как я могу создать спецификацию, которая принимает только последовательные (т.е. сохраняющие порядок) коллекции?

Например

cljs.user=> (s/def ::path (s/+ number?))                                                                                                                                                                                 
:cljs.user/path
cljs.user=> (s/explain ::path [])                                                                                                                                                                                                                           
val: () fails spec: :cljs.user/path predicate: number?,  Insufficient input
:cljs.spec.alpha/spec  :cljs.user/path
:cljs.spec.alpha/value  [] 
cljs.user=> (s/explain ::path [1 2 3])                                                                                                                                                                                                                      
Success!

Это как и ожидалось, но в то же время обратите внимание на порядок

cljs.user=> #{1 2 3}
#{1 3 2}
cljs.user=> (s/explain ::path #{1 2 3})                                                                                                                                                                                                                     
Success!

И это не имеет никакого смысла. Итак, вторичный вопрос:

Почему выражения, относящиеся к последовательности (cat, *, +,?) В спецификации, принимают коллекции, нарушающие последовательность?

UPD Я перепутал последовательное / упорядоченное различие в оригинальном вопросе. Вычистил терминологию.

2 ответа

Решение

Спецификации для последовательностей (спецификации регулярных выражений) не должны соответствовать упорядоченным, то есть последовательным коллекциям. Эта ошибка была исправлена ​​в текущих версиях спецификации, см. CLJ-2183.

В Clojure 1.10.0-RC5 результаты ожидаемые:

(s/conform ::path [1 2 3])   ; => [1 2 3]
(s/conform ::path #{1 2 3})  ; => :clojure.spec.alpha/invalid

(s/explain ::path #{1 2 3})
;; #{1 3 2} - failed: (or (nil? %) (sequential? %)) spec: :user/path

В последней строке видно, что спецификации регулярных выражений теперь соответствуют только значениям, которые sequential?,

Как я могу создать спецификацию, которая принимает только сохраняющие порядок коллекции?

Есть функция предиката clojure.core sorted? который вернет true для коллекций, которые реализуют Sorted,

(sorted? (sorted-map))
=> true

Он не возвращает true для коллекций, содержимое которых отсортировано, но не реализовано Sorted:

(sorted? [1 2 3])
=> false

Вы можете использовать произвольные функции предикатов в спецификациях, чтобы вы могли определить функцию, которая возвращает true для коллекций с отсортированным содержимым:

(defn ordered? [coll]
  (or (empty? coll) (apply <= coll)))

(ordered? [1 2 3])
=> true

Тогда вы можете использовать s/and объединить этот предикат с вашей спецификацией регулярных выражений:

(s/def ::path (s/and (s/+ number?)
                     ordered?))

(s/explain ::path [1 2 3])
Success!
=> nil

(s/explain ::path #{1 2 3})
val: [1 3 2] fails spec: :playground.so/path predicate: ordered?
=> nil
Другие вопросы по тегам