Fork me on GitHub
#clojure-spec
<
2023-01-20
>
Colin P. Hill19:01:49

Does anyone have any usage examples where s/and's chained conforming behavior was useful? I find I almost always have to work around it with a lambda or s/nonconforming because the functions and specs I'm composing the s/and from don't expect conformed input, and that makes me wonder if I'm missing something about the intended usage patterns.

colinkahn19:01:03

Conceptually the same, clojure.spec.alpha itself uses that for keys* using &- https://github.com/clojure/spec.alpha/blob/master/src/main/clojure/clojure/spec/alpha.clj#L1810

thanks3 2
Alex Miller (Clojure team)20:01:49

I find it is often useful when chaining predicates. the main thing that makes it not useful most often are when you put something in it that gives you a conformed value that differs from the original (particularly s/or). I'd be curious what the most common cases are for you when it's an issue. in spec 2, there is a non-flowing s/and- (name subject to change)

Colin P. Hill21:01:17

I'll dig around in my code in a minute to see if I can find a succinct and reasonable example – my current project isn't so much a useful product as it is a playground to get a feel for what can be done with specs beyond simple validation, so most of the stuff in my immediate reach is kinda ramshackle and/or at the edges of what is intended. But yeah, s/or is the main culprit. Non-transforming conformations aren't an issue – if every spec in the chain simply returned its argument, s/and would be indistinguishable from its non-flowing variant.

Colin P. Hill21:01:54

But also, non-transforming conformations are less interesting. Conformation is one of the main things I'm trying to get a feel for right now. I found the Jira tickets where you discouraged one of the spec-tools maintainers from viewing specs as a coercion engine, so atm I'm trying to explore what kinds of transformations are supported, and how they can be used.

Alex Miller (Clojure team)21:01:38

I think it’s likely that we will add a nonconforming or option - that’s the most common need. Mostly wondering if there’s anything else

colinkahn21:01:05

> Conformation is one of the main things I'm trying to get a feel for right now. From various talks I've interpreted conformation being suggested in two places: • To make use of existing specs (like the keys* example conforms the tuples into a map for the existing keys spec). • In macros, where you're parsing at compile time for syntax reasons.

colinkahn21:01:11

(I think those were probably talks by @U064X3EF3 so I'm sure he could say if those are accurate or not 😄)

Colin P. Hill21:01:04

> Mostly wondering if there’s anything else Probably not. Maybe alt, being the regex equivalent of the same. The other transformers don't really make things more deeply nested than they were before, so they're not usually an inconvenience. The case that prompted the question is a little unusual – I have a multi-spec in an and, so while there's a contract on what that multi-spec is supposed to represent, I can't necessarily be sure how it will conform (and since conformation output can be non-obvious I don't necessarily want to include that in the contract and count on it being obeyed). Easy solution in nonconforming, but it got me thinking that I may not rightly understand why and is designed the way it is and might not be using the tool in the best way.

Alex Miller (Clojure team)21:01:11

the purpose of conforming is to tell you why and how a value matched a spec

Colin P. Hill21:01:14

> In macros, where you're parsing at compile time for syntax reasons. Parsing is kinda what I've been fixing on for my current understanding (not just macros, could also be for data-oriented APIs). It seemed like standard conformations all aimed to describe the implicit structure of their input while generally leaving the scalars at the leaves of the tree alone – exactly what a parser does with a stream of tokens, just here with potentially more initial structure than just a stream.

Alex Miller (Clojure team)21:01:10

the "why" part is why s/or and s/alt tell you which path was taken, why s/cat separates it's concatenated parts, etc

Alex Miller (Clojure team)21:01:47

s/and's flowing is extremely useful when chaining predicates (where typically the value is not not modified, just passed through) ala (s/and int? pos? even?)

Colin P. Hill21:01:40

How does that differ from how the non-flowing variant will behave? I thought conforming with a predicate function was essentially an identity operation, so here even? receives the same value that pos? did

Alex Miller (Clojure team)21:01:44

the original value (not the conformed value) is passed through s/and-

Alex Miller (Clojure team)21:01:01

for specs that are pred fns, doesn't affect conform (but does affect gen)

Colin P. Hill21:01:51

Oh interesting. I'm not immediately seeing how it affects gen, could you say more on that?

Alex Miller (Clojure team)21:01:06

it's explained in that blog

Colin P. Hill21:01:18

Ah sorry, skimmed poorly

Alex Miller (Clojure team)21:01:25

well s/and- is - it only gens from the first predicate. s/and gens, then filters

Alex Miller (Clojure team)21:01:11

I expect you will often need a custom generator with s/and-

Colin P. Hill21:01:29

Does this mean that and- will be an exception to the general rule that a spec's generator retries generation until it gets something that conforms to the (whole) spec? Or does and do some more proactive filtering ahead of that step with its 2nd through last terms?

Alex Miller (Clojure team)21:01:00

I don't think it's an exception - they will all try 100 times and all spec generators check s/valid? on the generated values

Alex Miller (Clojure team)21:01:56

s/and-, by not using anything but the original pred, is less likely to generate valid values though

Colin P. Hill21:01:11

Got it, that makes sense. I wasn't aware that the original flavor built its underlying pre-verification generator out of more than one of its terms

Colin P. Hill21:01:08

I'll have to read the implementation at some point, because it's not immediately obvious to me how that could even be done :thinking_face:

Alex Miller (Clojure team)21:01:55

generate values from the first pred. only keep ones that satisfy 2nd pred. only keep those that satisfy 3rd pred, etc.

Colin P. Hill21:01:21

Just a chain of such-thats so the generation tooling handles filtering before the spec does its final check?

Alex Miller (Clojure team)21:01:54

yes, so it helps to think about the ordering of preds in s/and

Colin P. Hill21:01:38

ack, my poor skimming embarrasses me again

Alex Miller (Clojure team)21:01:52

I forgive you, there is a lot to read :)