how can I recognize an object such as the one returned from (s/or :1 int? :2 number?)
. I see that s/regex?
, s/get-spec
, and s/spec?
all return nil
. I notice however that the class of the object has (type (s/or :1 int? :2 number?))
in its ancestors?
(ancestors (type (s/or :1 int? :2 number?)))
==> #{clojure.spec.alpha.Spec clojure.lang.IObj clojure.lang.IMeta
java.lang.Object clojure.spec.alpha.Specize}
So I can use the following ugly idiom:
(instance? clojure.spec.alpha.Spec (s/or :1 int? :2 number?))
isn't there a better way?Next question, how can I extract int?
and number?
from the object? I see that (keys (s/or :1 int? :2 number?))
throws an Exception.
(keys (s/or :1 int? :2 number?))
Execution error (IllegalArgumentException) at clojure-rte.genus-spec-test/eval17912 (form-init5322407590801644377.clj:30310).
Don't know how to create ISeq from: clojure.spec.alpha$or_spec_impl$reify__2118
I see that (s/describe (s/or :1 int? :2 number?))
returns the list (or :1 int? :2 number?)
. However, to recognize the object as one which has a describe
method.it's important to be clear in talking about the two worlds of spec - spec forms and spec objects. (s/or :1 int? :2 number?)
is a spec form, which when evaluated, returns a spec object (something that satisfies the spec protocol). Trying to nail down concrete types for the latter part is going to be a bad time - the important thing is the protocol which is inherently polymorphic.
so how do I know whether I have an object which satisfies the spec protocol?
spec 1 does not have great answers for extracting information from either spec objects (which are opaque) or spec forms. One path to this is to write specs for spec forms and then use s/conform to "parse" the forms. A significant stab at this exists in https://clojure.atlassian.net/browse/CLJ-2112 but I think the future direction is really making a more data-centric representation which is one thing we are doing in spec 2 (but that's still very much a work in progress)
spec?
is the predicate for that
yes but spec? returns nil.
for what?
user=> (s/spec? (s/or :1 int? :2 number?))
#object[clojure.spec.alpha$or_spec_impl$reify__2118 0x51ec2df1 "clojure.spec.alpha$or_spec_impl$reify__2118@51ec2df1"]
aaaaaaaaahhhhhhhhh!!!!!! yikes. s/spec?
returns non-nil. That's EXCELLENT I have a local function named gs/spec?
which just asks whether it is a sequence whose first element is spec. spec?
was my first guess. just was shadowed by a local function. Cool. thanks.
yeah, your predicate is asking a symbolic spec form question
and I just typed spec? at the REPL.
REPL issue.
is there a way to undef
a function. Sometimes I rename a function while debugging, and the old function (of course) is still defined in vm, and if my code accidentally calls it because I didn't find and change all references, that introduces bugs which are hard to find.
@jimka.issy You can use (s/fdef foo/bar nil)
(I assume, I know (s/def ::foo nil)
at least works)
yes, s/def to nil
what is s/ ?
clojure.spec.alpha
you mean use spec to redefine a function?
are you asking about function specs or functions?
I think @borkdude and I assumed you meant specs since we're in the spec channel here
funtions. Sorry if I mistyped before?
sorry, its probably my fault.
you can use ns-unmap
(def name nil)
works of course.
^^ that doesn't unmap, just redefines
ahhh ns-unmap
. thats the cleaner way.
user=> (def x 1)
#'user/x
user=> (ns-unmap *ns* 'x)
nil
user=> x
Syntax error compiling at (REPL:0:0).
Unable to resolve symbol: x in this context
user=> user/x
Syntax error compiling at (REPL:0:0).
No such var: user/x
I recently discovered ns-unmap also works for imported classes. I need to fix a bug in sci which doesn't support that yet :/ ;)
I have several functions which are memoized. it speeds up my program 1000 fold. But when debugging, it can be a source of errors, because I forget that the function might not really be called.
the double-edged blade of memoization :)
indeed. So sometimes I really want to set-the-d*mn-function to undefined
to make sure it's not a problem of memoization
@jimka.issy A solution to that problem might be to call the original function foo*
and the memoized one foo
Here's the macro I'm using.
(defmacro defn-memoized
[[public-name internal-name] docstring & body]
(assert (string? docstring))
`(let []
(declare ~public-name) ;; so that the internal function can call the public function if necessary
(defn ~internal-name ~@body)
(def ~(with-meta public-name {:dynamic true}) ~docstring (memoize ~internal-name))
))
This allows my to locally rebind the name such as
(binding [my-function (memoize -internal-name)]
...)
which I often do in test suitsmakes sense
for tests you can also use with-redefs
which doesn't require your vars to be dynamic
never heard of with-redefs. what's that.
(doc with-redefs)
indeed. so that just saves needing to declare them as dynamic? or does it do something else?
dynamic bindings aren't visible to all threads, with-redefs
changes the root binding of vars temporarily
(note that there are issues using with-redefs with concurrency, including future etc)
@alexmiller are these the same issues with any dynamic variable?
yeah, when running tests concurrently this may bite you
Aren't dynamic variables thread-local in clojure?
yes, but with-redefs doesn't use thread-local, it's just a global mutation that gets restored afterwards
ah ha.
Personally I don't have any test suites that run into problems with this, but then again, I hardly use with-redefs at all
There could be a performance penalty to marking all your vars dynamic, but not if they haven't been dynamically re-bound yet since there's a pretty efficient check for that happy path
why doe s/describe return lists using and
and or
rather than clojure.spec.alpha/and
and clojure.spec.alpha/or
? that makes programmatic usage more difficult.
s/describe
isn't intended for programmatic usage. use s/form
rather than that
Excellent. that does the trick. much more program friendly
we regularly see people run into failing test suites when using with-redefs
because they don't understand what it does, which is why I mention this
:thumbsup:
👍:skin-tone-2:
s/describe is primarily useful for printing shorter specs for humans (used in doc
for example)
makes sense.
I just gotta ask, is there a way to create a shorthand alias for a namespace to use in fully-qualified keys without actually creating that namespace (using the ns
form)?
@amorokh I think this is a pretty common pattern:
user=> (require '[clojure.spec.alpha :as s])
nil
user=> (alias 'foo (create-ns 'foo))
nil
user=> (s/def ::foo/bar int?)
:foo/bar
Rumor has it that core team is working on making this easier@borkdude thanks, not sure I want to do that but at least I know about that possibility