This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2017-06-26
Channels
- # aws (1)
- # beginners (50)
- # boot (32)
- # chestnut (2)
- # cider (14)
- # clara (23)
- # cljs-dev (131)
- # cljsrn (44)
- # clojure (133)
- # clojure-belgium (3)
- # clojure-denmark (4)
- # clojure-dev (6)
- # clojure-italy (4)
- # clojure-nl (2)
- # clojure-russia (95)
- # clojure-spec (59)
- # clojure-uk (14)
- # clojurescript (157)
- # cursive (26)
- # data-science (1)
- # datomic (160)
- # devops (5)
- # dirac (80)
- # emacs (2)
- # graphql (2)
- # jobs (2)
- # lein-figwheel (1)
- # lumo (9)
- # off-topic (151)
- # onyx (2)
- # parinfer (2)
- # pedestal (5)
- # perun (2)
- # re-frame (60)
- # reagent (3)
- # remote-jobs (1)
- # test-check (3)
- # uncomplicate (11)
- # yada (1)
I have trouble running stest/check
in a repl. It just returns an empty vector immediately, just as if the symbol was undefined. I have required the symbol and I can evaluate it in the REPL but it seems the check
can’t find the symbol. How can I make sure that stest/check
finds the function that I gave to it?
I'm very excited about the goal of clojure.spec leading to improved error messages, but there is still a ways to go.
I just typo'd :require
in
(ns raven.intro
(:requre [clj-http.client :as http]
[raven.secrets :as secrets]))
and got the following user-friendly error:
CompilerException clojure.lang.ExceptionInfo: Call to clojure.core/ns did not conform to spec:
In: [1] val: ((:requre [clj-http.client :as http] [raven.secrets :as secrets])) fails at: [:args] predicate: (cat :docstring (? string?) :attr-map (? map?) :clauses :clojure.core.specs.alpha/ns-clauses), Extra input
:clojure.spec.alpha/spec #object[clojure.spec.alpha$regex_spec_impl$reify__1200 0x1331d7d5 "clojure.spec.alpha$regex_spec_impl$reify__1200@1331d7d5"]
:clojure.spec.alpha/value (raven.intro (:requre [clj-http.client :as http] [raven.secrets :as secrets]))
:clojure.spec.alpha/args (raven.intro (:requre [clj-http.client :as http] [raven.secrets :as secrets]))
#:clojure.spec.alpha{:problems [{:path [:args], :reason "Extra input", :pred (clojure.spec.alpha/cat :docstring (clojure.spec.alpha/? clojure.core/string?) :attr-map (clojure.spec.alpha/? clojure.core/map?) :clauses :clojure.core.specs.alpha/ns-clauses), :val ((:requre [clj-http.client :as http] [raven.secrets :as secrets])), :via [], :in [1]}], :spec #object[clojure.spec.alpha$regex_spec_impl$reify__1200 0x1331d7d5 "clojure.spec.alpha$regex_spec_impl$reify__1200@1331d7d5"], :value (raven.intro (:requre [clj-http.client :as http] [raven.secrets :as secrets])), :args (raven.intro (:requre [clj-http.client :as http] [raven.secrets :as secrets]))}, compiling:(/home/deg/Documents/git/projects/raven/src/clj/raven/intro.clj:1:1)
there's room for improvement for sure, there isn't a lib that does human readable translation yet?
It takes a lot less than that. Just a spec that had a list of valid keywords for the ns form could trivially pump out ":requre is not one of [:import :require :use ...]"
deg: It’s actually a lot trickier than this from a spec perspective due to the “form” structure of the dsl. If we were to design the ns api now, it is highly unlikely we would do it this way (would probably be a map). This turns out (due to the high fanout and nested differences) to be a particularly challenging case for spec to generically give a good error message.
I guess it goes against the choice of making specs maps open to extension tho (which is arguably good or bad)
The problem is that there are very few people in the community who are simultaneously likely to (1) hit this kind of error; (2) take more than a few seconds to spot the problem and feel the pain; and (3) have the comfort level to dive into the source of clojure core.
And you are right too, of course, that spec's philosophy is generally against closed lists of keywords. But, enough of us have written that for our own purposes. And, I'm sure that very few people would be against tight checking for the sake of error checking of special forms or canonical macros.
deg: just because it’s hard for spec to produce a good generic error here does not mean that the ns macro can’t take matters into its own hands instead to provide a customized response. not a done deal.
And, relatively easy to fix any one error. The challenge is creating a framework where, anytime a newbie is bitten by one of these messages and reports it, it triggers a process that makes that message be forever better for the next user.
if you specify the legal keywords for the NS form as a set, then that's still very much open to extension, no?
@jjttjj one other thing to consider is: what is user
? because in my experience that can vary wildly inside of your system, a login user might require login
and password
while a registering user might require much more, think about this, and you end up having more specific entities (like: login-user
and new-user
) or you might drop the entities are all (that might vary a lot depending on much re-use you can give to those entities)
I’m a bit confused by the value of “in” when using “map-of”. When there is something wrong a value in the map, the in
path doesn’t actually seem to point to that value. For example:
https://gist.github.com/bhb/6f06dd07bcf5b275a7d4faf3167bfc85
@vikeri Are you sure you have fdef
'd the function?
(defn foo [x] x)
=> #'sandbox.spec/foo
(s/fdef foo :args (s/cat :x int?))
=> sandbox.spec/foo
(stest/check `foo)
=>
({:spec ...,
:clojure.spec.test.check/ret {:result true,
:num-tests 1000,
:seed 1498486054141},
:sym sandbox.spec/foo})
@vikeri If you fail to either (1) fdef
a defined function, or (2) give check
a symbol which does not resolve to a function, it will exhibit the behavior you describe:
(defn bar [x] x)
=> #'sandbox.spec/bar
(stest/check `bar)
=> ()
(stest/check `not-a-function)
=> ()
is there any way to provide a custom message in the output of s/explain
?
stathissideris: no
thanks. Are there any plans for it, or is it out of scope/a bad idea?
there are no plans for it right now. the idea is that specs should be able to give you generically consistent errors. The explain-data error you get has enough info that you should be able to build custom user errors from that if desired - I believe Sean Corfield is someone doing a lot of this right now.
I get it. I guess I have a slightly more complex case where I use s/&
to further validate a conformed value and s/explain
reports the whole code of the anonymous function as the failing predicate, which is useful, but it would be even better to have a human readable message to say what was expected
somewhat related: https://dev.clojure.org/jira/browse/CLJ-2115
It looks like that: val: ... fails spec: :monitor.settings/aliases-header predicate: (fn [{:keys [aliases]}] (apply distinct? (map :alias aliases)))
@U050SC7SV I don’t want it that bad, it’s more of a “nice to have” for me 🙂
s/&
is indeed a special case and we are likely going to have to add support for custom forms there regardless (to support things like s/keys*
which use it for implementation), but unlikely we would have a custom explain there beyond that
@alexmiller ok, thanks for the explanation and thanks for your efforts in general!
One way to make this a bit more self documenting would be for the second argument of s/&
to be named function with a descriptive name instead of an in-place anonymous function
@joshjones Hmm, I required the ns where the fn was defined but maybe that didn’t define the fdef. That is probably the issue.
A simpler example: https://gist.github.com/bhb/c8d01c455494921a3698a9cf951272ff of how :in
works with map-of
. I think I’m beginning to understand how the path works. [:hi 0]
means something like “construct a key/value pair from the map and the key :hi
, then navigate to the 0th element of that k/v pair”
correct - maps are conformed as a sequence of map entries (spec’ed as a tuple of k and v)
however, that path won’t get you to the right place so I think that 0 is actually going to get you to the wrong place.
and that seems like a bug
and would be happy to see a jira about that. it is related to https://dev.clojure.org/jira/browse/CLJ-2080 but not addressed by that ticket
I've been looking at it for a few minutes now @alexmiller -- and I was going to say it looked like a bug too (but was not confident enough that my understanding about it was correct). the second element of the :in
in this case is which element of the tuple spec caused the error. But the first element (`"hi"`) is coming from the every-impl
spec, and this part particularly does not seem correct
It's tricky
putting some println's in the spec code:
(clojure.spec.alpha/explain-data :foo/user-map {"hi" "foo"})
EVERY IMPL, mapping
i: 0
v: [hi foo]
TUPLE IMPL, mapping
i: 0
form: clojure.core/string?
pred: #object[clojure.core$string_QMARK___6415 0x674c583e clojure.core$string_QMARK___6415@674c583e]
TUPLE IMPL, mapping
i: 1
form: clojure.core/int?
pred: #object[clojure.core$int_QMARK_ 0x272031ce clojure.core$int_QMARK_@272031ce]
=> #:clojure.spec.alpha{:problems ({:path [1], :pred clojure.core/int?, :val "foo", :via [:foo/user-map], :in ["hi" 1]}), :spec :foo/user-map, :value {"hi" "foo"}}
I would consider this on top of the patch for 2080, which improves things in the explain case, whereas this is the conform case which doesn't currently use the kfn
Rich and I have an ongoing discussion/argument about what happens here :)
i can see why, as it's not necessarily a clear cut answer as to what should be there. from one of your spec slides, I have that :in
represents: "vector of specs from root spec to failing spec"
@alexmiller @joshjones I appreciate the info. I will look at the tickets and patches mentioned above and file a follow up bug. I agree it’s tricky - especially in the case where the key is wrong. Given a nested data structure, how do I provide a path to a map key? AIUI, it doesn’t work if I consider a “path” to equal “a vector of keys” that would work with get-in
.
What I’ve started to do is try to construct a function (maybe this already exists?) that takes some data + an :in
path and retrieves the value.
That’s really what is driving this: I’d like to be able to take some (invalid) data + a problem (which contains the :in
) and be able to use the unique :in
value to get the problematic value, which is likely deep inside the original data
I had originally (naively) thought that I could use get-in
for that function, but I think it’s more subtle than that, so I’m writing my own to use these special :in
paths.
the idea is that you should be able to do that
it’s not possible in all cases
but I don’t know that it’s useful to specify [<key> 0] either
Yeah, tricky. For my cases, I’m considering changing my code to accommodate a “spec path” that allows me to reach keys i.e. writing new functions that work like get-in
or update-in
with this new type of path
just a thought, not a question: I think I’ll write a few “readable” and size constrained generators for strings, keywords and symbols so that my eyes don’t bleed whenever I try to read the output of s/exercise
(and I know it’s a good thing that it produces “challenging” output)