Fork me on GitHub
#clojure-spec
<
2020-04-20
>
Aron21:04:18

so I am reading docs and I am sure I will figure this out too, but someone could help me here a lot with a shortcut to speccing a hashmap that has 3 levels, the first is a fixed set of keys, second also a fixed set of keys, and the third is depending on the key either a string or a vector of keywors. so a valid example would be {:firstlevelkey {:label "string" :fields [:fieldone :fieldtwo]}} and I just want to say that on the first level only certain keys are allowed( one predefined set of keywords), second level always have to be both a :label and :fields and label always have to be string and the :fields always needs to be a vector of keywords from another set of predefined keys

Aron21:04:49

i don't need a full solution, just tell me please what to use so I can read up on those specifics because it's taking forever to sieve through 🙂

Aron21:04:27

I am trying to google > clojure spec "-un" and it's not going well

Aron21:04:03

the more I read the less I understand.

seancorfield22:04:41

> I just want to say that on the first level only certain keys are allowed This isn't something Spec favors -- it follows the concept of "open for extension". So you will need additional predicates with s/and to restrict keys (and remember that it's not really "idiomatic" to do that).

Aron22:04:40

Ok, good to know, so I won't force it if it's not expected

seancorfield22:04:41

The whole spec is going to end up looking something like this:

(s/def :key/label string?)
(s/def :field/keys #{:fieldone :fieldtwo ,,,}) ; your set of predefined keys allowed
(s/def :key/fields (s/coll-of :field/keys :kind vector?)) ; maybe you want min and/or max counts too?
(s/def :second/level (s/keys :req-un [:key/label :key/fields]))
(s/def :top/keys #{:firstlevelkey ,,,}) ; available top-level keys
(s/def :top/level (s/map-of :top/keys :second/level))

seancorfield22:04:30

If your set of keys isn't known at spec-time, it's a bit harder but that's probably a good first cut outline to start with.

Aron22:04:43

it's known 🙂 luckily

seancorfield22:04:44

(with better names, of course)

seancorfield22:04:23

That actually solved the restricted set of top-level keys, but at the expense of not requiring a minimum set either.

Aron22:04:30

the naming of keywords is arbitrary, right? so I could do just :label instead of :key/label, it just pays off to have very specific names of symbols so there is no collision?

seancorfield22:04:00

Right. Qualified names help avoid collisions.

seancorfield22:04:46

Only s/keys cares -- :req-un treats the qualified names as just their unqualified part (`:req` would pay attention to the qualifier as well).

Aron22:04:20

it's quite hard to grasp the pattern

Aron22:04:34

I need to experiment with this, thank you very much

Aron22:04:40

one more question that I think I know the answer for but still, if I want to spec anything, I always have to give a name to both the thing I want to spec and either use the definitions in place or create a named spec, right? So there is no way to write specs that would fit existing names automatically.

seancorfield22:04:43

I'm not quite sure I'm following your question but I think the answer is "no, no way to do that in Spec 1".

seancorfield22:04:09

Spec 2 does allow unqualified keys in a schema to have inline specs (predicates).

Aron22:04:52

in app namespace I define something like (def whatever "string") and then in app.specs namespace I say (s/def whatever string?) and there is some magic to match them

Aron22:04:03

probably a bad idea

seancorfield22:04:43

Specs are in a completely separate "space" from regular Var names so you need to associate them yourself explicitly.

seancorfield22:04:03

With functions, that's s/fdef but there's nothing for other Vars.

seancorfield22:04:22

You need to use s/valid? or s/conform to "apply" a spec to a value at runtime.

seancorfield22:04:36

(does that answer your question?)