This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2019-02-04
Channels
- # announcements (7)
- # beginners (37)
- # boot (6)
- # calva (13)
- # cider (13)
- # cljdoc (52)
- # cljs-dev (9)
- # clojure (117)
- # clojure-europe (3)
- # clojure-italy (12)
- # clojure-nl (21)
- # clojure-russia (8)
- # clojure-spec (77)
- # clojure-uk (20)
- # clojurescript (142)
- # community-development (6)
- # cursive (5)
- # datomic (13)
- # emacs (9)
- # figwheel-main (20)
- # fulcro (33)
- # graphql (11)
- # instaparse (6)
- # klipse (1)
- # off-topic (7)
- # om (8)
- # quil (7)
- # re-frame (11)
- # reagent (39)
- # reitit (10)
- # shadow-cljs (36)
- # spacemacs (3)
- # test-check (3)
- # tools-deps (83)
- # utah-clojurians (31)
- # vim (14)
Hey everyone, I'm experiencing something odd trying to implement spec into our project for the first time. I'm using fdef to define my function's spec, however it's not validating my arguments. I'm using clojure 1.10. Here's the code:
(defn inc-num [x]
(inc x))
(s/fdef inc-num
:args (s/cat :x number?)
:ret number?)
Calling this function (inc-num "a") throws a ClassCastException instead of the spec error. Any ideas?I did not! Let me go ahead and do this, maybe I got ahead of myself in the documentation 🙂
yes, writing an fdef does not turn on instrumentation automatically. also note when you re-define the fdef, you have to call instrument again to make it effective
I see. So for all functions that I'm relaying on fdef to spec, I must also instrument those functions
Per namespace?
if you’re using component, you might want to hook it up to the start/stop cycle. no, globally
yep that'll instrument everything that's been loaded, across all namespaces. there's also unstrument
to do the opposite
Awesome! Thanks everyone for the quick feedback, I'm movin' again. I'll go through the documentation more thoroughly
@U6TUZTAAF I wrote some more function spec examples here too: https://blog.taylorwood.io/2017/10/15/fspec.html
I’ve never used fspec. does spec really check the arity of such a function when you call a higher order function whose function argument is spec’ed with it?
@U04V15CAJ fdef
uses fspec
internally, so there's not much difference AFAIK. when you spec a function that takes another function as an argument, spec invokes the passed function ~20 times — a kind of mini-check — to see if it conforms
(defn f [g] (g 1))
(s/fdef f :args (s/cat :g (s/fspec :args (s/tuple int?))))
(st/instrument `f)
(f #(doto % prn inc))
-1
0
-2
...
59
-69770
-1165
1
=> 1
so whenever you call f
(instrumented), spec is going to do a lil mini-check of the function you pass to f
to see if it conforms to the fspec
, and if it does then of course f
will call g
again
why is that sane behavior? I can see this being useful when doing generative testing
wut…
$ clj
Clojure 1.10.0
user=> (require '[clojure.spec.alpha :as s])
nil
user=> (require '[clojure.spec.test.alpha :as stest])
nil
user=> (s/fdef clojure.core/keep :args (s/cat :f ::keep-fn :coll seqable?))
clojure.core/keep
user=> (s/def ::keep-fn (s/fspec :args (s/cat :x any?) :ret any?))
:user/keep-fn
user=> (stest/instrument `keep)
[clojure.core/keep]
user=> (keep inc [1 2 3])
Execution error (FileNotFoundException) at user/eval144 (REPL:1).
Could not locate clojure/test/check/generators__init.class, clojure/test/check/generators.clj or clojure/test/check/generators.cljc on classpath.
user=>
yeah, I know why this happens, I’m just surprised that it suddenly wants to do generative testing while it can just conform while running, like normal functions
I haven’t used fspecs in speculative yet and this may be a reason I’m not going to..
I think the problem is more about speccing higher-order functions in general, than it is purely about fspec
, b/c fdef
is just a shorthand for s/def
+ s/fspec
. another option is to just use fn?
or ifn?
when you're speccing HOFs, that way spec doesn't mini-check them
in most cases it’s sufficient, although it would maybe nice to be able to speak about function arity
one case where I can see value in fspec
ing args to HOFs is when you check
the HOF, spec will pass it a dummy function that expects the right :args
and returns values generated according to the :ret
spec
that would be useful, but I want this to be pluggable. so my fdef would not use fspecs, but I do want to plug them instead of ifn?
during generative testing.
but overriding ifn?
would be a bit too global, since the args and ret could use ifn?
with different properties
chiming in that the generator requirement for HOFs also does not seem sane to me. should be able to instrument without assumption that you’re using generative testing
I’d say so too. @alexmiller maybe should be an option to turn this off on spec2?
Is there a way quick way, without using an external lib, to generate a random value from a spec while limiting the max depth when the value is a map?
e.g. I have nested s/keys
specs and I would like to generate a random value for the spec but limit the depth of the data structure
I know there are some libs on github that do this, but I am wondering if there is a short way to do it out of the box with specs
is it a recursive spec? if so, there's a *recursion-limit*
binding you can use during generation. the other thing that comes to mind is passing in a small/fixed size
arg to the sample
function
not sure if this would have the effect you want, but maybe https://gist.github.com/taylorwood/232129ccd3cb809281fea591d46f1b8a#file-infix-spec-clj-L58
@taylor I’ll try that, ty! Also I am trying to generate random values for this spec:
(s/def :elogic/term (s/and (s/keys :req [:elogic.term/type])
(s/multi-spec elogic-term-type :elogic.term/type)))
but I am getting “Couldn’t satisfy such-that predicate after 100 tries.”yeah that's b/c the generator for your s/and
is only based on the first inner s/keys
spec, and the generated values from that don't conform to the second multi-spec
you can wrap that s/and
with s/with-gen
to specify a custom generator, or override the generator elsewhere
@alexmiller is it a generally bad idea to use specs to parse strings?
it’s not recommended, but I literally can’t stop you :)
Do your own spec-inspired (and maybe backended by spec) library to specify string?!?!
I recently found this lib: https://github.com/miner/strgen It’s cool, but maybe comes with limitations
@U2J4FRT2T not a bad idea 😄 I would need to first learn more about how spec works though
The nice thing about using spec for strings is that you get nice error messages and can generate examples. For better error messages one could also use a parser combinator or parser generator.
@U04V15CAJ being able to generate examples is definitly useful
sorry, was on a call
the point is that spec regex were designed to spec sequential collections
strings are not sequential collections and the performance of the regex specs on them might be bad
and there are quite good options for parsing strings (namely, the regex built into the JDK and exposed by Clojure)
test.chuck has a function that generates strings from regexes https://github.com/gfredericks/test.chuck if you need that
FWIW, we use test.chuck
s string regex generator quite a bit -- we're very happy with that, combined with regular string regex. I would not recommend trying to use spec's sequence regexes on a string.
@U04V70XH6 good to hear. how does it compare to miner’s lib? I see his name in the README of test.chuck. Was his lib merged into test.chuck?
@U052852ES I think this thread is worth reading since you asked me about a related problem you’re solving with spec
@U04V15CAJ No idea. First I've heard of Steve Miner's regex lib.
@U04V15CAJ I have seen it and I will have a look again, thank you.