Fork me on GitHub
#clojure-spec
<
2016-12-09
>
yenda12:12:30

Hi, could someone help me understand why in the above code generating ::engine-variable takes a split of a second and test/check runs forever until hitting a GC overhead ?

yenda12:12:01

I even tried

(binding [s/*recursion-limit* 1]
    (stest/check `convert {:num-tests 1 :max-size 1}))

morrifeldman12:12:44

I'd like a constraint that only allows one branch of an or in a derived spec. Is this possible? Here is an example of what I'm trying to do.

(s/def ::an-int-or-string (s/or :int int? :string string?))
 
  (s/def ::my-map (s/keys :req [::an-int-or-string]))

  (s/def ::my-map-string (s/and ::my-map
                               #(string? (::an-int-or-string %))))

mpenet14:12:45

am I awful thinking about writing an (with-ns ...) macro that does create-ns + in-ns + restore proper ns once out of scope ?

mpenet14:12:46

getting tired of writing super verbose specs preceeded by (alias baz (create-ns 'foo.bar.baz))

mpenet14:12:45

(with-ns 'foo.bar.baz
  (s/def ::a string?) ;; ::foo.bar.baz/a
  )

mpenet14:12:40

it could be a 2 arg version of in-ns otherwise

bronsa14:12:23

@mpenet that won't work

bronsa14:12:31

the reader will still resolve ::a in the current ns

bronsa14:12:19

you can't change ns context & use it in the same expr

bronsa14:12:33

need to be 2 separate exprs

mpenet14:12:40

that's too bad

bronsa14:12:41

(not even do would work)

mpenet14:12:12

It'd require reader hackery then.

mpenet14:12:44

the syntax is quite appealing, it would really save a ton of boilerplate

bronsa14:12:51

you could get e.g. (do (in-ns 'foo) ::foo) to work with reader hackery but no way to get (with-ns 'foo ::foo) to work w/o introducing placeholder objects & resolve aliases at compile time

bronsa14:12:38

realistically, neither is doable

mpenet14:12:04

Well I might give it a try once I am done with the giant schema -> spec port I am into atm. Sounds fun nonetheless

lvh16:12:12

@alexmiller Are you a good person to report suggested stdlib macro specs to? If so, I submit http://dev.clojure.org/jira/browse/CLJ-207 for consideration — I just got bitten by that; was expecting :let in the first position to work (kinda feel it should anyway, but OK — if it doesn’t the error message could be a lot more descriptive, and that seems like the kind of thing spec can help with here, plus you’re probably already speccing what for looks like)

alexmiller16:12:50

jira is a good person to report suggestions to :)

lvh16:12:06

Does that go on the ticket?

lvh16:12:10

Or do I re-tag it spec?

lvh16:12:14

or is that a new ticket

alexmiller16:12:16

I’d file a new enhancement ticket

alexmiller16:12:20

and point to this one

lvh16:12:25

cool, thanks

alexmiller16:12:29

requesting a spec for for

yenda16:12:38

I am trying to validate data after edn/parse-string, the field above doesn't validate with the predicate #{true false} even though (s/valid? ::boolean (java.lang.Boolean. "true")) does :edit actually it does not for "false", only for "true"

minimal16:12:27

use boolean?

yenda16:12:12

poor me I'm still on clojure 1.8 😞

yenda16:12:27

but yeah I can just add the boolean? predicate source to my spec

yenda16:12:22

nice thanks ! didn't think it was also handling this

yenda16:12:02

that is so wierd I add a test/check working before and now it throws an exception cider.nrepl.middleware.test$report cannot be cast to clojure.lang.MultiFn. I tried :monkeypatch-clojure-test false in lein profile but it doesn't change anything

yenda17:12:22

I am trying to figure out what is causing this, it happens both in clojure 1.8 and 1.9

yenda17:12:33

if I try to run the test right after jacking-in I get the error, if I recompile only the test one or more time and run it it works, if I recompile the whole namespace I get the error even if I make it work once before

yenda17:12:41

Ok I found the problem I had to explicitely require [clojure.test.check] in my test namespace

alexmiller18:12:06

@yenda note that in Clojure we use only the canonical Boolean/TRUE and Boolean/FALSE for true/false values so you should never call the Boolean constructor. This is in particular a problem with false as the non-canonical false value will be treated as true (although you’ll see subtle weirdnesses with the non-canonical true value too).

(false? (Boolean. “false”)) ;; false
 (false? (Boolean/FALSE)) ;; true

yenda18:12:20

Thanks for the tips

alexmiller18:12:11

it’s pretty subtle and can definitely be a gotcha in certain Java serialization / interop cases

dm321:12:59

is unform supposed to handle nested values?

dm321:12:26

boot.user=> (s/def :a/test-map (s/map-of :a/key :a/val))
:a/test-map
boot.user=> (s/def :a/key keyword?)
:a/key
boot.user=> (s/def :a/val (s/or :str string? :int int?))
:a/val
boot.user=> (s/unform :a/test-map (s/conform :a/test-map {:x "a", :y 1}))
{:x [:str "a"], :y [:int 1]}

alexmiller22:12:43

that should work, but it is buggy

alexmiller22:12:50

I actually wrote a patch for it this morning

lvh22:12:31

Is there a way to introspect the generated samples for fspecs? I’m trying to see what sort of stuff it comes up with now to see if it’s worth coming up with a more specific (and ideally: faster to generate) spec

lvh22:12:13

specifically right now I have a HOF that takes forever to test (for good reason)

lvh22:12:28

(s/def ::variant
  (s/fspec
   :args (s/cat :input string?)
   :ret (s/coll-of string?)))

(s/fdef
 comp*
 :args (s/cat :vs (s/+ ::variant))
 :ret ::variant)

lvh22:12:34

So I’m guessing coming up with lots of ::variants, and then checking if a bunch of combinations of them is still a variant… well, that’s going to be expensive 🙂 I’m not sure if I should just run the generative tests extremely sparingly, or work on producing faster ones (e.g. by helping the generator generate realistic variants faster — I have a bunch laying around anyway...)

alexmiller22:12:23

you might try changing the :ret spec to (s/coll-of string? :gen-max 3) to see if that helps

lvh22:12:10

(s/and string? #(<= 8 (count %) 11))

lvh22:12:14

yeah that’s not gonna be fast.

lvh22:12:44

there aren’t any fancy string specs yet, right? for printable or something?

alexmiller22:12:13

no, but lots of ways to create custom gens

lvh22:12:26

yeah. (fn [] (gen'/string-from-regex #”[a-z0-9]{8,11}")))) right now

lvh22:12:31

because I got lazy, mostly 😉

lvh22:12:59

(`gen’` is test.chuck)

alexmiller22:12:05

I guessed :)

lvh22:12:17

I figured; mostly for the benefit of other people 🙂

nwjsmith22:12:17

I’m having some modelling trouble with spec. Let’s say I’ve spec-ed a map :child/entity:

(s/def :child/entity (s/keys :req [:child/name]))
I want to have functions that only operate on stored versions of this entity, so I create a stricter version of the spec:
(s/def :child/stored-entity (s/merge :child/entity (s/keys :req [:child/id])))
This is working pretty good! I can write specs for stored and unstored entities (or both!). The trouble starts when I add a parent entity:
(s/def :parent/entity (s/keys :req [:parent/children]))
(s/def :parent/stored-entity (s/merge :parent/entity (s/keys :req [:parent/id])))
The :parent/stored-entity spec is incomplete. I can’t specify that its children are required to be :child/stored-entitys.

nwjsmith23:12:03

If I use a different key for a :parent/stored-entities children, it means that functions written to work with :parent/entitys won’t work with the :parent/stored-entity, which is a Bad Thing™.

alexmiller23:12:34

well in this case, you’d probably need stored-entity to be built from scratch rather than by restricting entity

alexmiller23:12:52

sometimes I feel like the right answer to these kinds of questions is to re-think the modeling approach though

nwjsmith23:12:19

I’m open to remodelling 🙂. I’m not sure how I could re-write :parent/stored-entity to solve this.

alexmiller23:12:43

I guess I would ask whether you really need to differentiate stored/unstored

nwjsmith23:12:45

I think I do. There are some functions that would need to ensure the entity is already on disk. An update-parent-in-place function would need it for example.

nwjsmith23:12:26

(that example probably counts as vulgarity on Clojurians)

bbloom23:12:47

spec lacks parameterized abstractions / generics / whatever - trying to encode that invariant is unlikely to work out - i recall some discussion of alternatives at some point in this room

bbloom23:12:52

one suggestion was just use two different keys

bbloom23:12:27

for example ::parent/stored-children and ::parent/children

bbloom23:12:00

what extra data do stored children have?

nwjsmith23:12:16

same thing, just a :child/id

bbloom23:12:18

could you have ::parent/children and ::parent/stored where the stored key contains a map of children ids to a stored boolean?

bbloom23:12:26

or timestamp?

bbloom23:12:47

see if you can come up with a representation that doesn’t change over time

bbloom23:12:58

where stored state ~= time

nwjsmith23:12:00

I’ve considered using something like Om/Datomic’s tempids

bbloom23:12:20

yeah, or symbols

bbloom23:12:46

you can always unfold a graph with symbols, ids, whatever

nwjsmith23:12:39

That way a parent always has an id, but it a stored one has a ‘permanent’ id.

nwjsmith23:12:53

K, I’m going to explore this a bit more and see what comes of it

bbloom23:12:18

yeah, try to model it openly where you can accrete data by adding more keys w/o “changing” the thing

bbloom23:12:22

this way some key always means the same thing

bbloom23:12:33

it will be easier to reason about and you’ll gain new features you didn’t even expect to have

bbloom23:12:11

for what it’s worth, i think “parameterized specs” may be useful, but i’m tempted to say we’re better off without them for as long as we can get away with it - if not forever

alexmiller23:12:18

Alternately, consider a separate predicate that checks whether all things are storeable

alexmiller23:12:35

That can be combined with the information