This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2017-03-24
Channels
- # aleph (4)
- # beginners (93)
- # cider (7)
- # cljs-dev (16)
- # cljsrn (5)
- # clojure (192)
- # clojure-dusseldorf (3)
- # clojure-italy (14)
- # clojure-russia (16)
- # clojure-serbia (1)
- # clojure-spec (85)
- # clojure-taiwan (1)
- # clojure-uk (79)
- # clojurescript (188)
- # code-reviews (9)
- # core-async (2)
- # crypto (1)
- # cursive (26)
- # datomic (21)
- # heroku (1)
- # hoplon (3)
- # jobs (7)
- # jobs-discuss (20)
- # jobs-rus (13)
- # off-topic (77)
- # om (15)
- # onyx (23)
- # pedestal (94)
- # planck (11)
- # proton (10)
- # protorepl (1)
- # re-frame (16)
- # ring (22)
- # ring-swagger (9)
- # rum (2)
- # specter (18)
- # testing (2)
- # untangled (14)
- # vim (12)
- # yada (58)
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?
what are the pros/cons of having in same/different namespaces?
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)
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"
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
maybe a better approach would be extending clojure.spec/spec to take an (optional) custom explain function
@hospadar the Spec protocol is subject to violent change without warning and weāre not interested in documenting it at the current time
we may not even keep it
I donāt think Rich is interested in having custom explain messages (but I could be wrong)
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
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"
(eval `(s/def ::my-keys (s/keys :opt ~my-keys)))
works, but (s/def ::my-keys (s/keys :opt ~my-keys))
no@hospadar Would recursive spec solve your issue of informative tree validation explain message?
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?)
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 (RT.java:962)
@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)
well s/nonconforming exists as a theoretical answer to that
but Iām not sure whether Rich considers it a good idea
(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...
He wonāt be there
@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
Iāll share the regex we use:
(def email-regex
"Sophisticated regex for validating an email address."
(-> (str "(([^<>()\\[\\]\\\\.,;:\\s@\"]+(\\.[^<>()\\[\\]\\\\.,;:\\s@\"]+)*)|"
"(\".+\"))@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\])|"
"(([a-zA-Z\\-0-9]+\\.)+[a-zA-Z]{2,}))")
re-pattern))
Then
(defn valid-email?
"Return true if the given string looks like an email address."
[s]
(boolean (re-matches email-regex s)))
and (s/def ::email (s/with-gen (s/and (bounded-string 5 128)
wstr/valid-email?)
(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 š
@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.
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.