Fork me on GitHub
#clojure-spec
<
2017-04-05
>
qqq09:04:03

in spec, how do I say "obj" is a map, where the values are also maps ?

qqq09:04:10

i.e. it's valid to write (get-in obj [k1 k2])

bronsa09:04:06

user=> (s/def ::foo (s/map-of any? map?))
:user/foo
user=> (s/valid? ::foo {:a {:b 1}})
true

qqq09:04:28

here I could also do (s/map-of any? (s/map-of any? any?)) but the inner (s/map-of any? any?) is equiv to map? right?

qqq09:04:30

this is neat

thheller09:04:37

@qqq (s/def ::map (s/map-of any? ::map) to make it recursive

qqq09:04:52

@thheller: but that'd be wrong 🙂

shafeeq09:04:06

Is there a way to send clojure spec over the wire? I’m looking to dynamically fetch specs from a clojure service via a HTTP request, into my clojurescript app that needs to use these specs for validation, etc.

shafeeq09:04:32

Unfortunately, I can’t share these specs via cljc, because the server, and the client apps should be decoupled. So it’s a runtime dependency.

thheller09:04:32

@shafeeq you can put your specs into a .cljc file so they can be shared, but serializing a spec will not work

qqq09:04:41

@thheller: I (perhaps incorrectly) thought specs are just data and can be passed around like data.

ikitommi09:04:43

the s/form is the serialization format for specs. You need to recursively fetch all the related specs too.

thheller09:04:56

the specs yes but the predicates they use are not

thheller09:04:26

(s/def ::foo #(= % :foo)) cannot be serialized and transferred over the wire

ikitommi09:04:41

spiked that with a custom rmi-style “spec-loader” that is used to negotiate the specs over the wire.

qqq09:04:29

@thheller: ah; besides functions, are there other things that need to be serailized? (and it seems like functions can be serialized if you have a giant hashmap of keyword to functions)

qqq09:04:40

[I'm not saying this is a good idea; I'm now just curious whether it can be done.]

viesti10:04:47

user=> (let [x :foo ] (s/form (s/spec #(= % x))))
(clojure.core/fn [%] (clojure.core/= % x))

viesti10:04:51

there was discussion about dynamic scope somewhere in channel history but slack eats it 🙂

shafeeq10:04:15

Well in my case, I only use clojure.core predicates, since I also need the spec to be representable via swagger as well.

shafeeq10:04:05

I’m recursively walking down specs, and doing an s/form on them.

shafeeq10:04:31

On the clojurescript side, now I have to walk through this list of specs and make definitions.

shafeeq10:04:19

Would something like this work? Am I missing some other perspectives or edge cases (apart from custom functions)?

ikitommi10:04:20

nice. did similar spike before the s/form bugs were fixed, now might work on most/specs already.

ikitommi10:04:43

you eval those on the client, right?

shafeeq10:04:31

yeah, will need to do that. trying that now.

ikitommi10:04:57

that opens up a security concern… running eval on something that has come over the wire.

thheller10:04:38

you can't really eval these on the CLJS side

thheller10:04:59

that is reading not eval

thheller10:04:27

you have a symbol clojure.core/string? but now what

ikitommi10:04:29

does the upcoming spec-of-specs help here?

thheller10:04:27

as @qqq suggested you could make this work if turn everything into keywords and then define those keywords on both ends

thheller10:04:43

but really ... use .cljc (just turn the specs into a library the client/server can share)

ikitommi10:04:17

oh, true @thheller i had test eval’ing the forms, but only in the clj-side.

shafeeq10:04:51

@thheller from a higher level perspective: the specs on the server side will evolve with time, and we shouldn’t really have to compile, and build the client. The client should ideally be oblivious to changes in the specs. It should pull it down (via http), and use it.

shafeeq10:04:05

that’s the idea, at least.

thheller10:04:34

then validate on the server and send the explain-data back, that is just data

shafeeq10:04:37

well, the specs are my API specification at the moment. Apart from getting a catalog of requests, I’m building and coercing requests on the clojurescript side based on the spec.

shafeeq10:04:00

pretty much swagger.json, but in spec

shafeeq10:04:40

I already have this functionality implemented for swagger.json. I’m moving to spec now.

qqq10:04:21

@thheller: I'm impressed by how flexible spec is. I can't imagine even asking this question in Haskell. 🙂

odinodin12:04:18

Is it possible to get full spec instrumentation during ClojureScript development in your running app? (using Figwheel). I’ve run cljs.spec.test/instrument which returns a list of fdef’ed functions, but none of the fdef functions trigger when the contract is broken.

rovanion12:04:56

Hi, I've been trying all day to figure out how to correctly spec a variable-length vector as described here: http://stackoverflow.com/questions/43230546/a-clojure-spec-that-matches-and-generates-an-ordered-vector-of-variable-length I can only seem to create a matching spec with spec/cat but it in turn will only generate lists 😕

thomas12:04:26

@rovanion can't you use the seq regex's for that?

thomas12:04:43

or is that not what you need?

shafeeq12:04:07

I’m rewriting my prismatic-schema spec into clojure.spec. There’s a lot of it. Has anyone here heard of or used some utility to help with this?

rovanion12:04:47

thomas: I'm able to match them using the seq regexes, but not generate data since they generate lists and I can't seem to be able to message them that I want vectors instead.

thomas12:04:51

@rovanion just looked at our SO post... and if you do something like gen/vector ?

rovanion12:04:25

thomas: I'd have to look up how that works first. The docs are very empty when it comes to explaining its use.

rovanion14:04:51

@thomas Alex helped me to find a solution: http://stackoverflow.com/a/43233374/501017

alexmiller14:04:14

it’s not uncommon to run into this when spec’ing a macro (the need exists in a few places in the clojure.core specs) and as you can see in the example, it’s quite tedious to properly solve (even that example is missing what you really want for describe/form). We’ve been discussing something like s/vcat for this but Rich has several competing ideas for how to implement it and I’m not sure where he will end up going with it.

ghadi17:04:21

Are they design alternatives or impl alternatives @alexmiller ?

slipset18:04:34

Following http://dev.clojure.org/jira/browse/CLJ-2141, it seems to me that Clojure is missing a predicate namespaced? which returns true if a thing is, well, namespaced.

ikitommi19:04:33

what kind of data does a (s/coll-of string? :into {}) expects?

ikitommi19:04:38

(def spec (s/coll-of string? :into {}))

(s/explain-data spec {"1" "2"})
; #:clojure.spec{:problems ({:path [], :pred string?, :val ["1" "2"], :via [], :in [0]})}

(s/explain-data spec ["1" "2"])
; nil

(s/conform spec ["1" "2"])
; CompilerException java.lang.ClassCastException

alexmiller19:04:47

@ikitommi if using :into {}, you should be using a spec for a map entry (like a 2 element tuple)

alexmiller19:04:18

Like (s/coll-of (s/tuple string? string?) :into {})

alexmiller19:04:03

@slipset I don't think that's generally useful enough

alexmiller19:04:27

@ghadi design

ikitommi19:04:24

thanks alex

alexmiller20:04:53

That's effectively how map-of is implemented

takeoutweight23:04:46

Is there any (idiomatic) way to attach custom s/explain data for user specs? Use case is a predicate that supplies its own context information that I'd like to have preserved in the failure message.

ghadi23:04:55

@alexmiller can you elaborate around design alternatives for vcat?

alexmiller23:04:33

or weigh in there

alexmiller23:04:56

@ghadi at this point I just don’t remember the details (and Rich did not fully elaborate on them anyways)

alexmiller23:04:11

we did talk about it for a while. I mostly remember that he didn’t like any of my ideas. :)

ghadi23:04:41

sounds legit