Fork me on GitHub
Alex Miller (Clojure team)01:07:00

Namespaced keywords should be fine in edn

Alex Miller (Clojure team)01:07:34

Autoresolved keywords are not part of edn though (as there is no namespace context)

Alex Miller (Clojure team)01:07:05

Do you have an example?


What exactly does calling spec with a map do? map-spec-impl suggests I can do that, but spec’s docstring only mentions predicates and regex specs; I guess a map can behave like a predicate since it’s an IFn?


Is there an easy way to get a keys that’s not open for extension? I’m digging into map-spec-impl and just extracting the generator bit isn’t super trivial.

Alex Miller (Clojure team)02:07:57

you can s/and with a restriction on the key set


yeah; I got that part; trying to fix the generator though


I guess I can fmap over the gen I get from keys, and then dissoc the noise it comes up with I don’t want


(I understand the general argument for having extra keys; it just doesn’t make sense for what I’m doing)


@alexmiller: Thanks! Much appreciated 🙂

Alex Miller (Clojure team)02:07:15

yeah, def leverage the existing specs as much as possible for gen


When using with-gen, is there some kind of automatic spec verification, or are generators assumed to be correct


My excl-keys macro ostensibly works, but merging it doesn’t seem to.

(defmacro excl-keys
  "Like [[s/keys]], but closed for extension."
  [& {:keys [req-un opt-un req opt] :as keys-spec}]
  (let [bare-un-keys (map (comp keyword name) (concat req-un opt-un))
        all-keys (set (concat bare-un-keys req opt))]
    `(let [ks# (s/keys [email protected](apply concat keys-spec))]
         (s/and ks# (fn [m#] (set/subset? (set (keys m#)) ~all-keys)))
         (fn [] (gen/fmap (fn [m#] (select-keys m# ~all-keys)) (s/gen ks#)))))))
(s/def ::a (excl-keys :opt-un [::b] :req-un [::c]))
(s/def ::b string?)
(s/def ::c keyword?)
(gen/sample (s/gen ::a))
(s/def ::x (excl-keys :opt-un [::y] :req-un [::z]))
(s/def ::y integer?)
(s/def ::z rational?)
(gen/sample (s/gen ::x))
(s/def ::mix (s/merge ::a ::x))
(gen/sample (s/gen ::mix))
Samples for ::a, ::x work fine, merging less so. I guess I’ll dive into how s/merge builds generators 🙂


Hey guys, I've been working on a patch to port test.check/let to clojure.spec.gen


Since we all have fmap blues from time to time.


Here's the unit-test vector I want to push to clojure.test-clojure.spec:


(deftest gen-let-macro
  (s/def ::a (s/spec keyword?))
  (s/def ::b (s/int-in 7 42))
  (let [t (s/tuple keyword? string?)
        m (s/keys :req [::a ::b])]
     (are [spec generator]
        (s/valid? spec (gen/generate generator))
        t (gen/let [x :abc, y "efg"] [x y])
        t (gen/let [x ::a, y string?] [x y])
        t (gen/let [[_ y] t] [::a y])
        t (gen/let [] [keyword? string?])
        t (gen/let [] t)
        t (gen/let [[x y] (gen/let [] t)] [x y])
        m (gen/let [{a ::a, b ::b} m] {::a a, ::b b})
        m (gen/let [a keyword?] {::a a, ::b ::b})
        m (gen/let [] {::a keyword?, ::b 7})
        m (gen/let [] {::a keyword?, ::b #{7 8 9}})
        int? (gen/let [i 'clojure.test.check.generators/int] i)
        #{'abc} (gen/let [x 'abc] x))))


That test vector should give a pretty good idea of how gen/let would work.


My idea is that it's really annoying having to coerce specs, predicates and constants to generators so it just does it automagically


I'll wait for @alexmiller to tell me if he likes it or not before I attempt a patch


hi there! how to specify :ret value for fdef if function returns void?


:ret nil? or :ret #(nil? %) ?


I think :ret nil? but I'm not 100%


#(nil? %) is probably wrong


@alexmiller: Since you're a category theory fan, I could generalize let from generators to specs if we model specs as having type (a -> Bool, Gen a) (á la Haskell)

Alex Miller (Clojure team)20:07:19

@mike1452 either will work, I'd use the shorter one

Alex Miller (Clojure team)20:07:41

@lvh re with-gen, the generator is not trusted and all generated samples will be verified by checking the spec as well

Alex Miller (Clojure team)20:07:20

@mike1452: sorry read further back - functions are never void in Clojure, only comes up in some interop cases