Fork me on GitHub
#clojure-spec
<
2016-07-10
>
jjcomer12:07:15

@donaldball: I get that error when I execute in a deftest.

donaldball15:07:27

I get it both when executing in a deftest and in cider, though it works fine from a repl

donaldball15:07:42

I was just experimenting with ignoring Rich’s advice and expressing specs for strings as spec regexes. I find I actually quite like a couple of things that fall out: the form is easier to understand that the string regex, and you get a generator for free.

donaldball15:07:26

But unless I’m missing something, you pretty much have to write one spec for the seq of chars and another spec for the string that unwraps it as a seq

donaldball15:07:24

Setting aside the merits of the idea, is there a way I could do with s/and and a conformed value?

skynet18:07:54

hey all, I'm trying to figure out if I can use spec for parsing strings (sentences) into structured data. I'm struggling with whether or not I will need to split around spaces, and how to handle things like phrases (sequences of words) if I use s/cat. is this kind of use case intended at all, or am I better off with something else?

xcthulhu19:07:10

How do I write my own generator for cljs.spec?

xcthulhu19:07:26

I have data structures like {0 0.0, 1 0.1, 2 0.2, 3 0.3, 4 0.4, 5 1.0}

xcthulhu19:07:41

(maps of integers from 0 to 5, increasing in value as the index increases, where one of the values must be 1.0 and all of the values must be within [0,1])

xcthulhu19:07:06

I can generate these things with this function:

(defn generate-random-map
  "Return a random structured map"
  []
  (let [values (sort (repeatedly 6 rand))
        pivot-value (last values)]
    (into {} (map-indexed vector (map #(/ % pivot-value) values)))))

xcthulhu20:07:08

I'd rather not rope in test.check. Too bad that clojure.spec doesn't let you register generators separately from specs

xcthulhu20:07:01

Because I wouldn't mind associating a generator with my specs in my unit tests where I do use test.check

xcthulhu20:07:16

I think you can do this with just just spec since it gives you 'cat' and 'fmap'

gfredericks20:07:28

xcthulhu: I think clojure.spec is designed with the intention that you rope in test.check for non-trivial generator needs

gfredericks20:07:46

is it just that you don't want test.check required in a non-test namespace?

xcthulhu20:07:25

But like I said, they do give you enough so you don't need test.check, it's just clumsy

gfredericks20:07:57

where's fmap?

gfredericks20:07:23

clojure.spec.gen/fmap?

gfredericks20:07:51

you could probably rewrite my code using clojure.spec.gen if that's what you really want

gfredericks20:07:34

looks like gen/shuffle isn't there

henrytill20:07:24

@xcthulhu: aren’t you already pulling in test.check as a transitive dep if yr using something the gen namespace?

gfredericks20:07:47

not necessarily

gfredericks20:07:11

since it's lazy loaded you can get away with not having test.check available if you never run the generators/tests

gfredericks20:07:22

which I think is the whole point of the lazy loading

henrytill20:07:28

ok, makes sense

xcthulhu20:07:44

Or cljs.spec.impl.gen for the brave

gfredericks20:07:03

I wonder how they do lazy loading in cljs

gfredericks20:07:55

a custom var deftype

xcthulhu20:07:00

Yeah, it's pretty hacks. Anyway, if I read this right they lazily load vector from test.check. Sadly, clojureScript doesn't give you double*, only double

xcthulhu21:07:30

Anyway, thank you I see how to do this crazy thing now

xcthulhu22:07:48

Here's the generator I ended up going with BTW:

(gen/fmap
   (fn [values]
     (let [max-value (apply max values)
           min-value (apply min values)]
       (->> values
           sort
           (map #(-> % (- min-value) (/ (- max-value min-value))))
           (drop 1)
           (zipmap (range)))))
   (gen/vector
    (gen/such-that (complement #{infinity, (- infinity)}) (gen/double))
    7))

xcthulhu22:07:02

This hacks around the fact that clojurescript doesn't have double*

xcthulhu22:07:11

infinity is defined to be Double/INFINITY and js/Number.INFINITY in the jvm and js respectively

gfredericks22:07:29

that should still give you nans though

gfredericks22:07:45

gen/double generates nans

xcthulhu22:07:16

Not with high probability, but I'll go add a restriction on to the such-that

gfredericks22:07:30

should be high enough to matter

gfredericks22:07:33

unless I screwed something up

gfredericks22:07:27

roughly 1% of the time

xcthulhu22:07:10

Here we go:

(defn nan?
  "Tests if a value is a NaN or not"
  [x]
  #?(:clj (and (double? x) (.isNaN x))
     :cljs (js/isNaN x)))

;;;;;;;;;;;;;;;;

(gen/fmap
   (fn [values]
     (let [max-value (apply max values)
           min-value (apply min values)]
       (->> values
           sort
           (map #(-> % (- min-value) (/ (- max-value min-value))))
           (drop 1)
           (zipmap (range)))))
   (gen/vector
    (gen/such-that (complement (some-fn #{infinity, (- infinity)} nan?))
                   (gen/double))
    7))

xcthulhu22:07:35

Hopefully this yak has been shaved by now