Fork me on GitHub
#clojure-spec
<
2016-07-07
>
seancorfield01:07:45

I thought alias had been updated so you didn’t have to have a real namespace for the second argument?

seancorfield01:07:40

It seems not. I was hoping to use alias to shorten a lot of qualified keywords (without having to actually load a namespace).

Alex Miller (Clojure team)02:07:41

no, we’re just considering that

seancorfield02:07:13

Now we’re beginning to use namespace-qualified keywords a lot more, I can say that would be a very convenient addition! 🙂

seancorfield03:07:49

That’s an interesting hack! 🙂

mandragoran06:07:28

I was wondering how I would spec an async function (a function returning a core.async channel). Would I need to write an async version of fdef or is there another way?

wagjo08:07:08

I'm a bit struggling to choose between :foo.vocabulary/term-uri and :foo.vocabulary.term/uri or just :term/uri. I know it's probably a bit soon, but any best practices for choosing the name(space)s?

mandragoran09:07:21

@wagjo: I've been taking advantage of the new ::alias/some-keyword feature. So I'd probably call it ::term/uri and map the term alias in all namespaces using it to foo.vocabulary.term.

vikeri10:07:11

How can I reference the first argument of a function in fdef :arg when the argument does not have a name since I’m destructuring it immediately with {:keys [a b]}?

vikeri10:07:12

nvm, realized the keywords in :arg (s/cat are not tied to the variable names

Alex Miller (Clojure team)11:07:30

@wagjo if you're in a lib or other public project, you should add enough context to be distinguishable from anyone else (something like your maven group/artifact id). If in a private app, I would probably do less just for typing sake. I think it will be useful to put specs for the domain in a small number of namespaces (maybe 1). It might be useful to separate generic specs from domain attributes (which alias them) too.

Alex Miller (Clojure team)11:07:42

@mandragoran: we will probably have some async related specs eventually. Until we do I think it's better to do type checks on ReadPort and WritePort than specifically on ManyToManyChannel as those are more generic

gfredericks12:07:54

ad-hoc higher-order specs?

lvh12:07:07

How do I write a spec for a map of unqualified keywords where the keyword itself is something dumb that’s going to collide a lot? In particular, swagger (the api format) has a toplevel key, also called “swagger”.

Alex Miller (Clojure team)13:07:23

can you give an example? not getting the question.

lvh14:07:04

Sure! Let’s say I’m parsing swagger

lvh14:07:36

(def sample-swagger
  {:swagger "2.0"
   :info {:title "my sample api for puredanger"
          :version "elventy flive"}})

lvh14:07:50

Let’s say I’m in a ns defining swagger in general (doesn’t matter much, could also be :swagger/whatever). I’m doing something like:

(spec/def ::swagger
  (spec/keys :req-un [:swagger/swagger
                      ::info]))

(spec/def :swagger/swagger
  ;; The top-level key is called swagger. This feels dumb.
  #{"2.0"})

(spec/def ::info
  (spec/keys :req-un [::api-title
                      ::api-version]))

(spec/def ::api-title string?)
(spec/def ::api-version string?)

lvh14:07:44

It seems like I’m naming :swagger/swagger what it is because :req-un expects to find the names of map keys that way, but having a ::swagger and a :swagger/swagger looks confusing

lvh14:07:32

Similarly, ::api-title actually needs to be : or something, but the tons of ns’es make it annoying to type

lvh14:07:50

I guess I’m asking if there’s a way to distinguish between the key under which something appears in a map, and its spec name

lvh14:07:30

IIUC there are some new reader macros in the current alpha, I don’t know if they help with this

lvh14:07:53

they might limit the typing to just the spec defs so at least the destructuring looks good, I suppose?

lvh14:07:09

if I had a choice, in this code, I would refer to that specced value (esp. after conform) as ::api-title — the only reason I wouldn’t do that is because the JSON I’m getting from swagger (a standard, so I can’t change it) uses keys that don’t collaborate well with that

lvh14:07:07

If I could provide, say, an alias, or say that under key :title I’m expecting to see spec ::api-title or something, that’d help too

lvh14:07:20

bailing out and using a fn to just assert that some keys are some particular value doesn’t feel very idiomatic either

lvh14:07:23

I guess you can define specs in terms of other specs? That doesn’t seem to help a lot but I’ll play with it

donaldball15:07:20

Has anyone run across or is writing a lib of commonly useful strong specs, e.g. for urls, hostnames, unix ports, valid sql identifiers, etc.?

Alex Miller (Clojure team)15:07:18

I haven't seen anyone doing so yet but it seems like a reasonable and perhaps inevitable thing to do

Alex Miller (Clojure team)16:07:35

@lvh aliasing is a good and useful thing, so creating api-title and api-version are just fine as base specs (you may want to use some other namespace though like :swagger/api-title or :swagger.api/title), then s/def code-localized versions to match the unqualified attributes that alias those base specs, like (s/def ::title :swagger/api-title)

lvh16:07:21

OK, so I define the “real” spec under :swagger.ap/title, and then a local alias. Makes sense. Thanks!

Alex Miller (Clojure team)16:07:26

if you create the keyword namespace as an actual namespace, you can alias it, then use the alias in the autoresolved keyword like ::sw/api-title (after something like (alias ‘sw ‘swagger.api))

Alex Miller (Clojure team)16:07:00

the key there is that alias currently has to refer to a real (loaded) namespace. That might change still but a workaround is what you see here https://github.com/clojure/clojure/blob/master/src/clj/clojure/spec/test.clj#L17-L19

Alex Miller (Clojure team)16:07:22

switch to (and thus create) the namespace, then switch back, and alias

leongrapenthin17:07:38

Today we wanted to create a spec that validates a percentage in five percent steps. It occured to us that int-in doesn't support a step argument like range does. Has this been discussed or is a ticket worth a shot?

sparkofreason17:07:54

Before I get too far in reinventing the wheel, is anyone else out there working on converting spec to Datomic/Datascript schema?

leongrapenthin17:07:08

@dave.dixon: I have thought about it and realized that generating a spec from Schema is likely more desirable

donaldball17:07:05

I’m not sure what I’m doing wrong with this simple spec:

donaldball17:07:07

(s/def ::foo (s/spec #(= :foo %) :gen (gen/return :foo)))
:user/foo
user> (s/gen (s/get-spec ::foo))
ClassCastException clojure.test.check.generators.Generator cannot be cast to clojure.lang.IFn  clojure.spec/spec-impl/reify--13361 (spec.clj:800)

donaldball17:07:10

Oops, I misread the contract for s/spec, carry on

leongrapenthin17:07:11

@donaldball: Wrap (gen/return ...) in a (fn [] )

Alex Miller (Clojure team)17:07:24

yep, or put # at the beginning

Alex Miller (Clojure team)17:07:41

(s/def ::foo (s/spec #(= :foo %) :gen #(gen/return :foo)))
(gen/sample (s/gen ::foo))

leongrapenthin17:07:07

@alexmiller: Yeah, but that's gonna be quite the error message 🙂 - We came up with (s/and (s/int-in 0 101) #(zero? (mod % 5))) if I remember correctly. Generator worked. Projecting this scenario on per permille or larger spaces was what made us wonder why a step is not supported.

Alex Miller (Clojure team)17:07:10

because it’s a min/max check, and you’re specifying enumerated values. that’s just not what it is intended to do.

Alex Miller (Clojure team)17:07:38

I think your spec is better anyways

Alex Miller (Clojure team)17:07:01

you could combine int-in with it too

Alex Miller (Clojure team)17:07:15

nvm, you’re doing that

leongrapenthin17:07:06

Yeah, the reasoning makes sense

Alex Miller (Clojure team)17:07:17

the hard part of int-in is generating uniformly across a well defined set and you’re leveraging that by building on top of it

leongrapenthin18:07:10

Looking at that from a different perspective it is only enumerated because of the min-max check

leongrapenthin18:07:34

You could argue that everything in a min-max interval is enumerated then

Alex Miller (Clojure team)18:07:48

well enumerations have obvious usage issues when there are a lot of values :)

leongrapenthin18:07:19

My case is a very specific enumeration and can't be discarded as just any enumeration. Every step range of allowed values is a subset of an integer range with the same boundaries.

leongrapenthin18:07:13

So from that perspective I think it still makes sense as a feature of a min/max check.

leongrapenthin18:07:43

The min/max check is also an enumeration with a step of 1, then.

leongrapenthin18:07:53

Better said, for our usecase it would be preferable if it was an in-range check than a min/max check with the word range meant in the same way as in clojure.core/range

Alex Miller (Clojure team)18:07:21

well, range does a lot of stuff (reverse ranges, infinite ranges, half-open ranges, support for all number types, etc)

Alex Miller (Clojure team)18:07:23

in-range implies to me a check that would need to enumerate the range to determine conformance and gen would be a lot harder.

Alex Miller (Clojure team)18:07:37

int-in is intentionally much narrower

leongrapenthin18:07:36

Agreed. I think what I want to say is that int-in as a name doesn't necessarily limit the spec to a min/max check

leongrapenthin18:07:45

A fully featured in-range is likely an overkill for the rare amount of usecases

leongrapenthin18:07:02

Thanks for discussing. Having thought about it I won't be able to resist to submit a small patch over the weekend for reconsideration, though 🙂

Alex Miller (Clojure team)18:07:10

I think Rich is unlikely to ok that

Paco18:07:51

I am running stest/check on an fdef that never finishes. It just keeps going and I don't know If its stuck or what is the deal. I have independently tested the input and output specs for possible problems and am getting nowhere. Any tips on how to debug this?

Paco18:07:12

exercise-fn seems to work fine

leongrapenthin19:07:30

@kidpollo: Isolating the problem by starting with dumbed down specs and adding until the problem occurs?

Paco19:07:44

I also tried (s/exercise-fn my-fn 100) and it takes like 1 min but it works.

leongrapenthin19:07:25

@kidpollo: Probably recursion in s/map-of or every-kv?

leongrapenthin19:07:52

Might be that it just takes so long to print the result because it prints a huge generated value

Paco19:07:10

Could be.

Paco19:07:24

I am running on the repl

Paco19:07:31

on intellij

leongrapenthin19:07:06

You could eliminate this quickly with setting print-length

Alex Miller (Clojure team)19:07:18

dump stack and see what it’s doing

Alex Miller (Clojure team)19:07:57

I think intellij has a button for it

Paco19:07:19

ohh let me see

Alex Miller (Clojure team)19:07:47

I guess maybe that’s only in the debugger mode

Alex Miller (Clojure team)19:07:28

(map println (.dumpAllThreads (java.lang.management.ManagementFactory/getThreadMXBean) false false))

Paco19:07:06

yah I was able to dump them in debug mode

Paco19:07:33

just see a bunch of locked threads in generators

Paco19:07:11

damn 700% cpu usage

Paco19:07:35

I guess i need to find what spec is going crazy. Start with simpler specs maybe? The input and output to this fn are multispecs. The maps are not so complicated maps.

Paco19:07:51

@alexmiller @leongrapenthin the problem seems to be happening in alpha-9. I went back to alpha 8 and the test finishes. It takes a while but it finishes

Alex Miller (Clojure team)19:07:44

I think check changed from 100 to 1000 iterations in alpha9

Paco20:07:00

oh my that would explain it

Paco20:07:38

when it produced errors it did end earlier

Paco20:07:11

I guess Ill leave it over night to see how long it actually takes

Alex Miller (Clojure team)20:07:19

That count is an option you can pass to check though iirc

Paco21:07:02

what is iirc?

jr21:07:48

(if I recall correctly)

Paco21:07:52

nice! snme in spanish 😛 (si no me equivoco)

Paco21:07:14

lol ^ totally does not exist in spanish 😛

Paco21:07:32

It seems it would look like:

Paco21:07:01

Is it encouraged that all clojure projects start using fully qualified keywords?

Alex Miller (Clojure team)21:07:22

Nothing categorical. Qualified keywords have utility - if they make sense for you, use them

Alex Miller (Clojure team)21:07:05

I think spec and the other syntax changes have increased their utility and decreased their verbosity, maybe changing that equation

danielcompton21:07:13

And their visibility 🙂

Alex Miller (Clojure team)21:07:58

Well I don't think that changes their utility :)

seancorfield21:07:01

I can see us (World Singles) using qualified keywords very heavily as it will make some of our code much more explicit. Hence the recent changes to java.jdbc to make it easier to get qualified keywords back in result sets (the new :qualifier option to everything that produces result sets).

ajss22:07:50

In spec/fdef, is there any way to make one element of :args depend on another? I've got a function that takes two maps, but wants the keys of each map to be the same.

Paco22:07:03

you can define the relationship between inputs and outputs in :fn

glv22:07:50

Right, but not constrain inputs based on relationships.

glv22:07:36

(For example, I have functions that take a grid and a coordinate within that grid, and obviously the coordinate must be within the bounds of the grid to be valid. But I don’t think there’s a way of expressing that.)

Paco22:07:44

Sounds similar to the ranger-rand example in http://clojure.org/guides/spec#_spec_ing_functions

Paco22:07:18

instead of using :ret just use named args in :args

ajss23:07:38

so (s/and (s/cat :a map? 😛 map?) #(= (keys (:a %)) (keys (:b %))))?

ajss23:07:16

ah - ": b" = 😛

ajss23:07:24

will run out of generator iterations won't it?

Paco23:07:48

well you would need to replace map? with a more "narrowed down" spec

ajss23:07:28

ah, yes that would work. Thanks 🙂

Alex Miller (Clojure team)23:07:49

@glv you can s/and an arbitrary predicate in the :args spec

Alex Miller (Clojure team)23:07:51

and flows conformed values so it should already be nicely destructured if you add it last

glv23:07:35

Ah, gotcha. I’ll try to incorporate that into my specs when I get a chance.