Mars
Mars

Reputation: 8854

number? can't be used in spec

In Clojure 1.9.0-alpha13, I can define a spec predicate using spec/and and integer? but not and and number?, it appears. This is puzzling. It's also puzzling that there is no error when I define the predicate, but only when I try to define a spec using it.

user=> (use '[clojure.spec :as s])
...
nil
user=> (s/def pos-int? (s/and integer? pos?))
user/pos-int?
user=> (s/def ::foo pos-int?)
:user/foo
user=> (s/def pos-num? (s/and number? pos?))
user/pos-num?
user=> (s/def ::bar pos-num?)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: pos-num? ...

Why do I get an error in the second case, but not the first? Is this a feature involving a distinction that I'm unaware of, or a bug?

Note that there is no error if I define ::bar directly:

user=> (s/def ::bar (s/and number? pos?))
:user/bar

Upvotes: 0

Views: 290

Answers (2)

Alex Miller
Alex Miller

Reputation: 70239

Your code here is incorrect. s/def should be called with a qualified-keyword to register a spec, not a symbol.

So this:

user=> (s/def pos-int? (s/and integer? pos?))
user/pos-int?

is not doing what you think it is. It is actually registering a function definition (which you typically do via s/fdef) under the symbol user/pos-int?.

user=> (s/def ::foo pos-int?)
:user/foo

This works because pos-int? is an existing function in clojure.core (that is, it's not using your prior definition at all).

This also appears to work but is similarly incorrect:

user=> (s/def pos-num? (s/and number? pos?))
user/pos-num?

This fails:

user=> (s/def ::bar pos-num?)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: pos-num? ...

because pos-num is not an existing predicate.

Presuming you want to register two specs for positive integers and numbers you could do:

(s/def ::pos-int (s/and integer? pos?))
(s/def ::pos-num (s/and number? pos?))

Upvotes: 3

Alan Thompson
Alan Thompson

Reputation: 29976

I believe your have discovered an interaction with the pre-defined pos-int? from clojure.core. In the source code:

src/clj/clojure/core.clj line 1400

(defn pos-int?
  "Return true if x is a positive fixed precision integer"
  {:added "1.9"}
  [x] (and (int? x)
           (pos? x)))

There is no pos-num? present.

Upvotes: 1

Related Questions