Fork me on GitHub
#clojure-spec
<
2019-04-07
>
didibus03:04:14

> But we are working on another kind of checking for closed specs That's really good news. I've had to implement closed keys spec in spec 1 to deal with the following three common case of defects: 1. Keys are mistyped, wrong qualified ns or name, like thinking it is id instead of identifier. Or just plain typo. 2. Keys are misplaced. Like putting the address on the user when it actually goes in the nested profile map. Address on profile being optional, open specs don't catch this bug. 3. Combinations are mistaken. Like say a map which has a query and insert key, this is supposed to be interpreted by some engine, but it is now ambiguous if the operation is supposed to be to query or to insert.

didibus04:04:51

I don't favour the use of closed specs, because they can cause unecessary breakage. And I'm not saying closed specs is the best solution to these above issues. But my team has faced these often, leading to real production bugs. And I haven't found a better solution to them yet.

didibus04:04:04

The only better idea we had was to seperate the spec of the producer from the one of the consumer. Specifically, code that looks to an input map to read from doesn't need to validate the input as being closed. As all the above bugs are on the producing code. That way, consumers are not broken by producer accretion that are backward compatible.

didibus04:04:30

But producers would validate their output maps using Closed spec. To ensure they didn't make any of the above mistakes.

didibus04:04:29

We never implemented it, because with spec1, it would be too tedious to keep seperate producer/consumer specs.

orestis05:04:34

Yeah, catching typos is a great use of closed specs. I was attempting going at it from a usage point of view, warning if you are trying to a lookup a key that isn’t guaranteed to be there.

orestis05:04:51

A spec-aware linter would be great.

ikitommi06:04:58

@orestis maybe you could work with @borkdude on clj-kondo for that? Would be awesome.

orestis06:04:49

Yeah, I was actually looking into this before. A tricky thing is that you probably need runtime info to get to the spec registry

orestis06:04:34

You could perhaps just collect s/keys calls statically for a good first approximation though.

ikitommi07:04:11

@didibus there is https://github.com/bhauman/spell-spec for spec1 just for that. Sadly, it also macros so can't program/compose with those.

ikitommi07:04:36

@alexmiller thanks for the info. My use case is this: reitit components can declare partial specs for the route data they are mounted to, e.g. a parameter-coercion-interceptor says that the route data can have :parameters key, with a map with keys :query, :path etc. for a given route. All the partial specs of the components mounted to a route will be deep-merged for the final effective spec. e.g. interceptor requiring data of form {:parameters {:path ...}} and another with {:parameters {:multipart ...}} should be merged to require effectively {:parameters {:path …, :multipart …}}. And there should be an way to validate that merged spec as “closed” to fail-fast on typos. I have just that working using data-specs + spell-spec + spec-tools coercion and will demo that soon, but I would want the users of the library not to require anything else than the core.spec to define the component specs. With spec2, I would like to support the selection / schema syntax as data so that one could say that “my component requires [{:parameters [{:query ::parameter-map}]}] and the other [{:parameters [{:multipart ::parameter-map}]}] and I could just merge those + spec to close them (with a small helper) into [{:parameters [{:query ::parameter-map} {:multipart ::parameter-map} ::closed-spec]} ::closed-spec] kinda thing, that could be used to check the strict configuration.

ikitommi07:04:24

also, noticed that the nested non-qualified keys don’t work yet, is that a bug or a feature? [{:a int?}] seems to work, but [{:a [{:b int?}]}] doesn’t.

Alex Miller (Clojure team)11:04:07

It works if you wrap the inner in s/schema (or register the inner with a name)

borkdude07:04:42

@orestis > You could perhaps just collect s/keys calls statically for a good first approximation though. That’s a nice idea. I’ll consider that for clj-kondo.

ikitommi07:04:27

with top-level merging & spec1

didibus07:04:29

That would be neat yes

didibus07:04:18

One thing I was thinking also is to make a custom validate function which can be called with an option to be closed or open.

didibus08:04:19

That way I could use the same spec, but have the producer code validate it assuming no added keys can be present

Alex Miller (Clojure team)11:04:07

That’s exactly the direction we’re going

Alex Miller (Clojure team)11:04:38

It’s in how you check, not in the spec

16
orestis11:04:47

That makes a ton of sense!

gfredericks15:04:50

literally friday at work somebody was debugging some code that walks & transforms a plumatic/schema to add openness everywhere, with exactly the same motivation

quoll19:04:10

I’m pushing up against leaving for my daughter’s swimming training, so I figure I should ask in here… I’m using an ns form with metadata, in the same way that clojure.core does. i.e.

(ns ^{:doc "some docs" :author "me"} my-ns)

quoll19:04:38

However, this fails on Clojure 1.10, due to “Extra input”

gfredericks19:04:07

works fine for me if I paste that in a repl

quoll19:04:13

sure, I can just go back to a standard docstring and drop the :author metadata, but given that clojure.core uses this I thought I’d question it

quoll19:04:18

yup, same here

quoll19:04:28

but leiningen hates to build it 🙂

quoll19:04:30

let me put together something minimal

gfredericks19:04:43

yeah, I just tried it in leiningen and it worked fine

gfredericks19:04:26

half of the time when I'm debugging something, trying to get a minimal reproduction also exposes the real problem

quoll19:04:50

OK, it’s looking like I had a typo elsewhere that got through the compiler before spec was introduced 😳