Fork me on GitHub
#clojure-spec
<
2016-12-14
>
gfredericks00:12:52

@ag that does a bunch of unnecessary shuffling on each generation

gfredericks00:12:32

I'd do (gmap/fmap #(clojure.string/join " " %) (gen/vector (gen/elements (clojure.string/split lorem-ipsum #" ")) 3 5))

gfredericks00:12:37

no need for a shuffle at all

ag00:12:11

so gen/elements guarantees that it would pull elements randomly? then yeah, makes sense

ag00:12:14

thanks!

ag00:12:00

damn it.. now I’m struggling with turning it into a spec

(def ^:private l-ipsum-gen
  (gen/fmap #(clojure.string/join " " %)
             (gen/vector (gen/elements (clojure.string/split lorem-ipsum #" ")) 3 5)))

(s/gen (s/with-gen (s/and string?) l-ipsum-gen))

ag00:12:07

doesn’t work

hiredman00:12:20

if gen/ is clojure.spec.gen then you need to wrap things in no argument functions

hiredman00:12:50

or, I should say, you need to wrap generators in

ag00:12:04

hmmm, ya, can’t figure out right syntax

ag00:12:57

ok… I think I’ve figured it:

(def ^:private l-ipsum-gen
  (gen/fmap #(clojure.string/join " " %)
             (gen/vector (gen/elements (clojure.string/split lorem-ipsum #" ")) 3 5)))

(gen/sample (s/gen (s/with-gen (s/and string?) (fn [] l-ipsum-gen))))

artemyarulin09:12:05

Hello. Where I should put specs: next to functions, next to tests, one global file with all specs for my project. Once I’ve seen [module-name]-spec.clj separate file for each module.

pyr10:12:28

HI! How do you go about generating related values?

pyr10:12:47

A simple case would be an indexed map where keys need to reappear in values

gfredericks12:12:11

@pyr are you familiar with gen/let and/or gen/fmap?

bronsa13:12:19

I was toying earlier with implementing split-with using spec

user=> (require '[clojure.spec :as s])
nil
user=> (defn s-split-with [pred?] (s/cat :left (s/* pred?) :right (s/* any?)))
#'user/s-split-with
user=> (s/conform (s-split-with int?) (range 1e5))
StackOverflowError   clojure.lang.RT.get (RT.java:751)

bronsa13:12:16

anything obviously "wrong" or is this stack overflow a real issue?

bronsa13:12:07

by contrast, if the pred fails early and the right branch is used, it doesnt overflow

Alex Miller (Clojure team)13:12:17

It's greedy so I'd expect all 1e5 to go left

bronsa13:12:35

e.g.

(s/conform (s-split-with ident?) (range 1e5))
returns fine

bronsa13:12:53

@alexmiller yes, I'd expect that to happen, not at stack overflow

Alex Miller (Clojure team)13:12:11

Yeah just thinking through

bronsa13:12:13

here's the overflow stack trace if that's helpful

Alex Miller (Clojure team)13:12:21

What if you don't have right?

bronsa13:12:02

it runs fine

bronsa13:12:24

(this is ok (s/conform (s/cat :left (s/* int?)) (range 1e5)))

bronsa13:12:54

uhm, so is (s/conform (s/cat :left (s/* int?) :right (s/* ident?)) (range 1e5))

bronsa13:12:08

but (s/conform (s/cat :left (s/* int?) :right (s/* int?)) (range 1e5)) overflows

Alex Miller (Clojure team)14:12:53

the regex derivative is keeping track of all possible solutions - in the last case the split could occur anywhere so there are a large number of those (the others do not have that ambiguity)

Alex Miller (Clojure team)14:12:37

so these are (algorithmically) very different specs

ddellacosta17:12:35

folks I’m a bit confused by something right now, and apologies if this is documented somewhere that I missed: If I have a spec keyword in namespace x (`(def ::foo …)`) and then I refer to it via a ref in a separate namespace (`[x :as y]`), I can’t seem to use that ref’ed keyword in a defmethod (`(defmethod foo :y/foo …)`)

ddellacosta17:12:45

does that make sense? Am I doing something obviously wrong?

josesanch17:12:27

This is not working because s/keys is a http://macro.Is there any way to do this?

bbloom18:12:49

josesanch: i don’t really think spec wants to be used in that way dynamically. i think it’s somewhat designed to ensure some things are static at useful times.... can you do get-mandatory-fields in a more static way and then validate with a dynamic spec NAME rather than a dynamic spec?

bbloom18:12:04

something like (s/explain-data (get-spec-name llambda) llambda))

bbloom18:12:36

otherwise, your choices are eval (probably not a good idea) or macros (which are going to be static anyway)

josesanch18:12:01

Thank you @bbloom. I was thinking about that. In this case I'm validating each field individually

bbloom18:12:48

in that case, i suggest using the per-field specs (which presumably already exist?) and a simple loop or map or reduce on top

bbloom18:12:07

ie do custom validation in addition to the spec validation

bbloom18:12:17

but others may have other suggestions ¯\(ツ)

josesanch18:12:10

Yes. This way. Thank you very much.

bbloom18:12:06

nice. this is one of the great features of spec vs a traditional type system: you can use it as a building block for other dynamic validations

Alex Miller (Clojure team)18:12:29

also, (s/keys) will validate all keys

Alex Miller (Clojure team)18:12:55

you could combine that with a check for whether a map contains all the required keys

Alex Miller (Clojure team)18:12:11

that could be a custom predicate that built from a set of keys

bbloom18:12:49

hm, are you saying like you’d make a wrapper object {:required #{::foo ::bar}, :object {::foo … ::bar …}} and then validate that with a custom predicate?

Alex Miller (Clojure team)18:12:22

I’m saying you could (s/and (s/keys) #(every? req-keys-set (keys %)))

Alex Miller (Clojure team)18:12:31

I probably typoed that, but you get the idea

ddellacosta18:12:59

@hiredman yeah I figured it out after a little while…PBKAC. Thanks!