clojure-spec

Nim Sadeh 2024-04-10T13:20:11.151439Z

I was a bit surprised at what happens when you combine spec with pre/post conditions. I use this sparingly at the boundaries of API calls/object coercions. I expected failed coercions to give useful information, but it doesn't. Is there a better way to guard structured at the boundaries? I ended up writing the following util function that does what I was hoping that pre/post conditions conditions would do, but I feel like I am doing something "wrong" by not just using the built-in functions. Why do they behave like that, and why is what I am doing not a good idea?

(defn defend-spec
  [spec candidate]
  (if-let [explained (s/explain-data spec candidate)]
    (throw (ex-info "Object failed to conform to spec" explained))
    candidate))

seancorfield 2024-04-10T15:53:54.253279Z

pre/post use assertions which are Error in the Throwable hierarchy and really aren't expected to be caught. ex-info produces an Exception which is reasonable to catch. I don't see pre/post used much in the wild, partly because assertions are generally "stop the world" (and a lot of people think they should be turned off in production -- I disagree), and partly because they don't produce very good information. Some people will do this instead: (assert (s/valid? spec candidate) (s/explain-str spec candidate)) -- that will still be an Error but at least the message will now be a useful(?) explanation of the failure. Note that some people think it's perfectly fine to catch Throwable but you'll see a lot of advice saying don't do that, only catch Exception.

Nim Sadeh 2024-04-11T14:50:05.044529Z

Why would they be turned off? I probably want the world to stop if data coming in outside my system is wrong. I can't necessarily predict what it will do in my database

seancorfield 2024-04-11T15:15:24.357219Z

Well, exactly... But some folks think assertions are for dev/test only 🤷🏻‍♂️

escherize 2024-04-16T18:59:42.047089Z

Performance is one reason someone might want to turn them off