Fork me on GitHub
#clojure-spec
<
2017-10-15
>
Chris O’Donnell01:10:42

am I missing something about conformers? I'm confused as to why (s/valid? (s/conformer (fn [_] :clojure.spec/invalid)) nil) is true. If the conformer function returns :clojure.spec/invalid, shouldn't the value be invalid?

Chris O’Donnell01:10:50

ah, it should be clojure.spec.alpha/invalid or just ::s/invalid 🦆

jumar07:10:35

what exactly is the relationship/difference between clojure.spec.gen.alpha and clojure.test.check.generators? So far, I've found two differences: 1. missing nat generator in clojure.spec.gen.alpha 2. cannot reference generators directly, but has to call them as functions: e.g. with clojure.test.check.generators I can do this:

(gens/hash-map
   :boolean gens/boolean
   ;; note: `nat` generator is not in spec 
   :small-integers (gens/vector gens/nat)
   :large-integer  gens/large-integer
   :double         gens/double
   :color          (gens/elements [:red :green :blue])
   :uuid           gens/uuid
   :string-or-keyword (gens/tuple gens/string gens/keyword))
But with clojure.spec.gen.alpha I have to do this:
(gen/hash-map
   :boolean (gen/boolean)
   ;; note: `nat` generator is not in spec 
   :small-integers (gen/vector gens/nat)
   :large-integer  (gen/large-integer)
   :double         (gen/double)
   :color          (gen/elements [:red :green :blue])
   :uuid           (gen/uuid)
   :string-or-keyword (gen/tuple (gen/string) (gen/keyword)))

jumar07:10:43

clojure.test.check.generators/let is also missing in clojure.spec.gen.alphanamespace

jumar07:10:48

Related to the 2.) When I actually try to use (gens/double) it throws an exception so the usage pattern is a bit inconsistent

gfredericks13:10:03

what kind of exception?

jumar18:10:33

1. Unhandled java.lang.ClassCastException
   clojure.test.check.generators.Generator cannot be cast to clojure.lang.IFn

jumar18:10:22

I don't have anything against either approach, I just thought that they behave in the same way O:-)

gfredericks18:10:55

oh, you mean between the two namespaces it's inconsistent yes absolutely, and that's unfortunate

jumar07:10:28

btw. @gfredericks thanks a lot for the great Conj talk. I missed it at the conference but watched it now. Really useful!

jumar07:10:56

just a small thing. imho, gen/large-integer doesn't accept min/max options. I had to use gen/large-integer* (slide 43). Funny thing is that if you use clojure.spec.gen.alpha/large-integer it doens't throw an exception but generates wrong data

gfredericks13:10:50

that's disappointingly error-prone

gfredericks13:10:36

the other thing is definitely a slide typo 😞

gfredericks13:10:15

my org-mode⇒beamer workflow is not yet sophisticated enough to automatically verify that the code works

stathissideris13:10:19

what do people normally do for spec’ing var-args

stathissideris13:10:43

do they put the whole thing in a s/?

stathissideris13:10:07

it feels slightly wrong because that means that it’s all or nothing

stathissideris13:10:15

ok some context: I’m trying to extend spec-provider to support inference of function specs, and when it comes to varargs, if I see an example of a function call where the varargs happen to be 1 "foo" I’m not sure if I should infer a spec of (s/cat ... :rest (s/? (s/cat :integer integer? :string string?)))

stathissideris13:10:43

which says that there are up to 2 varargs but they’re both optional

stathissideris13:10:11

feels like it’s more restrictive than the function signature

gfredericks13:10:32

if all you know is one example call, I don't think you can know very much at all for sure

stathissideris13:10:34

but even if I get more calls, I’m not sure what’s the “best” spec to infer

stathissideris13:10:57

for example let’s say I get one more example where the varargs is just 20

stathissideris13:10:21

does it then become (s/cat ... :rest (s/? (s/cat :integer integer? :string (s/? string?))))

stathissideris13:10:09

or maybe I always wrap the individual var-args with s/? because they’re optional

gfredericks14:10:24

there's no right answer, it's all heuristics

gfredericks14:10:39

which I guess means I have no further advice 🙂 those kinds of problems make me just throw up my hands and give up

stathissideris14:10:58

they are hard, but they make you think hard about idioms and coder expectations

stathissideris14:10:14

I had to do a lot of that for spec-provider

stathissideris14:10:05

what’s the difference between s/alt and s/or?

stathissideris14:10:19

I saw Ambrose used alt for multiple arities

stathissideris14:10:29

I’ve always used or

taylor14:10:02

;; unlike `or`, `alt` works on sequence elements, not on values themselves!

taylor14:10:13

the example here https://clojuredocs.org/clojure.spec/alt makes that a little more obvious

stathissideris15:10:06

ok, so it’s like with alt you have an implicit s/cat around each option

taylor15:10:26

I think it's more like all the "regex" spec types (`cat`, alt, *, +, ?) are specifically meant to specify elements of a sequence. I wouldn't say there's an implicit cat around alt or any of the regex specs, they all work on sequences whether you use them alone or in combination

taylor15:10:10

I'm probably missing some context for your use case though? Writing specs for multi-arity functions?

taylor15:10:26

The fdef docstring has a nice example:

(s/fdef clojure.core/symbol
  :args (s/alt :separate (s/cat :ns string? :n string?)
               :str string?
               :sym symbol?)
  :ret symbol?)

taylor15:10:46

Using an alt case for each arity

stathissideris15:10:42

ok, but apart from conciseness, what’s the benefit over s/or in this case?

stathissideris15:10:56

(Yes, I’m writing specs for multi-arity functions)

taylor15:10:10

(s/fdef clojure.core/symbol
        :args (s/or :separate (s/cat :ns string? :n string?)
                    :str (s/cat :str string?)
                    :sym (s/cat :sym symbol?))
        :ret symbol?)

stathissideris15:10:55

yeah, it’s longer, but are there any other disadvantages?

taylor15:10:50

Not sure what other disadvantages there might be. The :args spec isn't just longer with or, it's more complex. They also produce slightly different output e.g. when incorrectly calling a instrumented function

taylor15:10:46

The or version has additional, unnecessary tags in its explanation

taylor15:10:42

from the :clojure.spec.alpha/problems: :path [:args :sym] vs :path [:args :sym :sym]

Alex Miller (Clojure team)15:10:22

Regex specs compose together to describe a single sequential context and are generally preferred for describing args

Alex Miller (Clojure team)15:10:28

Generally it's best to use a top level cat as you will then get a map conformed to component names that you can use in the :fn spec

stathissideris15:10:19

@U064X3EF3 but are different arities a “single sequential context”?

Alex Miller (Clojure team)15:10:35

For any given case, yes

Alex Miller (Clojure team)15:10:49

I would actually write that example now as

Alex Miller (Clojure team)15:10:58

Sorry, I can't type it all, on the phone

stathissideris15:10:20

no problem, would be grateful to have your example when you get some time

Alex Miller (Clojure team)15:10:38

Use alt for alternatives, ? for optional args, * for varargs etc. you can describe any set of arities I've encountered with a regex spec

stathissideris15:10:22

alright, thanks

stathissideris15:10:44

on a bit of tangent: what about keyword args?

Alex Miller (Clojure team)19:10:42

Use keys* - that's what it's for

stathissideris08:10:10

ok, many thanks!