Fork me on GitHub

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 ( 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 🙂

👍 3
Alex Miller (Clojure team)13:01:56

I don’t understand the use case

Alex Miller (Clojure team)13:01:42

Saying “this has to be produced from a call to that function” seems weird


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

Alex Miller (Clojure team)14:01:23

then spec the truth - what does the data look like?

Alex Miller (Clojure team)14:01:35

don't couple the spec to code

Alex Miller (Clojure team)14:01:51

this is the whole schema/select idea

Alex Miller (Clojure team)14:01:58

schema is all possible fields that may travel together

🙌 3
Alex Miller (Clojure team)14:01:11

select tells you what fields are selected from the schema at different points


but I want to keep parts of the data implementation details

Alex Miller (Clojure team)14:01:07

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.

Alex Miller (Clojure team)17:01:03

if it truly is "implementation details", then you are just erecting scaffolding and barriers in the way of future change

Alex Miller (Clojure team)17:01:08

there needs to be some balance between the agility of Clojure data and the constraints of specs - don't spec everything to death

👍 9

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