Fork me on GitHub
#clojure-spec
<
2017-04-25
>
souenzzo00:04:20

There is this specs:

(s/def ::a integer?)
(s/def ::b string?)
(s/def ::m (s/keys :opt [::b]))
When I try
(s/valid? ::m {::b "b" ::a "b"})
I get :clojure.spec/invalid, explanation: val ["a"] fails... Is it a bug?

souenzzo00:04:26

IMHO it should be valid, once spec just looks for what is defined, and on ::m spec's, there is no ::a

ghadi00:04:42

> When conformance is checked on a map, it does two things - checking that the required attributes are included, and checking that every registered key has a conforming value. We’ll see later where optional attributes can be useful. Also note that ALL attributes are checked via keys, not just those listed in the :req and :opt keys. Thus a bare (s/keys) is valid and will check all attributes of a map without checking which keys are required or optional. https://clojure.org/guides/spec

ghadi00:04:02

@souenzzo this is actually by design

ghadi00:04:11

Specs registered under namespaced keys give the keys global meaning thus are checked whenever they are encountered

ghadi00:04:09

This is different than other systems. (I like it)

mbjarland09:04:36

have a clojure.spec question, assume I have a number of specs defined for seq:s of chars, how would I define a string spec which uses them? i.e given:

(s/def ::my-char-sequence
  (s/cat :valid-chars (s/* (s/and char? #{\a \b}))))
=> my-char-sequence

(s/explain ::my-char-sequence [\a \b])
Success!
=> nil

(s/explain ::my-char-sequence [\a \c])
In: [1] val: \c fails spec: my-char-sequence at: [:valid-chars] predicate: #{\a \b}
=> nil
how would I write a string spec using the char seq spec:
(s/def ::my-string
  (s/and string?
             (??? <something using ::myc-char-sequence and perhaps (seq %) > ???)))

dergutemoritz10:04:02

@mbjarland Inserting (s/conformer seq) after the string? predicate in the ::my-string spec should do the trick!

mbjarland10:04:24

@dergutemoritz !! ahh...I knew there had to be away to make a transformation before handing it off to spec...that is great!! thanks

dergutemoritz10:04:38

@mbjarland You're welcome! Note that when using that with s/conform it'll also return a seq of chars. Maybe you want to add something like (s/conform (partial apply str)) at the end of the chain.

mbjarland10:04:11

seems conformer has a second unform argument function

mbjarland10:04:23

would that fix it

dergutemoritz10:04:34

Yeah but that's for use with s/unform for recovering the original value - slightly different approach with different pros and cons but works, too 🙂

dergutemoritz10:04:36

Another option would be to use string regexen instead of spec regexen

mbjarland10:04:41

yeah, I was hoping not to have to do string regex

mbjarland10:04:52

I like the composability of specs

mbjarland10:04:31

errr...I'm assuming "string regexen" refers to actual regular expression #" " matching?

Adam Munoz10:04:47

Hi everyone. How do you spec :args of any type? i.e. takes x that can be anything and returns for example boolean.

Adam Munoz10:04:31

Ok, any? 🙂

Adam Munoz10:04:37

thanks anyway

mbjarland10:04:16

@adammunoz, I'm a beginner with spec but perhaps something like:

(defn some-fn [x]
  true)

(s/fdef some-fn
        :args (fn [_] true)
        :ret boolean? )
->
(doc some-fn)
-------------------------
string-layout.core/some-fn
([x])
Spec
  args: (fn [_] true)
  ret: boolean?

Adam Munoz10:04:56

@mbjarland Thanks. No I meant that predicate that I want is maybe any? .

Adam Munoz10:04:08

(any? 1) => true

mbjarland10:04:16

: ) yes looks like it

Adam Munoz10:04:19

(any? "whatever") =>true

mbjarland10:04:37

I figured any would be collection based, but you are right

dergutemoritz12:04:24

@mbjarland Yeah that's what I meant by string regexen 🙂

dergutemoritz12:04:45

Note that there are also various libraries which provide a structured representation of regexen similar to that of spec which can be composed just as well and compiled down to java.util.regex.Pattern

mbjarland12:04:47

@dergutemoritz have any clj ones to share?

dergutemoritz12:04:26

@mbjarland I seem to remember having seen multiple ones actually, but I fail to find them again!

mbjarland12:04:43

@dergutemoritz : ) no worries (edit: was on touch screen...they suck)

dergutemoritz12:04:47

I have used Scheme Regular Expressions with great pleasure in the past, though. Perhaps I'm mixing it up.

mbjarland12:04:19

and as a side question, how do you organize specs for functions? Right after the function itself? in another namespace? somewhere else?

mbjarland12:04:11

keeing them close feels good in a sense but also pollutes the code with a lot of "spec noise"...for good or bad

dergutemoritz12:04:04

Personally, I like to put them before the function definition (if you mean fdefs) but I don't have enough confidence in it to recommend it as a good idea or anything 😉

dergutemoritz12:04:29

Also, I'm not specing each and every function just for the sake of it

mbjarland12:04:10

I got another one (starting to feel like a spambot here), do you see any immediate reason why this:

(s/def ::align-specifier
  (s/and char?
         #{\L \C \R \l \c \r}))

(s/def ::bracket-expr
  (s/cat :left-bracket #{\[}
         :align ::align-specifier
         :right-bracket #{\]}))

(s/def ::between-bracket-expr
  (s/cat :between-char
         (s/* (s/and char? (complement #{\[ \]})))))

(s/def ::layout-exprs
  (s/cat :le
         (s/* (s/alt :br ::bracket-expr :be ::between-bracket-expr))))
would result in:
(s/explain ::layout-exprs [\a \b])
StackOverflowError   clojure.core/complement/fn--6675 (core.clj:1438)

mbjarland12:04:08

improvements to the specs also welcome, I can for example see that I could remove the (s/and char? in the first one

mbjarland12:04:00

all of ::align-specifier, ::bracket-expr and ::between-bracket-expr work in isolation

mbjarland12:04:21

as in I can run explain and valid? on them and they behave as I would expect

mbjarland12:04:34

ah...complement...maybe I need to be explicit there instead...

mbjarland12:04:26

I did feel a bit woozy writing (complement #{\[ \}}) : )

dergutemoritz12:04:38

@mbjarland Looks like you're running into an infinite left recursion because ::between-bracket-expr may not consume any input

mbjarland12:04:53

@dergutemoritz ok, I will have to marinate on that a bit. Thanks for the pointer

dergutemoritz13:04:27

Or perhaps not 😄

mbjarland13:04:47

food for marination

mbjarland13:04:13

and yes, changing (s/* to (/s+ in :between-bracket-expr fixes the problem. It probably should have been + to begin with

mbjarland13:04:39

and so I'm lost in the woods again...and not for the first time with spec : ) If I write standalone specs I can run explain/valid?/exercise etc on them. What if I spec a function, do I have to actually instrument the function to test my spec or is there a way to get hold of the function spec and run the normal spec-exercising-functions on it?

mbjarland13:04:48

I guess I can do a s/def on say the args spec and then exercise that separately...

Alex Miller (Clojure team)14:04:19

yes, you can call s/get-spec on the fully-qualified symbol to get the function spec, and the function spec supports keyword lookups for it’s parts (`:args`, etc).

mbjarland14:04:24

@alexmiller ok, nice and data centric as usual. thank you

Alex Miller (Clojure team)14:04:00

(s/fdef user/foo :args (s/cat :a int?))
(s/valid? (:args (s/get-spec 'user/foo)) [100])

Alex Miller (Clojure team)14:04:37

or you can go the other way and define the args spec as a standalone spec and then assemble the fdef spec from it (I’ve done this in some cases)

mbjarland14:04:22

that's what I ended up with, but still good to know it's not a black hole and you have access to the function spec

mbjarland14:04:53

I find my biggest hurdle with spec so far is not understanding the language but understanding its place in the process and best practice use. Is there for example still a place for using specs as preconditions in functions? I guess it would be nice to see an example of a larger production system using spec (or a similarily reality checked code base) and what kind of usage patterns they ended up with. Any references to presentations or repos with such code base much appreciated....

sparkofreason16:04:59

Is there any way to use coll-of or every (or some other existing capability in spec short of writing custom predicates and generators) to spec non-Clojure collections, like java.util.HashSet? I feel pretty certain the answer would be "no", but just want to double-check before reinventing any wheels.

dergutemoritz17:04:08

@dave.dixon Since most (all?) Java collections are seqable, you should be able to make it work by anding in a seq conformer

sparkofreason17:04:53

@dergutemoritz thanks. Looks like even every works if you specify :kind to be a custom predicate, but still doesn't generate. And just verified every-kv doesn't seem to work with :kind, and keys doesn't give any hooks for custom collections, as far as I can tell. Which seems sensible - can't support everything possible in the JVM with a single API.

dergutemoritz17:04:28

@dave.dixon Oh, right, generation isn't covered by my suggestion either. Note what the doc string says about a custom :kind predicate, though: > :kind - a pred/spec that the collection type must satisfy, e.g. vector? (default nil) Note that if :kind is specified and :into is not, this pred must generate in order for every to generate.

dergutemoritz17:04:36

Hm but I doubt that passing something like :into (HashSet.) will just work either 🙂

sparkofreason17:04:46

@dergutemoritz not documented, but the collection must also support the Clojure interface for supporting into.

dergutemoritz17:04:11

Yeah, seems reasonable

dergutemoritz17:04:27

I doubt that spec is of much use for mutable collection types

sparkofreason17:04:40

Actually, I want to use it for bifurcan collections.

Alex Miller (Clojure team)18:04:09

covering Java colls was not really a goal of the spec coll preds

Alex Miller (Clojure team)18:04:29

I can’t say we’ve really talked about it either way though. doesn’t seem like there’s any technical reason such a thing couldn’t exist. not sure if there are performance concerns in growing/shrinking non-persistent colls in generators.