Fork me on GitHub
#clojure-spec
<
2019-02-26
>
Audrius12:02:15

how to make spec for list of maps? '({}{}{}) ?

mpenet12:02:59

(s/coll-of map?)

mpenet12:02:24

but most of the time you want something more precise than map?

Audrius12:02:54

but how to make that the maps also conforms to a spec?

mpenet12:02:20

replace map? with a namespaced keyword that points to a spec

👍 5
mpenet12:02:35

or a spec directly

jsa-aerial15:02:24

Any reason why (s/explain-data a-spec a-value) does exactly what is expected (and is correct) and given the exact same input (s/valid? a-spec a-value) fails with a class cast exception? This is on 1.9

borkdude15:02:46

do you have the spec and example data? did you test in clj 1.10?

jsa-aerial16:02:45

@favila doesn't look to be that (might be peripherally related). I think the issue is related to a misuse of s/merge in my case. I have a set of predicates def'd to specs. None of these are map/key related (like with s/keys) so none involve a map. Then I use s/merge (because s/and short circuits...) on these. Now, s/merge is happy to do this. s/explain-data is happy to do 'the right thing' and check all predicates of the merged spec. But s/valid? has a bug as well, but instead of 'doing the right thing' it just blows up. I put 'right thing' in quotes because I suppose s/merge should not allow this in the first place. All of this is a shame - s/and should not be short circuiting in the first place. This is a logic system after all. If you are concerned about guarding flow through to other predicates then your predicates are what's broken. I suppose you could have a guard operator for that sort of case, but IMO, that would still be bogus. ¯\(ツ)

favila16:02:36

why would you want s/and not to short-circuit?

jsa-aerial16:02:28

Because you are saying that the spec should satisfy all predicates. Like in propositional logic. Not programming languages

jsa-aerial16:02:22

The reason this is really useful is because you would like to catch all the errors in one go - not get one report to user. fix that one, get the next, report to user, etc. That is really annoying and just wrong

favila18:02:21

but s/and accepts specs and conformers

favila18:02:33

not just predicates

favila18:02:45

well, nm just conforming is a problem

favila18:02:09

so you would have to drop conformers, or introduce some trickery about evaluation

favila18:02:56

even if all were evaluated order would still have to matter

seancorfield18:02:00

@jsa-aerial s/and flows data through all the specs so it must be short-circuiting -- it has an inherent order and you can't rely on being able to apply subsequent specs if an earlier spec fails. (and I feel we've had this conversation before)

jsa-aerial19:02:21

It is what it is - I think if you wanted that 'guarded flow` capability that another name would have been far better. I don't really care TBH, I just think it was a poor choice.

misha19:02:44

Yeah, each next spec in s/and receives conformed value from prior one. So if one step is invalid – next would just blow up with random noise instead of useful explain data.

misha19:02:09

otherwise how would you s/conform to s/and spec? conform only last step? none? Most of the s/and specs I saw – check fields first, and then some relation between them next (e.g. this id and that id are the same.), which is basically: 1st step is conforming, last is not

favila19:02:56

if s/and were restricted to predicates, and predicates were written defensively (which they should be IMO), then I think what @jsa-aerial proposes would be fine; order could be used for making generators, but all could be checked in parallel

misha19:02:43

how do you restrict to predicates only?

favila19:02:53

I mean if conformers were not legal

favila19:02:05

bascially, you would have to drop conformers as a feature of s/and

misha19:02:12

well, this excludes s/keys opieop

misha19:02:17

or anything containing s/or

favila19:02:05

I don't follow?

favila19:02:05

I'm just talking about s/and. Maybe for clarity I should say this would not be s/and anymore. s/nonconforming-and or s/parallel-and

jsa-aerial19:02:21

It is what it is - I think if you wanted that 'guarded flow` capability that another name would have been far better. I don't really care TBH, I just think it was a poor choice.

favila19:02:56

s/all maybe

misha19:02:20

I'd call it s/every or something. But "flowing" behavior was surprising to me back then, I agree

misha19:02:23

but, how would such s/every be more useful than clojure.core/and?

favila19:02:43

every predicate would be tested unconditionally

favila19:02:53

I should say, could be

borkdude19:02:08

is this for form validation or something?

favila19:02:13

I'm guessing

misha19:02:20

ok, clojure.core/every-pred

favila19:02:24

something user-facing

jsa-aerial19:02:14

Not necessarily, could be anything. You just want to be able to report all findable problems at once - not piecemeal

favila19:02:41

but I haven't found spec very useful for things like that

jsa-aerial19:02:57

Actually it works amazingly good when coupled with phrase

misha19:02:50

what extra would it provide?

favila19:02:59

report all errors instead of just the first one

borkdude19:02:01

maybe build your tool on top of spec, but not use s/and to collect all the errors. just call s/explain for each field for example

misha19:02:08

you can't exercise it, can you? because it involves conforming

favila19:02:45

conforming and excercising seem orthogonal?

jsa-aerial19:02:14
replied to a thread:something user-facing

Not necessarily, could be anything. You just want to be able to report all findable problems at once - not piecemeal

misha19:02:14

exercise gives you pairs of [generated conformed], I think?

jsa-aerial19:02:57

Actually it works amazingly good when coupled with phrase

favila19:02:17

the conformed of s/every would always be identity

favila19:02:27

or whatever, s/every-pred

misha19:02:33

I think you'll be able to implement such s/every on top of spec2

favila19:02:35

no conforming

favila19:02:01

I'm more curious what the explain-data would look like

misha19:02:43

ok, what if one of the items in s/every would be spec with s/and inside? it'll propagate conformed value, I think

seancorfield19:02:01

It's worth pointing out that Spec2 draws a harder line between specs and predicates -- and s/and accepts specs, not arbitrary predicates. See https://github.com/clojure/spec-alpha2/wiki/Differences-from-spec.alpha

favila19:02:02

the point is, this spec would simply not do that

misha19:02:10

well yeah, explain - "conforms"

seancorfield19:02:19

(! 591)-> clj
Clojure 1.10.0
user=> (require '[clojure.spec.alpha :as s])
nil
user=> (s/def ::foo (s/and int? (partial > 10)))
:user/foo
user=> (s/valid? ::foo 10)
false
user=> (s/valid? ::foo 1)
true
user=> ^D

Tue Feb 26 11:22:41
(sean)-(jobs:0)-(~/clojure)
(! 592)-> clj -A:spec2
Clojure 1.10.0
user=> (require '[clojure.spec-alpha2 :as s])
nil
user=> (s/def ::foo (s/and int? (partial > 10)))
:user/foo
user=> (s/valid? ::foo 10)
Execution error (IllegalArgumentException) at clojure.spec-alpha2/spec* (spec_alpha2.clj:197).
No method in multimethod 'create-spec' for dispatch value: clojure.core/partial
user=> 

misha19:02:30

@seancorfield I don't see "not arbitrary predicates" there. It says "Qualified symbols (predicate function references)" are ok

seancorfield19:02:47

@misha See the code I just posted above.

misha19:02:41

what if you defn it first, instead of partial?

favila19:02:17

probably it needs an fspec

favila19:02:23

(just guessing)

favila19:02:27

which sounds great to me

borkdude19:02:49

(s/def ::foo (s/and #(int? %) #(> 10 %)))
works in spec-alpha2

misha19:02:02

I think it should be orthogonal: fdef and predicate fn

seancorfield19:02:22

Yes, you can def a predicate from (partial > 10) and then it falls into the set of things that can be treated as a symbol spec. But the point I'm making is that s/and isn't intended to operate on predicates, it's for specs.

misha19:02:51

in spec2? or in both 1 and 2?

seancorfield19:02:58

We got our entire codebase up and running on Spec2 and we had several places where we had "mixed" specs and predicates and those were rejected and we had to rewrite things somewhat.

favila19:02:15

so a def-ed predicate is a spec

misha19:02:24

most of my use cases for s/and were s/keys + fn

jsa-aerial19:02:25

If you have s/keys you can use s/merge which does satisfy all semantics

favila19:02:37

well symbolic spec, not spec object

borkdude19:02:47

@favila see above. anonymous functions are allowed, but you have to write them with fn or #(… %)

favila19:02:17

that doesn't jive with the doc @seancorfield linked

borkdude19:02:35

To be honest, I found it confusing too, but that’s how it works now

misha19:02:59

also, my impression was, that any predicate is sort of first class in spec by design

seancorfield19:02:19

It's an important (but subtle) distinction. Spec1 doesn't enforce it but Spec2 does.

favila19:02:50

what about michiel's counter-example?

seancorfield19:02:13

What counter-example?

borkdude19:02:22

(fn [x] int?) is a “symbolic spec”, it’s just what they call it now

favila19:02:39

"fn" is a spec form?

borkdude19:02:40

but just simple symbol isn’t, like int?

misha19:02:40

I see how spec2 could disallow fns returning fns as predicates (partial), but forbidding anonymous fns would be strange (and doing it only for few spec forms – just awkward)

borkdude19:02:24

I think this will be a source of much confusion.

jsa-aerial19:02:25

If you have s/keys you can use s/merge which does satisfy all semantics

seancorfield19:02:25

int? is acceptable as a symbolic spec.

favila19:02:58

> Spec forms (lists/seqs), with spec op in function position, composed of other symbolic specs > Qualified keywords (names that can be looked up in the registry) > Qualified symbols (predicate function references) > Sets of constant values (an enumeration)

favila19:02:16

unless "fn" is a spec form, I don't see how anonymous inline fns would be allowed

favila19:02:33

even then that seems iffy

misha19:02:47

@jsa-aerial s/merge is for "map-validating specs", it's not for things like "also check that value in [:foo :bar] is the same as in [:baz]" but s/and - is exactly for that

jsa-aerial19:02:22

Yes, I know that - just saying it does satisfy all semantics

misha19:02:46

which "all semantics"?

seancorfield19:02:01

He means "not short-circuiting"

jsa-aerial19:02:05

If you merge a bunch of specs it check them all

misha19:02:34

I meant, it is (s/and keys-spec custom-pred) or (s/and regex-spec custom-pred)

misha19:02:17

keys-spec could be (s/merge keys-spec1 keys-spec2 ...), yes. but important part is a custom-pred, which is usually (for me) goes last, and does not conform anything, but often requires conformed input, in case there are branches with different "postvalidation" semantics required

ikitommi20:02:08

a new guide on coercion with spec1 and spec-tools: https://cljdoc.org/d/metosin/spec-tools/0.9.0/doc/spec-coercion

🔥 30