Clojure.spec: `alt` vs` или `для спецификации последовательности

Я следую clojure.spec guide (http://clojure.org/guides/spec). Меня смущает разница между alt и or для спецификации последовательности.

Для меня два следующих примера работают одинаково хорошо. Итак, какая разница между этими двумя?

; Use `alt`
(s/def ::config (s/* (s/cat :prop string?
                        :val (s/alt :s string? :b boolean?))))
(s/explain ::config ["-server" "foo" "-verbose" true "-user" 13])

; Use `or`
(s/def ::config (s/* (s/cat :prop string?
                        :val (s/or :s string? :b boolean?))))
(s/explain ::config ["-server" "foo" "-verbose" true "-user" 13])

Ответ 1

s/alt предназначен для конкатенации вложенных спецификаций регулярных выражений, где использование s/or указывает подпоследовательность. В вашем примере это не имеет значения, поскольку вы не используете вложенные спецификации регулярных выражений. Вот пример:

(s/def ::number-regex (s/* number?))

(s/def ::or-example (s/cat :nums (s/or :numbers ::number-regex)))

(s/valid? ::or-example [1 2 3])
;;-> false
(s/valid? ::or-example [[1 2 3]])
;;-> true

Как вы можете видеть, or указывает подпоследовательность, в которой запущен новый контекст регулярного выражения, тогда как alt указывает противоположное:

(s/def ::alt-example (s/cat :nums (s/alt :numbers ::number-regex)))

(s/valid? ::alt-example [1 2 3])
;;-> true
(s/valid? ::alt-example [[1 2 3]])
;;-> false

Ответ 2

Из http://clojure.org/guides/spec, мы знаем

При объединении регулярных выражений они описывают одну последовательность.

Это означает, что если вы хотите использовать вложенные последовательности, вы должны сделать это следующим образом.

(s/def ::config (s/* 
                 (s/cat :prop string?
                        :val (s/spec
                              (s/alt :s string? :b #(instance? Boolean %))))))

И тогда ваши данные выглядят так ( Уведомление скобки вокруг)

(s/explain ::config ["-server" ["foo"] "-verbose" [true] "-user" [13]])

Кроме того, если вы делаете (s/or).

(s/def ::config (s/* (s/cat :prop string?
                            :val (s/spec 
                                  (s/or :s string? :b #(instance? Boolean %))))))

ваши данные должны быть такими же, как у старого ( Уведомление нет скобок вокруг)

(s/explain ::config ["-server" "foo" "-verbose" true "-user" 13])

BTW, для не вложенных последовательностей. все еще есть небольшая разница между (s/alt) и (s/or):

;;; for (s/or)
(s/def ::name-or-id (s/or :name string?
                           :id int?))
(s/conform ::name-or-id 42) ;;=> [:id 42]

;;; for (s/alt)
(s/def ::name-or-id (s/alt :name string?
                           :id int?))
(s/conform ::name-or-id [42]) ;;=> [:id 42]