Preconditions are already assertions, so it makes more sense to use s/assert in your code body than in a precondition.
On Wednesday, June 7, 2017 at 8:12:22 AM UTC-5, David Goldfarb wrote: > > One big downside of using s/assert in a precondition: It does not work > with (s/nilable ...) specs, since s/assert returns valid values. > > I fell into this trap for a moment of head-scratching just now. > > On Wednesday, September 14, 2016 at 4:59:09 PM UTC+3, Alex Miller wrote: >> >> Another option that has been added since the guide was written is >> s/assert which seems closer to what you're suggesting. >> >> (defn name [user] >> {:pre [(s/assert :common/user user)]} >> (-> user :user/name)) >> >> ;; need to enable assertion checking - this can also be enabled globally >> with system property clojure.spec.check-asserts >> (s/check-asserts true) >> >> (name {:user/name "Elon"}) >> "Elon" >> >> (name {:x "Elon"}) >> ExceptionInfo Spec assertion failed >> val: {:x "Elon"} fails predicate: (contains? % :user/name) >> :clojure.spec/failure :assertion-failed >> clojure.core/ex-info (core.clj:4725) >> >> Rather than use it in a precondition, you can also use s/assert directly >> in the code. >> >> On Wednesday, September 14, 2016 at 7:37:24 AM UTC-5, >> joakim.t...@nova.com wrote: >>> >>> (ns spec-test.core >>> (:require [clojure.spec :as s])) >>> >>> (s/def :user/name string?) >>> (s/def :common/user (s/keys :req [:user/name])) >>> >>> ; first version of name (using :pre) >>> (defn name [user] >>> {:pre [(s/valid? :common/user user)]} >>> (-> user :user/name)) >>> >>> ; This statement works ok and returns "Elon": >>> (name {:user/name "Elon"}) >>> >>> ; but this statement... >>> (name {:x "Elon"}) >>> >>> ;...will throw: >>> CompilerException java.lang.AssertionError: >>> Assert failed: (s/valid? :common/user user) >>> >>> ; ...but then I don't get as much information >>> ; about the error as if I would have called: >>> (s/explain :common/user {:x "Elon"}) >>> >>> ;...which also contains the predicate: >>> val: {:x "Elon"} fails spec: :common/user >>> predicate: (contains? % :user/name) >>> >>> ; (second version of name - more verbose) >>> ; or do I need to wite it like this: >>> (defn name [user] >>> (let [parsed (s/conform :common/user user)] >>> (if (= parsed ::s/invalid) >>> (throw (ex-info "Invalid input" (s/explain-data :common/user user))) >>> (-> user :user/name)))) >>> >>> ; so that: >>> (name {:x "Elon"}) >>> >>> ; ...will return: >>> CompilerException clojure.lang.ExceptionInfo: >>> Invalid input #:clojure.spec{:problems} >>> ({:path [], :pred (contains? % :user/name), >>> :val {:x "Elon"}, :via [:common/user], :in []}) >>> >>> ; It should be nice if I could be able to write it like this >>> ; (or similar, to get a better error message): >>> (defn name [user] >>> {:pre [(s/explain :common/user user)]} >>> (-> user :user/name)) >>> >>> -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en --- You received this message because you are subscribed to the Google Groups "Clojure" group. To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.