Fork me on GitHub

how can I ns-resolve symbol a spec sitting in a different namespace based on a given string?

Alex Miller (Clojure team)21:12:32

can you more clearly state inputs and output?


I want something like:

 (symbol "foo"))
but for a spec. How can I resolve ::my-specs/foo when given two strings “my-specs” and “foo”


yeah, let’s say I want to validate a spec, but instead of spec I have its name as a string

Alex Miller (Clojure team)21:12:13

my-specs is an alias only meaningful in the context of a namespace. is that the current namespace or some other one?


so let’s say I have a bunch of fields that I extracted from e.g. SQL query, it’s a vector of ["foo" "bar"]. I have a namespace my-specs with two specs in it: (s/def ::foo string?) and (s/def ::bar boolean?)… now I want to validate data, or generate data, basically I need to “get” those specs


all that programmatically

Alex Miller (Clojure team)21:12:54

do you know that all of these specs are in my-specs?


sure… let’s assume they are 100% there

Alex Miller (Clojure team)21:12:26

if so (s/get-spec (keyword "my-specs" "foo")) should work?


OMG… there’s a literally a function called get-spec… LOL


Thank you Alex… sorry for being so lame at explaining

Alex Miller (Clojure team)21:12:34

np, just trying to impedance match :)


however… what if I want to check if the spec is indeed there? 🙂

Alex Miller (Clojure team)21:12:15

well if you get nil, it's not there :)

Alex Miller (Clojure team)21:12:28

you can also call (s/registry) to just get the full map too


ah… okay… awesome… exactly what I needed. Thanks again!


@ag expectations.clojure.test does a dynamic lookup like that to let you "expect" values conform to specs:


(the dynamic require/resolve is so the code can run on Clojure 1.8)


Oh… that’s nice. I’ll give a gander as well. Thank you Sean!


hey friends… another dynamic lookup related question: if I have a (s/keys) spec and string representation of one of the fields how do I get-spec of that? e.g: I have: ::foo-spec/foo defined as:

(s/def ::foo 
  (s/keys :req-un [::bar-spec/bar]))
and in bar-spec ns I have:
(s/def ::bar (s/keys :req-un [::name]))
and I have strings “foo-spec”, “foo” and “” what’s the best way to get-spec of ::bar/name ? How can I make it work for multiple levels of nesting?


meaning I can get-spec/foo but now I need to “analyze” its :bar field, I have no idea where it sits, is there a way to find it out?



Alex Miller (Clojure team)22:12:16

there are multiple independent questions here

Alex Miller (Clojure team)22:12:20

re understanding the structure of a keys spec, you can use s/form to get a fully resolved form representing the spec (so you'd see (clojure.spec.alpha/keys :req-un [:bar-spec/name]) )

Alex Miller (Clojure team)22:12:37

finding the subspecs inside that spec is a matter of fishing for it (made somewhat complicated by the and / or support in s/keys). There are a variety of ways to tackle that, all a little meh, that's something we're looking at having better answers for in spec 2.

Alex Miller (Clojure team)22:12:02

if you go that path, you have only fully-qualified subspecs, so you can just pass them to s/get-spec

Alex Miller (Clojure team)22:12:55

and re nesting, there is no such thing as ::bar/name in this case - you've just smooshed together two independent things there


> you’ve just smooshed together two independent things there ehmm… I’m just trying to illustrate that I needed nested lookup…

Alex Miller (Clojure team)23:12:32

just saying that the specs themselves are not "nested" and have no naming relationship. spec references are always via fully qualified keywords (even if you use autoresolved names to specify them)


yeah, it seems this isn’t as simple as I thought it would be… so basically I wanted to figure out specs for a vector of strings like:

["" "" ""]
given that all specs are there with the relations set between them

Alex Miller (Clojure team)23:12:37

what do those strings mean?

Alex Miller (Clojure team)23:12:05

those are nested keys in map data or something?


so there’s:

(s/def account (s/keys :req-un [::id ::contact])
(s/def contact (s/keys :req-un [::first-name ::address])
(s/def address (s/keys :req-un [::city])


and they all may be sitting in different namespaces


now without knowing the ns of city or contact but knowing where account resides and given a string “” I want to get-spec of ::address/city


oh.. well, it gets a tad bit crazier when some fields are s/nilable

Alex Miller (Clojure team)23:12:42

the whole point of having namespaces is being able to differentiate things. seems like you're working really hard to rebuild what that gives you.

Alex Miller (Clojure team)23:12:28

the whole idea behind spec is that attributes are the main source of semantics and maps are weaker aggregations of those

Alex Miller (Clojure team)23:12:43

you are working significantly at odds with that idea


So if you want a spec that describes a relational data, how would you do it?

Alex Miller (Clojure team)23:12:21

generically, relational data is sets of maps


@ag it would probably help you to remember to use explicit qualifiers (on keywords) in your spec definition and stop using :: at least until you get your head around this...


so the above snippet with account -> contact -> address is okay?


(s/def :person/account (s/keys :req-un [:account/id :account/contact])
(s/def :account/contact (s/keys :req-un [:contact/first-name :contact/address])
(s/def :contact/address (s/keys :req-un [:address/city])
for example ^


That also gets you close to the "relational" model if you look at something like next.jdbc querying a database since it will use qualified keywords for column names, based on the table they are in...


(although there you have to reify the primary keys so you'd most likely have :account/id :account/contact_id and the latter would be the FK into the :contact table and its :contact/id column etc)


If you did the join with next.jdbc/execute! you'd end up with a flat map containing

{:account/id 123, :contact/first-name "Ag", :address/city "Wherever"}


yeah, I see how this can simplify a few things.