Fork me on GitHub
Garrett Hopper12:10:11

During development, does it makes sense to use fdef and instrument for all my functions or sprinkle my functions with assert and old enable assertions during development? Perhaps a combination of the two?

Garrett Hopper12:10:21

I guess the benefit of fdef is that it can be in a separate namespace; is that recommended?


I think this depends on personal preference and whether you’re writing something intended to run on Clojure 1.8. I tend to put fdef next to the function definitions, but keep other specs in spec-specific namespaces

Garrett Hopper13:10:01

Hmm, ok. Thanks

Garrett Hopper13:10:08

Assuming pre-1.9 compatibility isn't a concern, do you think having them together is ideal?


personally I like to see the fdef right next to the function itself :man-shrugging:

Garrett Hopper13:10:19

Thanks 👍:skin-tone-2:

thumbsup_all 4

You may already know this ,but it’s perfectly fine to put the fdef definition above the function definition. I find this much more readable than putting it below the function.


(sometimes people don’t realize this is a valid way to organize their specs)

Garrett Hopper14:10:55

Ah, interesting. :)

Garrett Hopper13:10:21

It seems like that would defeat some of the namespace'd keyword designs of spec though.


@alexmiller hi! We found strange flawing bug for clojure.spec lib in multithreaded environment. We use latest clojure.spec versions ("0.2.176" "0.2.44") for request validation. Every http request we translate to clojure map, then we call (s/valid? spec input-request-map) for input validation. We have 300 000 sample requests (saved in edn file) for testing (constant requests). If we use one thread on JMeter to send request then we always have result true for (s/valid? spec input-request-map) for every 300 000 constant requests. if we use 2 threads or more on JMeter to send requests then sometimes we have false result. If we save false map to a file and then try to validate it (s/valid? spec input-request-map) then we see that it is always true. If we validate twice in multithreaded env (or (s/valid? spec input-request-map) (s/valid? spec input-request-map) ) then it works (but logs tell us that one of s-exps inside or sometimes false). All map is standard clojure maps (immutable). The code (or (s/valid? spec input-request-map) (s/valid? spec input-request-map) ) works for us as workaround but we can't watch on it without crying. It's Clojure, but we met undefined behaviour like in mutable world.

👀 8
Alex Miller (Clojure team)16:10:31

Sounds unexpected to me. :) Instead of using valid?, could you try using s/explain so you’d get a print to the console when it goes wrong? How is JMeter getting the data to send? Is it possible you have two threads reading from the same reader and thus getting something invalid? If you could isolate this down to something reproducible, would be great to have a jira for it.


@djtango If you are using Leiningen, the command lein deps :tree can be useful. With tools.deps there is clj -Stree. Maybe you were already aware of those and looking for something more, though.


thanks for this - wasn't really aware of using lein deps or using it in this way


at this point any and all suggestions are welcome!


"lein pedantic" is something to look into as it will flag the conflicts for you. Boot has a similar feature on its show task.


I'm not sure if there's an equivalent yet for clj...


thank you!

Garrett Hopper19:10:27

So, I added (clojure.spec.test.alpha/instrument) to the bottom of my core.cljs file, however I have a namespace alpha which defines a symbol (def global (beta/example ...)). The problem being that core depends on alpha which uses beta/example prior to it being instrumented. Do I have to add the instrument to every namespace to avoid this?


if you want that call to beta/example to get instrumented and you want to keep def global (instead of making it a function or wrapping it in a delay) then yeah I think you'd have to instrument it separately in this case

Garrett Hopper19:10:34

Makes sense :thumbsup: In this case, it was easy enough to not define the global, though I could see scenarios where that's not really a solution.

Garrett Hopper19:10:30

How do I spec and instrument functions that will be used during initialization?


How would you spec standards based things. Country codes for example:


This is where I am so far. (s/def :iso/country-code (s/and s/string? #(= 3 (count %))))


I'd probably scrape the values, put them in a set, and use that set as the spec


If you can get a big set of valid sample data and convert it into valid EDN, you could try using to infer the spec


@jaihindh.reddy in these kinds of cases I enumerate all the possibilities in a set and use that set in the spec.

👍 8

@taylor ISO charges money for downloads of data. But it is freely available on their website. Scraping that would be awkward to say the least.


e.g. (s/and string? countries/all-country-codes) where all-country-codes is a set.


in your example all the ISO codes are on the Wiki page


…which also makes it somewhat maintainable in the longer term.


are you concerned with keeping the list fresh as time passes?


Ideally I would love to do something like (into #{} (slurp url-for-all-country-codes))


yeah, that's a separate problem from clojure.spec


Country codes should be relatively stable. Dont mind manually updating it


Well, manually is fine, but I would plan on doing it every once in a while. The list of countries is actually surprisingly unstable.


Can I spec protocols and multimethods?


I think you're OK with multimethods tho' (I saw a CLJS ticket that indicated s/instrument was fixed to work with multimethods, so I assume it works in CLJ too).


On that note, when should I use one or the other?


Currently I think protocols should be the default, and multimethod if you think you need the arbitrary dispatch.