Fork me on GitHub
Drew Verlee13:10:58

I poked around with the idea of phrase and i’m not sure it make sense for what i’m doing. As @seancorfield suggested i can pull the information with explain-data. And if i want to reference something inside the spec it might make sense to pull it into a var to use across multiple domains rather then pulling it out via regex matching (which is what phrase seems to do). I’m would be interested to hear how something like phrase worked in a larger project, as it seems prescriptive.


did anyone create a web based spec browser thing?


@mpenet What’s your use case? Do you want to browse specs at dev time, or, say, publish specs online similar to API docs?


yep exactly


i know of but i d like to ship a browser based thing


I guess this could be adapted, but just asking in case somebody did that work already


Hm, not that I know of. On a related note, I wonder if it’s be useful to start a wiki that collects these spec helper libraries as a resource


@drewverlee I’ve been working on something like this which tries to map a human readable message by a problem’s spec keyword, and falls back to a symbolic predicate match (like Phrase) if it can’t. Trying to embloginate it now.

Drew Verlee15:10:03

I was thinking this over last night. I don’t think there is any generic relationship between a human readable message and the code. The only way i could see to do this that was better then plain old clojure would be some sort of semantic analysis of the function words. I feel like a neural net might be able to make something out of the function names and values. I was curious about phrase, but felt it wasn’t doing much for me that i couldn’t get from core spec and a mapping to human readable message. I’m curious about your work.


I suspect its there, but I’m not even close to finding it. My work is mostly a straight dispatch based on the problem’s spec keyword, falling back to a non-macro based Phrase implementation if that fails. And only the latter out of necessity. s/keys specs for example, generate problems that really require you to look at the predicate to know exactly what went wrong.


So the predicate dispatch is mostly to catch cases where :via just doesn’t tell you enough (in the s/keys case, I mean the exact field) about what went wrong.


I’m hoping that approach casts a wide enough net to handle most of the problems generated by our specs. Chopping the specs up, and def’ing each piece seems to help.

Drew Verlee15:10:08

I think dispatching on the problem spec’s keyword and maybe some context importer to the end user might make sense. The advantage to just handing them as data and passing them around is that you could organize your code around them.

Drew Verlee15:10:41

Is that in line with your thinking? I should get back to you tomorrow when im feeling better.


I think it is 🙂 Yeah, would love to talk more.


It’s in use now in a fairly large project, but was introduced only about a month ago. So still waiting to see how it pans out.


Not sure if this is the correct place to ask this question (if not, feel free to boot me to another channel!), but I recently stumbled across some performance related things with respect to spec generative testing specifically regarding every and the :kind and :into parameters. So the spec I had is loosely similar to this:

(s/def ::x (s/every string? :kind vector?))
(time (some-gen-testing ::x))

=> "Elapsed time: 93083.882115 msecs"

(s/def ::x (s/every string? :into []))
(time (some-gen-testing ::x))

=> "Elapsed time: 293.882115 msecs"
As you can see, :into is orders of magnitude faster than :kind for generative testing. My question is this: when should I be using :kind and when should I be using :into?

Alex Miller (Clojure team)16:10:22

Are you using latest spec? This problem has been fixed.

Alex Miller (Clojure team)16:10:39

I would recommend preferring kind if that’s sufficient


@alexmiller I think you may have linked the wrong JIRA


@alexmiller looks like there was a very fun dependency error with pedantic being enabled in leinengen. I was pulling alpha17 when my deps required beta2. Rerunning things I got the following (which looks acceptable to me):

(s/def ::x (s/every string? :kind vector?))
(s/def ::y (s/every string? :into []))
(s/def ::n nil?)

(defn xfn

(defn yfn

  :args (s/cat :x ::x)
  :ret ::n)

  :args (s/cat :y ::y)
  :ret ::n)

(time (tu/stest-w-report `xfn))
(time (tu/stest-w-report `yfn))


(time (tu/stest-w-report `xfn))
"Elapsed time: 974.417512 msecs"
=> true
(time (tu/stest-w-report `yfn))
"Elapsed time: 522.806027 msecs"
=> true


ie, off by half a second rather than minutes


many thanks for calling that out!


@alexmiller is that voluntary or a leftover ? just lost a bit of time tracking success! spurious messages in logs that where coming from here


the originating code was a bit weird anyway (just calling s/explain-data in a when-let to validate a piece of content and grad the error in one go), but maybe printing here is not something desirable


@mpenet that is what explain does, if you look at the code up above it as full of prs, the default for explain is to print stuff out


No prob with explain printing. Just found a bit odd that -data would

Alex Miller (Clojure team)16:10:17

You’ll get that if you ask for explain on something valid

Alex Miller (Clojure team)16:10:38

Which leads me to why you would do that... :)


The code in question is gone. But since there an if branch in that code for the printing in case of success maybe it's expected people would do that


yes that was the case, not very nice


I was not expecting it to trigger a print regardless


ex-data doesnt for instance


are you sure you aren't just calling explain, not explain-data?


Not in front of my computer anymore. I think it was -data, but not sure


Ah no it was explain, sorry about the noise 🙏 All good then

Stefan Roex18:10:08

I'm was wondering if the following is possible with clojure.spec, since I'm not sure if it's one of the use-cases it tries to solve. Let's say we have a user which can have two roles: (s/def ::role #{:admin :normal}) and a admin specific field which is only required when the user is an admin, not a normal user. I currently have: (s/def ::user (s/keys :req [::role] :opt [::admin-specific-field]), but that's only partially true, since the second key is required depending on the value of ::role. Anyone have an idea how to spec this? Thanks 🙂 🙂


I think there’s a multi-spec example in the spec guide very similar to that

Stefan Roex18:10:06

@taylor Thanks! Didn't catch that for some reason 😄


Hey, considering is the top two voted ticket for Clojure right now, I thought about this general issue for quite a while last year and as a result wrote a spec that, given a root spec, recursively parses specs into reversed dependency order, to do a spec rewrite to a new root, while replacing certain parts of the spec-tree. The latter algorithm isn't implemented yet, I put it aside for quite a while. It shouldn't be too much work but maybe I'd need to get tools.analyzer into the mix for rewrites of multispecs. And then there was Rich announcing better programmability of specs in his conj keynote. So the question is, is it still worth pursuing this? Any ideas about how deep the rabbit hole might get? I'm aware of some libs doing spec parsing into records etc. but for some reason I'd like to avoid that (you're welcome to convince me). Also I haven't looked into term rewriting at all, but maybe that's what I'm trying to do.