clojure-spec

About: http://clojure.org/about/spec Guide: http://clojure.org/guides/spec API: https://clojure.github.io/spec.alpha/clojure.spec.alpha-api.html
2020-05-20T05:12:33.476700Z

Is it possible to share spec over wire? Or to serialize them?

seancorfield 2020-05-20T05:14:14.477900Z

@neo2551 You can get the code form of a spec with s/form -- and encode that send it over the wire -- but any predicates are going to depend on the code itself (anything beyond simple core predicates).

seancorfield 2020-05-20T05:14:42.478500Z

I'm not sure how easy it is in Spec 1 to reconstitute a spec from its form tho'. That's something that is easier with Spec 2.

alexmiller 2020-05-20T12:49:39.485300Z

Just eval the form you get

2020-05-20T05:15:10.479Z

Great! Thanks a lot!

jrychter 2020-05-20T07:24:41.485Z

Hmm. I think I understand the "global semantics" goals, but what about the specific case I have now? I think I am falling into answer (3) in Alex's list above. I am preparing API responses and I do want to preserve the same key names, as they will be sent to the API callers somewhere down the way. However, those responses fall outside of my app semantics boundary, and some fields will contain data in a different form (like timestamps). That is OK, but I would still like to verify some aspect of those responses — whether they are a valid ::status/status, for example. I think there is also a conflict with the "open maps" idea that Rich has been talking about. I would like to be able to validate data as being valid in terms of the supplied spec, and whatever else is in the map should not matter. I do understand the global semantics idea, just pointing out that these two might sometimes clash. I would find the ability to check only "is this a valid ::bar ?" very useful. Anyway, just wanted to describe the use case, as material for thinking.

seancorfield 2020-05-20T18:03:12.486500Z

@jrychter FWIW, Spec 2 has support for checking specs with closed maps as an option (it's a check-time option, not a spec definition time option).

seancorfield 2020-05-20T18:04:01.487600Z

Also, if it helps: what we do at work is have different (but related) sets of specs for domain, API, and persistence -- since API and persistence are constrained by external systems whereas domain is not.

seancorfield 2020-05-20T18:04:20.488100Z

Then we transform data at the boundaries between those layers as needed.

Kyle Brodie 2020-05-20T22:35:57.493900Z

I am trying to spec CSV rows that are parsed into Clojure maps but I'm hitting StackOverflowError in (s/keys :req-un [::kw1 ... ::kw156]) and right now the keywords are all (s/def ::kwN string?). I mostly want this for generating examples while developing so I don't have to use spec. It works if I give Java more stack space (I'm giving it -Xss512k to match Heroku's smallest dynos) but I think I'm using spec wrong

Kyle Brodie 2020-05-20T22:42:58.495500Z

I think the overflow is in expanding the and macro for keys-pred at https://github.com/clojure/spec.alpha/blob/5a4834eeb2967b2eca382efe0afcb3f590bdb3c2/src/main/clojure/clojure/spec/alpha.clj#L466

seancorfield 2020-05-20T22:46:27.497Z

@kyle That's certainly a lot of keys in a hash map to require all be present... I assume they have more meaningful names than kw1, kw2, etc?

Kyle Brodie 2020-05-20T22:50:53.498700Z

Yeah its because the CSVs are denormalized so the columns are always there even if there is no data. I can get example data and make them into Clojure maps for my development. Just thought it would be cool to test out my functions with generated data

Kyle Brodie 2020-05-20T22:51:12.499200Z

The data will be reduced to less keys after a few steps so those I should be able to spec then

seancorfield 2020-05-20T22:51:57Z

If you wanted random key/value rows with random keywords and random strings, just for testing stuff, you could use

(s/def ::csv (s/map-of keyword? string? :min-count 156 :max-count 156))

seancorfield 2020-05-20T22:52:26.000600Z

I guess you could make a set of all the known keys and replace keyword? there with the set.

seancorfield 2020-05-20T22:53:15.001600Z

(s/def ::csv (s/map-of #{:kw1 :kw2 ,,, :kw156} string? :min-count 156 :max-count 156))
I think that would work (you could make the set of keys into a separate spec)

Kyle Brodie 2020-05-20T23:01:06.002300Z

(s/def ::simple-row (s/map-of (set columns) string? :min-count 156 :max-count 156)) (s/exercise ::simple-row 1)

Error printing return value (ExceptionInfo) at clojure.test.check.generators/fn (generators.cljc:435).
Couldn't satisfy such-that predicate after 100 tries.
class clojure.lang.ExceptionInfo

seancorfield 2020-05-20T23:53:12.005200Z

@kyle Ah, yeah, that's because it is trying to randomly generate elements from the full set for each key but each key in the generated map needs to be distinct 😞

seancorfield 2020-05-20T23:53:54.006200Z

You'd have to wrap the key part spec with a generator that produces the full set (in some random order, if you cared).

seancorfield 2020-05-20T23:55:30.007300Z

I suspect it would be easier to wrap the whole s/map-of spec in a custom generator since all it really needs to do is zip together that set of keys with a bunch of random strings...