Fork me on GitHub

I've just started learning clojure-specs. My question is: what is the right place to put them? in a different namespace or right next to the production code?


Personally I like to put them in a separate namespace, but there's no rule about it

Yehonathan Sharvit12:03:42

what are the pros/cons of having in same/different namespaces?


easier to exclude if it's in another namespace


other than that it's a matter of taste, diff languages do it both ways (ex haskell vs ocaml)


Q: is there any documentation of the Spec protocol? I ask because I'm building a couple implementations of it to wrap up some of my more complex predicates. Reason being I want to generate custom error messages that are more descriptive


I think I've mostly figured it out, but I'm mainly just guessing based on my reading of the implementations already in clojure.spec


specific problem: I'm validating a tree-structure that's defined in an EDN file. Not only do I want to validate that the nodes are all structurally OK, I need to validate certain properties of the graph as a whole like "are all the edges pointing at valid nodes" "is it acyclic?"


My first approach was to just write those validators completely outside of spec, but that makes them hard to compose (like if I pack this datastructure inside of another one)


My second thought was "just use predicates", but an error message like {:pred (not (contains-cycles? graph)) :obj <the whole graph>} is super unhelpful to a user trying to figure out which node is invalid


So instead I packed the predicates into a (reify Spec ...) and it's GREAT but it took me a while to figure out a) that there was even a protocol that I could implement b) how to implement it


I did something similar (spec that validates a dsl: parses, potentially generates error messages with line/num syntax helpers etc)


basically a reified Spec with a custom explain*


+ some caching not to parse again for explain


what I guess I'm really angling at is "If I wanted to contribute some helpful documentation of the spec protocol, would there be interest in receiving that, and how would I go about doing it?"


I have some other related minor thoughts like "maybe explain-1 shouldn't be private so that Spec implementors can use it without having to #'hack/it"


yeah @mpenet same thing for me too


I have a little custom lispey transform language I'm using to do some basic filtering that gets packed inside other specs


I am not sure what's the status of the Spec protocol, might be considered an (unreliable) implementation detail. Not sure but at the time @alexmiller advised me to do this so I hope not


yeah I suspected maybe that was the case


(or might be)


maybe a better approach would be extending clojure.spec/spec to take an (optional) custom explain function


in the same way it takes an optional generator


it would probably make my code look a little simpler 😛


it would be nice yes

Alex Miller (Clojure team)13:03:32

@hospadar the Spec protocol is subject to violent change without warning and we’re not interested in documenting it at the current time

Alex Miller (Clojure team)13:03:04

we may not even keep it

Alex Miller (Clojure team)13:03:10

I don’t think Rich is interested in having custom explain messages (but I could be wrong)

Alex Miller (Clojure team)13:03:16

I think Rich’s conception is that in general, people should not be writing their own spec impls, they should be composing the provided specs


(def my-keys [::a ::b ::c])
=> #'my-ns/my-keys
(s/def ::my-keys (s/keys :opt my-keys))
CompilerException java.lang.IllegalArgumentException: Don't know how to create ISeq from: clojure.lang.Symbol, compiling:(/tmp/form-init5422364345568681610.clj:1:18) 
Rly? I will need to do a macro over your macro? Why not allow data on spec declaration? 😢


@souenzzo you can probably unescape (so long as the list is available at compile time as in your example


(s/def ::my-keys (s/keys :opt ~my-keys))


Use that trick in my project.clj to parameterize versions


That only works if the macro explicitly allows it


@alexmiller I feel that, I'd really rather not be writing custom preds. But in some cases I have to and it's nice if I can pack them into my specs with more error detail than just "this predicate failed"


well you can (eval (s/def ::foo ~(...)), which is quite horrible


(eval `(s/def ::my-keys (s/keys :opt ~my-keys)))
works, but (s/def ::my-keys (s/keys :opt ~my-keys)) no


hopefully in the future we can conform the spec form, modify it, then unform it


@souenzzo What's your use case for defining keys separately? Are you reusing them?


wouldn't hold my breath tho


@hospadar Would recursive spec solve your issue of informative tree validation explain message?

Alex Miller (Clojure team)13:03:40

CLJ-2112 is work that Rich asked me to do - that’s something we will definitely have


@yonatanel sometimes yes, sometimes no (at least I'm not sure how I'd implement that right now?)


I'd love to be proven wrong though


that's a super-dumb simple example of the kind of problem I'm trying to validate


it's pretty easy for me to write a validator (in the form of a predicate) that just reduces over the edges to check their validity, and I can s/and that together with the structural spec at the top level


but I want to get a more helpful error message that says "this edge failed validation because this node isn't valid"


hence why I've started writing implementations of Spec that let me emit more specific error messages


@hospadar I wonder if there should be a line between what you should validate with spec and what should go to another validator, other than runtime dependencies such as a database. In your case the rules might be a bit complex but you don't need anything but the data, so spec sounds good, but you can't compose yourself out of every case, so if spec already supports custom predicates and generators, why not explainers?


yeah that's my thought - if I was going out over the wire during validation ("only valid if exists in DB" situations) I'd probably not attempt to jam it in the spec


spec is very attractive to me for this kind of problem for me though since it makes it easy to build composable validators and still get helpful robust error reporting


I feel like spec should be able to validate (with useful errors) any data structure where the correctness of the data-structure doesn't rely on any external factors?


(i.e. the validation is very deterministic data->spec->answer is the same every time)


I kind of wish that a spec defined using s/and did not perform conforming while passing the value through its specs


I find myself defining named predicates that add additional constraints on what the value should be like using s/and, and it would be more natural for those predicates to receive the unconformed value


I know I could in theory use s/unform inside my predicates, but often times I already have an existing predicate that I use elsewhere as a part of the business logic of the program, so now I have to create a wrapper in order to use it with spec


user> (s/explain (s/and (s/cat :a int?) (fn [[a]] (> a 1))) [2])
UnsupportedOperationException nth not supported on this type: PersistentArrayMap  clojure.lang.RT.nthFrom (

Alex Miller (Clojure team)19:03:56

@luxbock we have talked about providing both flowing and non-flowing versions of s/and


@alexmiller and something for s/or that doesn’t produce pairs? (so we don’t need (s/conformer val) when we don’t care about the label)

Alex Miller (Clojure team)19:03:40

well s/nonconforming exists as a theoretical answer to that

Alex Miller (Clojure team)19:03:51

but I’m not sure whether Rich considers it a good idea

Alex Miller (Clojure team)19:03:06

(s/conform (s/nonconforming (s/or :a int? :b string?)) 100)
=> 100


Maybe I’ll chat to him next week in Portland about it. I’m loathe to rely on s/nonconforming since it may go away...


does core.spec have any kind of spec that validates emails?


@tjtolton That’s kind of a hard problem. You can use regexes, but it depends how “correct” you want to be?


right, haha, which is why I was vaguely hoping there would be some out of the box email validators and generators


We can dev one ourselves, its no problem


I’ll share the regex we use:

(def email-regex
  "Sophisticated regex for validating an email address."
  (-> (str "(([^<>()\\[\\]\\\\.,;:\\s@\"]+(\\.[^<>()\\[\\]\\\\.,;:\\s@\"]+)*)|"


yeah, then the trick is writing a generator for it



(defn valid-email?
  "Return true if the given string looks like an email address."
  (boolean (re-matches email-regex s)))
(s/def ::email (s/with-gen (s/and (bounded-string 5 128)
                 (wgen/fn-string-from-regex wstr/email-regex)))
where fn-string-from-regex is a wrapper for test.chuck‘s string-from-regex generator.


It generates some really wild email addresses 🙂


string from regex


that's neat


@tjtolton Depending on what you’re doing with generated emails, you might want a simpler regex for the generator portion — here’s just two examples from s/exercise: ”򂋕򏤹򱵺𧶊@[858.0.376.98]" ”\"񳭊𱢆򘪥󱬈򓖿򬜆\"@5cl84.e.a2.QN-.hpr1s.H.Ikp"


Hmm, the unicode didn’t survive the copy’n’paste...


Anyway, you get the idea.


Yeah, I do, thanks!


Test.chuck is pretty neat


Newbie here. I’m trying to write a multi-spec to handle a case like this:

            ::id "1"
            ::type "person"
            ::properties {
                ::firstName "Elon"
                ::lastName "Musk"
                ::email ""

            ::id “2"
            ::type “house"
            ::properties {
                ::color “red"
                ::style “Victorian"
For a particular type value, I want the acceptable properties keys to be different, while every instance has an :id, :type and a few other fields. The properties are variable, but not the parent/core keys. In the examples I’ve seen for multi-specs the type is always inside the variable collection itself.. e.g. I’d need to move type into properties instead of leaving it at the top level.


I think that you can perfectly use a multi-spec with the type where it is. Your entity specs will all have id, type and properties. No need to complicate things IMO


likely - but as I said - newbie - I don’t know how to do it. I’m fooling around with using a nested s/spec in there to get the properties map returned for each type in the multi-spec.