clojure-spec

About: http://clojure.org/about/spec Guide: http://clojure.org/guides/spec API: https://clojure.github.io/spec.alpha/clojure.spec.alpha-api.html
vlaaad 2021-01-19T11:31:25.012700Z

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

borkdude 2021-01-19T11:58:26.013700Z

@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

borkdude 2021-01-19T11:58:36.013900Z

and you can validate that property using spec or using some other assertion

borkdude 2021-01-19T11:59:40.014500Z

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

vlaaad 2021-01-19T12:04:24.017900Z

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 🙂

👍 1
alexmiller 2021-01-19T13:55:56.018600Z

I don’t understand the use case

alexmiller 2021-01-19T13:57:42.020Z

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

alexmiller 2021-01-19T13:57:56.020300Z

Data is data

vlaaad 2021-01-19T14:05:50.021700Z

there is evolution over time

vlaaad 2021-01-19T14:09:06.023700Z

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

alexmiller 2021-01-19T14:09:23.024Z

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

alexmiller 2021-01-19T14:09:35.024300Z

don't couple the spec to code

alexmiller 2021-01-19T14:09:51.024600Z

this is the whole schema/select idea

alexmiller 2021-01-19T14:09:58.024900Z

schema is all possible fields that may travel together

🙌 1
alexmiller 2021-01-19T14:10:11.025300Z

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

vlaaad 2021-01-19T14:11:21.025700Z

but I want to keep parts of the data implementation details

alexmiller 2021-01-19T14:13:07.026500Z

then don't spec those parts

vlaaad 2021-01-19T14:14:30.027Z

Good point, although I’m not sure it captures the intention

emccue 2021-01-19T15:31:19.028100Z

@vlaaad Wouldn't just speccing the return values in the namespace that effectively declares that structure be enough?

emccue 2021-01-19T15:31:53.028400Z

at least from an encapsulation POV?

dgb23 2021-01-19T17:43:18.030600Z

Would be speccing it with any? (and a docstring when we get it) a better idea in this case?

dgb23 2021-01-19T17:46:23.031400Z

The user can infer that ::the-thing is explicitly anything forever.

alexmiller 2021-01-19T17:48:01.031600Z

why bother?

alexmiller 2021-01-19T17:49:03.032100Z

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

alexmiller 2021-01-19T17:52:08.034100Z

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

👍 3
1
vlaaad 2021-01-19T20:11:28.037100Z

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.

vlaaad 2021-01-19T20:15:34.038400Z

I like the (s/def ::the-thing any?) btw, coupled with API fspecs with :ret ::the-thing I think it reaches the intention