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-04-14T13:09:04.031200Z

Is there a way to make fn spec that allows fn to throw exceptions? I want to express a contract "this function returns any value and can even throw"

avi 2021-04-14T13:29:43.031300Z

IIRC, no.

avi 2021-04-14T13:31:43.031500Z

This has encouraged me to spec out some of my functions to return values that indicate success or failure. Often they’re :cognitect.anomalies/anomaly values. I’m pretty happy with this approach. It makes the functions more testable, I think. And by speccing out the error values you get the property testing to extend to error cases as well.

vlaaad 2021-04-14T13:33:09.031700Z

Yeah, but I want to document that I allow user-provided callback to throw an exception. It’s like a promise “it’s okay to fail”…

avi 2021-04-14T13:33:40.031900Z

Yeah, makes sense, reasonable. I just think that’s out of scope for spec.

avi 2021-04-14T13:35:53.032100Z

If I were to speculate the thinking behind that call, I’d guess that it’s an example of the position that exceptions should be used in truly exceptional cases — cases that are hard to predict and handle — and can thus be thrown anywhere, at any time. (That’s a reductive summary missing much nuance but I hope potentially slightly useful.) Just speculation though, I could easily be mistaken.

avi 2021-04-14T13:36:57.032300Z

This might be a little silly, but in some cases I’ve written functions that catch Exceptions and then return them as values! e.g.

(s/fdef check-render-result
  :args (s/cat :result (s/or :success ::r/success-result
                             :failure ::r/failure-result)
               :path   ::fs/file-path-str)
  :ret  (s/or :success nil?
              :failure (partial instance? Exception)))

alexmiller 2021-04-14T14:06:25.032500Z

function specs document non-exceptional use

alexmiller 2021-04-14T14:06:47.032700Z

so they don't include any way to talk about exceptions

vlaaad 2021-04-14T14:52:12.032900Z

understood

jmromrell 2021-04-14T19:26:00.046Z

I am trying to spec the requests and responses to an API, but am running into issues with namespace collisions.

(ns my-app.validate.endpoint-a)

(s/def :ok-response/status #{200})
(s/def :ok-response/body ...)

(s/def ::ok-response (s/keys :req-un [:ok-response/status
                                      :ok-response/body]))

(s/def :created-response/status #{201})
(s/def :created-response/body ...)

(s/def ::created-response (s/keys :req-un [:created-response/status
                                           :created-response/body]))

(s/def ::response (s/or :ok ::ok-response
                        :created ::created-response))
The problem comes when I add validation for endpoint-b which will also have an :ok-response/body which will be different than that for endpoint-a. I am already encoding the type of response (`ok-response` vs. created-response ) in the kw namespace due to s/keys mandating that the kw name matches the key in the map being spec'd. I can continue to encode further dimensions (endpoint, method) into the namespace to avoid these collisions, but it quickly gets unwieldy:
(s/def :get-endpoint-a-ok-response/body ...)
Am I overlooking an idiomatic way of handling this problem? I'd love to see something like
(s/def ::ok-response-body ...)

(s/def ::ok-response (s/named-keys :req {:status #{200}
                                         :body   ::ok-response-body}))

jmromrell 2021-04-14T19:27:25.046700Z

Note: s/named-keys above could also address support for string keys https://clojure.atlassian.net/browse/CLJ-2196

seancorfield 2021-04-14T19:40:01.047600Z

Spec 2 will provide that functionality @jmromrell if I’m understanding correctly what you are asking.

seancorfield 2021-04-14T19:40:26.048Z

But it sounds like you might also want to look at multi-spec?

jmromrell 2021-04-14T19:44:25.048100Z

That's great. The inflexibility of s/keys has been a pain point for me multiple times.

jmromrell 2021-04-14T19:44:38.048300Z

I'll look into it. Thank you!