Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

clojure.spec: `alt` vs `or` for sequence spec

Tags:

clojure

I'm following clojure.spec's guide (http://clojure.org/guides/spec). I'm confused by the difference between alt and or for sequence spec.

For me the two following examples work equally well. So what's the difference between the two?

; 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])
like image 879
M. Tong Avatar asked Jun 02 '16 05:06

M. Tong


2 Answers

s/alt is for concatenating nested regex specs where using s/or specifies a nested sequence. In your example it doesn't make a difference since you are not using nested regex specs. Here is an example:

(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

As you can see, or specifies a nested sequence in which a new regex context is started, whereas alt specifies the opposite:

(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
like image 123
Leon Grapenthin Avatar answered Nov 16 '22 05:11

Leon Grapenthin


From http://clojure.org/guides/spec, we know

When regex ops are combined, they describe a single sequence.

that means if you want to valid the nested sequences, you should do like this.

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

And then your data looks like this (Notice the brackets around)

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

Also, if you do (s/or).

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

your data should be the same as the old one (Notice there are no brackets around)

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

BTW, for non-nested sequences. there is still a little bit difference between (s/alt ) and (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]
like image 40
lambeta Avatar answered Nov 16 '22 06:11

lambeta