I was thinking, is there (should there be) a way to describe something in spec as “this has to be produced from a call to that function”?
Sort of a bit like (s/fdef some-fn :args ... :ret ::the-thing)
without specifying ::the-thing
anywhere else, but, like, inverted: (s/def ::the-thing (s/ret-of some-fn))
. I don’t think there is a good way to fit this into conform
machinery, but I’m just thinking about the ability to express the API contract in spec (so far expressing it in the docstring is fine 🙂 )
@vlaaad you can do this without spec maybe, just by letting the "factory" function put in some unique property which indicates it was made by that function
and you can validate that property using spec or using some other assertion
you can also do this via .impl naming, e.g. create a defrecord in an impl namespace and have one public API function for creating those records
I also can use blanket (https://github.com/vlaaad/blanket) to cover the implementation details 😄 To be clear — I’m not looking for solutions outside of spec, I just thought the problem I’m solving is about describing the API, and spec seems like a fitting tool for this use case. I also heard @richhickey is redesigning spec particularly around fn specs, hence decided to share a use case to consider 🙂
I don’t understand the use case
Saying “this has to be produced from a call to that function” seems weird
Data is data
there is evolution over time
I want to express the intention — if next version returns something else (or more realistically — if the next version has more functions that produce something valid in a other context), it will remain valid in that other context
then spec the truth - what does the data look like?
don't couple the spec to code
this is the whole schema/select idea
schema is all possible fields that may travel together
select tells you what fields are selected from the schema at different points
but I want to keep parts of the data implementation details
then don't spec those parts
Good point, although I’m not sure it captures the intention
@vlaaad Wouldn't just speccing the return values in the namespace that effectively declares that structure be enough?
at least from an encapsulation POV?
Would be speccing it with any?
(and a docstring when we get it) a better idea in this case?
The user can infer that ::the-thing
is explicitly anything forever.
why bother?
if it truly is "implementation details", then you are just erecting scaffolding and barriers in the way of future change
there needs to be some balance between the agility of Clojure data and the constraints of specs - don't spec everything to death
Sounds like a disapproving opinion. I want to express a single thing: the shape of this object is implementation detail, and despite this fact, it can be used in that context. This is not a barrier in the way of future change, and definitely not specing everything to death, this is a contract that helps user to see the relationship between parts of API.
I like the (s/def ::the-thing any?)
btw, coupled with API fspecs with :ret ::the-thing
I think it reaches the intention