Fork me on GitHub

I think I’ve found a bug in clojure 1.9 / clojure.core.specs.alpha:

(if-let [foo 1] foo :clojure.spec.alpha/invalid)

CompilerException clojure.lang.ExceptionInfo: Call to clojure.core/if-let did not conform to spec:
In: [2] val: :clojure.spec.alpha/invalid fails at: [:args :else] predicate: any?
 #:clojure.spec.alpha{:problems ({:path [:args :else], :pred clojure.core/any?, :val :clojure.spec.alpha/invalid, :via [], :in [2]}), :spec #object[clojure.spec.alpha$regex_spec_impl$reify__1188 0xeabf22e "[email protected]"], :value ([foo 1] foo :clojure.spec.alpha/invalid), :args ([foo 1] foo :clojure.spec.alpha/invalid)}, compiling:(*cider-repl catalyst-graft*:8654:1) 


issue appears to be that you can’t return the value :clojure.spec.alpha/invalid from macros. It’s kinda like a macro hygiene issue, but for s/invalid


it's known


cool. Figured it would be


Hello everyone, I am learning Spec'ing higher order functions. Here are the examples which I am experimenting with (actual HOFs I am working on are quite complex) :

(Works fine with exercise-fn.)
(defn hof1 [x f] (f x))
(s/fdef hof1
        :args (s/cat :x number?
                     :f (s/fspec :args (s/cat :y number?)
                                 :ret number?))
        :ret number?
        :fn #(= ((-> % :args :f) (-> % :args :x)) (:ret %)))

(exercie-fn doesn't work :(. Here I am expecting input f to be either + or - or * fn.)
(defn hof2 [f x] (f x 1))
(s/fdef hof2
        :args (s/cat :x number?
                     :f (s/fspec
                          :args (s/cat :y number?)
                          :ret number?))
        :ret number?
        :fn #(= ((-> % :args :f) (-> % :args :x) 1) (:ret %)))
How write spec to ensure correctness in the samples generated using exercise-fn for HOFs? (I want to use spec's check fn to test HOFs on generated inputs based on written spec.)

Shantanu Kumar14:11:03

Hi, I’m a spec noob here — can somebody tell me an easy way to automatically (st/instrument) in all namespaces?


you only need to call st/instrument once


but you have to make sure you’ve loaded all the namespaces before you do


if it’s in your dev setup, just call st/instrument in your main or dev namespace at the bottom


if it’s in tests, your best bet is probably to use a fixture

Shantanu Kumar15:11:17

Also, how do I generally make “did not conform to spec” errors easier to read? I’m using Orchestra+Expound and have specified the following, but to no avail:

(set! s/*explain-out* expound/printer)
as mentioned here:


@kumarshantanu Can you post an example of the code you’re calling and the “did not conform to spec” message you get?

Shantanu Kumar15:11:46

This gist works for me at the REPL. @bbrinck The error reporting I’m dealing with extracts the exception message and stack trace and sends it as HTTP response. Is Expound supposed to work in that use case?


Yes, it should. For instance, building on my previous example, here is code to catch the instrumentation exception and return the message as a string: (try (foo "") (catch Exception e (.getMessage e)))


Another option is to use Expound to validate incoming data directly. For instance, let’s say the HTTP endpoint takes some JSON as incoming data


Then you could validate that data with something like:

(binding [s/*explain-out* expound/printer]
  (s/explain-str :my-app/http-data-spec http-data))


By default, Expound writes out all the Clojure specs that failed, but there is an (undocumented, but soon to be documented) option to omit this


if you’re interested in that, I can give some example code


If anyone else is interested, the issue was the dynamic var *explain-out* is only set in the current thread, and servers like HTTP Kit spawn new threads for requests. The fix is to replace (set! s/*explain-out* expound/printer) with (alter-var-root #'s/*explain-out* (constantly expound/printer))


there a better way of having a generator that just executes a function with no regard to the input? this does what i want, but notice the argument here is unnecessary #(gen/fmap (fn [_] (utils/uniform 1e12 1e13)) (s/gen pos-int?))


I was going to suggest gen/return but do you even need the output of the pos-int? generator?


for example, doesn’t matter what the previous generator is doing if you’re just constantly returning the same thing regardless of input

(gen/sample (gen/fmap (constantly -1) gen/pos-int))
=> (-1 -1 -1 -1 -1 -1 -1 -1 -1 -1)


this is a nonstandard generator construction The "right way" would be to use combinators somehow rather than calling a function that supplies its own randomness. That might be a bit more effort, depending on what you want. If you aren't doing things the "right way", then there's not really a better way.


do you really need a uniform distribution between two doubles?


(gen/double* {:min 1e12 :max 1e13}) would not be very nonuniform I don't think


@U3DAE8HMG yeah my point was i didn’t need the output of the pos-int? generator. gen/return was EXACTLY what i was looking for, thanks 😉


@U0GN0S72R yeah i need uniform distribution between two large numbers because made a spec that expected a number beteween those two ranges and this issue causes it to fail without a custom generator


i also stumbled upon #(gen/choose 1e12 1e13) that does uniform distribution


that ticket is interesting


@U0DHHFEDP what is your spec for exactly?


I'm having trouble seeing why something would fail


@U0GN0S72R it’s a ghetto spec for a unix timestamp in ms, the generator fails because it attempts to make ints starting at very low numbers. here’s the code

(s/and pos-int?
          (s/int-in 1e12 1e13))


it passes most the time when ran by itself but i have it nested in another datastructure and it fails 100% of the time


oooooh; this is an s/and issue, not an s/int-in issue


if you swap the order of those two you should have it succeed


or set the generator to be the default generator that the s/int-in expression generates


I think CLJ-2179 is a red herring here


tried swapping the order, didn’t work


i think the issue is legit, if you try running the code (gen/sample (s/gen (s/int-in 0 100))) you get a ton of low numbers


(0 0 0 1 0 4 0 52 3 34)


things like this


my final code works well enough though (s/with-gen (s/and pos-int? (s/int-in 1e12 1e13)) #(gen/choose 1e12 1e13) ))


sorry for formatting 😉


@U0DHHFEDP getting low numbers that are in the range is one thing, but I thought you were describing getting numbers that were out of range


@U0GN0S72R that’s what I thought was going on, i think my issue is that i was plugging in doubles using the short notation. It seems (s/int-in) doesn’t cast those for you but will verify that the input is in range, this is why I needed to pair it with pos-int


;; Fails reliably
(s/def ::test2 (s/and pos-int? (s/int-in 1e12 (- 1e13 1))))

;; Fails sometimes
(s/def ::test3 (s/and pos-int? (s/int-in 1e9 (- 1e10 1))))

(s/def ::works (s/int-in (encore/as-int 1e13) (encore/as-int (- 1e14 1))))


bumping the numbers up in the first example makes it fail a lot more often, I guess pos-int is the only thing generating numbers here?


in any case was my fault for plugging doubles in something that is clearly for ints 😉


yeah, when you use s/and the generator is taken from the first spec and the remaining specs are used to filter