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 
> <javascript:> 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.

Reply via email to