Fork me on GitHub
#clojure-spec
<
2020-05-25
>
niclasnilsson06:05:03

@charles.fourdrignier, well, yes and no. It compiles then, but it still doesn’t work. The following compiles, but the expected output is not useful, and not what’s expected from a recursive spec. (see output from explain and conform)

(require '[clojure.alpha.spec :as s])

(def expr '(+ (+ 1 (* 2 "three")) 200))

(defn operator? [x] (#{'+ '- '* '/} x))

(s/def ::operator operator?)
(s/def ::operand int?)

(s/def ::expr any?)
(s/def ::expr
  (s/or :operand ::operand
        :expr (s/cat :operator ::operator
                     :left ::expr
                     :right ::expr)))

(s/explain ::expr expr) ;; success, but shouldn't be if recursive specs works.

(s/conform ::expr expr)
;; result 
[:expr {:left (+ 1 (* 2 "three")) :operator + :right 200}]

valerauko07:05:34

i haven't tried this, but would an alias work? eg.

(s/def ::expression ::expr)
(s/def ::expr
  ; ...
  :left ::expression
  :right ::expresssion)))

Charles Fourdrignier08:05:24

Which Clojure did you use ? Clojure (from 1.9.0) includes clojure.spec and the namespace is different.

(require '[clojure.spec.alpha :as s])
With this namespace, it works for me (got an invalid for your expr ). Maybe a bugfix done on clojure repository and not in the clojure/spec.alpha ?

niclasnilsson11:05:17

@UAEH11THP, nope, unforturnately doesn’t work.

😢 4
niclasnilsson11:05:23

@charles.fourdrignier, this is spec 2, but I’ve tried previously on the spec that’s in the box with 1.10 as well.

Charles Fourdrignier11:05:13

Sorry, I don't get you were using Spec 2.

niclasnilsson11:05:27

But it seems to work in spec 1, even without the any? trick. Very odd?

Charles Fourdrignier11:05:24

Yes. I use the any? trick a couple of months ago in Spec 1. Pretty sure some error push me to use it... Very strange.

niclasnilsson11:05:57

Hmm… But this actually seems to work in “spec 1”, (clojure 1.10.1) but not in spec 2.

(require '[clojure.spec.alpha :as s])

(def bad-expr '(+ (+ 1 (* 2 "three")) 200))
(def expr '(+ (+ 1 (* 2 3)) 200))

(defn operator? [x] (#{'+ '- '* '/} x))

(s/def ::operator operator?)
(s/def ::operand int?)

(s/def ::expr
  (s/or :operand ::operand
        :expr (s/cat :operator ::operator
                     :left ::expr
                     :right ::expr)))

(s/valid? ::expr bad-expr)  ; false
(s/explain ::expr bad-expr) ;
(s/conform ::expr bad-expr) ; :clojure.spec.alpha/invalid

(s/valid? ::expr expr)   ; true
(s/explain ::expr expr)  ; success
(s/conform ::expr expr)

Alex Miller (Clojure team)12:05:02

There are some known issues with eager spec dereferencing in spec 2