I could use some guidance on using clojure.spec to validate input arguments to a function. Here’s some context:
• I am debating instrumenting with fdef
vs. using :pre
and :post
conditions.
• The function is internal to the app and the argument values are completely under the control of the programmer (i.e. there is no validation of external data going on).
I was initially leaning toward instrumenting with fdef
because I see this as a check for program correctness rather input validation. It should be safe to turn this check off in production with no change in behavior.
But I want the instrumentation to be turned on by default during development and turned off by default in production. I can imagine ways to achieve that, but the fact that I couldn’t find any mention of a typical pattern for doing that (e.g. my understanding is that assert
is set up to be easily enabled/disabled in a blanket manner) made me think I might be missing something.
> I was initially leaning toward instrumenting with fdef because I see this as a check for program correctness rather input validation.
what's this
?
(I can interpret it in two different ways)
Yeah, that’s fair. this = “this solution I’m designing”, not this = “instrumenting with fdef”.
If ease of turning on/off is the main concern, perhaps I'd go for fdef because it's the default choice, and toggling it globally, and both for inputs and outputs seems relatively easy with https://github.com/jeaye/orchestra
There are reasons beyond toggling ease why some people might prefer assert
to instrumentation. https://github.com/fulcrologic/guardrails or https://github.com/nedap/speced.def (disclaimer: I authored it) and probably a couple more libraries favor it.
There's no fixed truth in the topic, but in absence of other concerns I'd probably 'start small'.
@vemv Thanks a ton! I need to digest this more and check out orchestra. Off the top of my head, my only concern is that I usually like to be a little wary about adding new dependencies if I don’t have to.
Another thing I should mention is that I have no intention of ever testing this function with generative tests. It’s not a good fit for generative testing.
Are you talking about clojure.core/assert or s/assert? Seems like the latter would be good here
Thanks! Somehow I missed s/assert
, I think you’re right. Maybe s/assert
in a :pre
condition…
I'd put it in the mainline
s/assert in mainline code is designed so that it can be compiled completely out of the program via https://clojure.github.io/spec.alpha/clojure.spec.alpha-api.html#clojure.spec.alpha/*compile-asserts*
Okay, thank you! I really appreciate the guidance.
Hello. Where i have several defined and registered predicates, can i compose them in this state?
;; contrived example
(s/def ::string-starts-with-an-a? (s/and string? #(string/starts-with? % "a")))
(s/def ::not-an-apple? #(not= "apple" %))
(s/def ::starts-with-an-a-but-not-an-apple? (s/and ::string-starts-with-an-a? ::not-an-apple?))
predicate composing after registration
disregard the above. my forms had not been evaluated, thus the error received was entirely my doing