Fork me on GitHub
#clojure-spec
<
2018-11-16
>
stijn13:11:24

is there a way to do something like this (s/def ::string-or-number (apply s/or [:string string? :number number?])) or is it macros to the rescue for e.g. creating a combined spec from a list of regexes?

mpenet13:11:06

you can do this with eval

mpenet13:11:22

(eval `(s/or ~@[:a any? :b any? :c any?]))`

borkdude13:11:52

@stijn apparently (s/def ::string-or-number (s/or #?@(:default [:string string? :number number?]))) also works

borkdude14:11:32

(ab)using reader conditionals

Alex Miller (Clojure team)14:11:20

this stuff is better in spec-alpha2

👍 12
8
Alex Miller (Clojure team)14:11:36

but don’t have time to talk about it

mpenet14:11:11

eager to see the new stuff

mhuebert14:11:16

When using fdef, are the arguments validated by :args supposed to be affected by the destructuring forms present in the function argslist?

mhuebert14:11:21

eg. in the following, the fact that I am using & [doc] to read the 2nd arg seems to affect how instrument works

(defn register-key
  [key & [doc]])

(s/def ::register-args (s/cat :key keyword?
                              :doc (s/? string?)))

(s/fdef register-key
        :args ::register-args)

(st/instrument)

(s/valid? ::register-args
          [:hello "there"])
;; => true

(register-key :hello "there")
;; => throws

taylor14:11:19

here's an example I have for a similar variadic function:

(defn slarp [path & [blurp? glurf?]]
  (str path blurp? glurf?))
(s/fdef slarp
  :args (s/cat :path string?
               :rest (s/? (s/cat :blurp? int?
                                 :glurf? boolean?))))
(s/exercise-fn `slarp)
=>
([("") ""]
 [("" -1 false) "-1false"]
 [("e7" 0 true) "e70true"]
 [("du9") "du9"]
 [("K1" -6 false) "K1-6false"]
 [("op0" -3 false) "op0-3false"]
 [("2y" 2 false) "2y2false"]
 [("qaGWVK") "qaGWVK"]
 [("9h1Rh") "9h1Rh"]
 [("") ""])

mhuebert14:11:47

I think I found that same example in my searching

taylor14:11:18

I think your example specs a fixed, 1- or 2-arity function instead of a variadic one that just destructures the first add'l arg

taylor14:11:30

which is why it works when you rewrite the function with fixed 1- & 2-arity

mhuebert14:11:43

instrument also fails when I try to use it with your slarp example

mhuebert14:11:48

(defn slarp [path & [blurp? glurf?]]
  (str path blurp? glurf?))

(s/fdef slarp
        :args (s/cat :path string?
                     :rest (s/? (s/cat :blurp? int?
                                       :glurf? boolean?))))

(st/instrument)

(slarp "a" 1 true)
;; => throws

mhuebert14:11:38

I can’t say I understand why changing the destructuring form used by the function should affect how the :args spec works

taylor14:11:37

the difference is that one version is variadic and one isn't, not how you choose to destructure the variadic version

taylor14:11:28

the slarp example doesn't throw an exception (on my machine)

mhuebert14:11:33

maybe it’s a ClojureScript difference

taylor14:11:50

yes, there is a difference there, I believes there's a JIRA for it

taylor14:11:04

FWIW I'd go with the fixed-arity version regardless of CLJS spec bug :man-shrugging:

taylor14:11:39

it's less ambiguous about how many args the caller can pass

mhuebert14:11:20

the actual use case is more like (defn register-key [key & [doc options data]) where both doc and options are optional

👍 4
mhuebert14:11:14

i’d like to have them show up in argslist (for eldoc etc) but in smaller arities it isn’t known whether the 2nd option would be doc, options, or data

mhuebert14:11:46

but i just started writing in this way recently and wasn’t sure if it is a great idea

taylor14:11:03

I might also consider kwargs-style variadic function in that case too

taylor14:11:19

(defn register-key [key & {:keys [doc options data]})

mhuebert14:11:59

that would work.. i was hoping to keep it feeling more like def / defn

mhuebert15:11:29

looks like there is another bug with variable-arity

mhuebert15:11:06

(defn defx [key & [doc data]])

(s/def ::defx-args (s/cat :key keyword?
                          :doc (s/? string?)
                          :data map?))

(s/fdef defx
        :args ::defx-args)

(st/instrument)

(s/valid? ::defx-args [:a "" {}])
;; => true

(s/valid? ::defx-args [1 1 1])
;; => false

(defx :a "" {})
;; => not thrown (expected)

(defx 1 1 1)
;; => not thrown (but should throw)

mhuebert15:11:31

now it only validates the length of the arg sequence but doesn’t validate the members

mhuebert14:11:24

if I rewrite register-key in multi-arity form, the same spec passes, eg

(defn register-key
  ([key])
  ([key doc]))

pablore15:11:10

How do you spec xforms?

pablore15:11:52

ie:

(def sort-dates
  (comp (map #(update % :date parse-date))
             (filter #(nil? (:date %)))
             (partial sort-by :date)))

borkdude15:11:07

@pablore for now we use ifn? in speculative, it’s a little (too) general though…

pablore15:11:07

Can I still specify args and ret?

jimbob16:11:19

do i need to require the namespace of the specs im using?

jimbob16:11:30

example if in my ns i have :blah.spec/reward

jimbob16:11:37

do i need to require blah.spec?

jimbob16:11:42

in the ns that calls it?

borkdude16:11:16

@ben.borders no, but you need to call the ns in which the spec was defined to be able to use it

jaihindhreddy-duplicate17:11:05

I want to study the spec source and understand it. Should I study spec.alpha or spec-alpha2?

seancorfield17:11:43

@jaihindh.reddy The only difference between those right now is the latter has the macro implementations refactored out as functions, that the macros then call.

seancorfield17:11:53

There are several bugs in spec-alpha2 right now preventing it from actually being used but the code itself is fine to read for learning purposes... although I'm not quite sure what you'll really get from the source, as opposed to the guide and reference material...?

seancorfield17:11:53

(like many parts of Clojure itself, the source is kinda gnarly and does not represent "typical" Clojure code nor, often, "best practice"... and that's fairly typical of a compiler and its runtime/standard library)

jaihindhreddy-duplicate17:11:52

I've recently started to read the Clojure source. Wanted to do that for spec too. Gotta say, it is pretty gnarly.

jimbob17:11:51

this doesnt make sense to me

jimbob17:11:58

got a

java.lang.Exception: Unable to resolve spec: :facts.rewards.specs.reward-award/award

jimbob17:11:09

but then i explicitly required the namespace

jimbob17:11:15

[facts.rewards.specs.reward-award]

jimbob17:11:17

and now it works

jimbob17:11:30

but other specs do not require the spec namespace to be required?

jimbob17:11:38

FWIW here is the spec:

(s/def ::awards (s/coll-of :facts.rewards.specs.reward-award/award))

schmee17:11:13

which specs are you referring to as “other specs”?

jimbob17:11:36

the other specs i have defined.. i dont want to enumerate all of them, but they dont require the namespace of the spec they are calling to be explitly required

jimbob17:11:49

ex:

(s/def ::qualification
  (s/and (s/keys :req-un [::action :facts.rewards.specs.qualification-conditions/conditions])
         #(qc/has-completeness-condition? %)))

schmee17:11:55

maybe those namespaces are required somewhere else in the code?

schmee17:11:17

specs use a global registry, so as soon as a namespace is required in on place it will populate the registry with all its specs

jimbob17:11:48

ah so it needs to be required somewhere?

jimbob17:11:01

otherwise its not populated in the registry

jimbob17:11:19

so i must not be requiring the ns then in src code