Fork me on GitHub
#clojure-spec
<
2016-08-29
>
potetm01:08:51

next question: is there an ad hoc way to supply a :gen for function :args to stest/check?

potetm01:08:54

I see you can s/def the args, then supply a gen. I'm not opposed to the notion that you must name something to use it elsewhere (even if that elsewhere is a test), but I thought there might be some sugar out there.

sattvik01:08:24

I think you can do it with instrument.

sveri09:08:00

@lvh Thanks, I just took ? It works as expected 🙂

lvh13:08:00

Is there any chance of a with-instrument macro, and/or a clojure.test + clojure.spec.test.check deftest macro a la defspec with test.check? (Too many similarly named words; I feel like I screwed that up)

sattvik13:08:47

We just started really looking into clojure.spec last week, and while we are still figuring out how to best leverage it, I definitely agree that a clojure.test integration and a with-instrument macro sound like useful additions.

sveri14:08:35

Hm, I am not sure, but, so far I would want to have everything instrumented during my tests

sveri14:08:49

Thats the first thing I do in my tests, turn on instrumentation

sattvik15:08:53

Yes, in general, you want to have everything instrumented. However, if you are using stubs or overriding specs, you may only want to do that within the scope of a few checks.

xcthulhu16:08:10

Hey! I'm trying to cryptographically sign data structures in Clojure/ClojureScript. The approach I am taking is to serialize my structures into a canonical form - namely recursively sorting the keywords of every hash-map in the structure, prior to serializing with transit. (1) Is there already something that does this for me?

xcthulhu16:08:27

(2) Maybe this isn't the best practice, is there a better way?

xcthulhu16:08:09

(3) If there isn't a better way, how do you enforce that a data structure is canonical in spec?

lvh16:08:21

@sveri +1 on also wanting instrumentation with stubs/overrides as sattvik mentioned

lvh16:08:44

@xcthulhu Hello I am a cryptographer

lvh16:08:52

Solving a similar problem, turns out

lvh16:08:23

Fortunately I don’t particularly care about canonicalization or deterministic signatures

lvh16:08:37

I’m personally using nippy, but I don’t think that has any particular canonical form guarantees

lvh16:08:03

@xcthulhu How do you keep the hash-map structure in the serialized form whilst being sorted?

xcthulhu16:08:20

Serialized form comes after the thing is canonical. Yeah, I've gone to pretty extreme lengths to keep all of my signatures deterministic.

xcthulhu16:08:10

I'm doing ECC in ClojureScript and I don't believe I can get a reasonable secure RNG without resorting to RFC 6979

lvh16:08:19

Sure; when you say canonicalizing a data structure the first things that come to mind are ASN.1 C(X)ER

xcthulhu16:08:43

Yeah... I've implemented a fragment.

xcthulhu16:08:56

I could go whole-hog. I was thinking of just using transit

lvh16:08:13

Fortunately RFC 6979 + ed25519 is how you’d want to do it anyway regardless of how much you care about that determinism 🙂

lvh16:08:29

How do you ship the ClojureScript?

lvh16:08:12

(The devil’s advocate argument against JS crypto usually goes by how you ship the source to begin with)

lvh16:08:31

Oh; OK — why SECP256k1?

xcthulhu16:08:53

Because I started this when I was doing consulting for digital currency and it's what everyone uses because of BitCoin

lvh16:08:58

Either way this is not a #spec discussion anymore so maybe we should move to #crypto 🙂

xcthulhu16:08:00

I should switch it to ed25519

lfn319:08:49

@fenton A thing I’ve done previously with schema is wrap a channel with a validation to check anything put into the channel conforms to the schema. This causes exceptions to be thrown where you’re putting the unexpected value onto the channel which is really useful. Sure you could do the same thing with spec if you wanted to.

lfn319:08:24

Is there a way to attach a doc-string to a spec?

fenton19:08:50

@ifn3 that sounds like a great use of specs

Alex Miller (Clojure team)20:08:03

@lfn3 re doc string, not currently but feel free to vote on http://dev.clojure.org/jira/browse/CLJ-1965

Alex Miller (Clojure team)20:08:08

@lvh re with-instrument, if you wanted to log a jira, that would help us remember this idea (seems quite reasonable)

lvh20:08:05

will do 🙂

lfn320:08:44

@fenton https://gist.github.com/lfn3/0aaf4c420df206484372e9b7bbbe6c6f <- demo of using specs to validate things put on channels.

ag22:08:26

if I have a vector of maps:

(def accounts
  [{:name "Foo"} {:name "Bar"} {:name "Baz"}])
How do I define a spec that confirms to be one of the names, e.g.:
(s/def ::account-name ,,,)
: so when I do:
(gen/generate (s/gen ::account-name))
it would generate something like :account-name “Baz”

bfabry22:08:46

@ag (s/def ::account-name (set (map :name accounts)))

ag22:08:34

alright thanks!

emrehan22:08:17

Hello all. I'm stuck at defining a map with undefined keys with clojure.spec. For instance, I'm defining a map with keyword keys and string values. I can define it as below with Schema library:

(require '[schema.core :as schema])

(def Data {schema/Keyword schema/Str})
(schema/validate Data {:x "str"})
> {:x "str"}
(schema/validate Data {:x 0})
> ExceptionInfo Value does not match schema: {:x (not (matches-some-precondition? 0))}  schema.core/validator/fn--8583 (core.clj:155)
How can I define such a map with clojure.spec?

emrehan22:08:56

thanks so much @bfabry 🙂 I must have been missed this map-of function.

ag22:08:51

what if I have a collection and want a spec that satisfies for one of the items in the collection? e.g.

(def accounts
  [{:name "Foo" :type :foo}
   {:name "Bar" :type :bar}
   {:name "Baz" :type :baz}])

(s/def ::foo-or-bar-or-baz #_(any of those))

bfabry22:08:32

@ag not sure what you're saying, could you give a (s/valid ::account-name ...) => true example? also is accounts known at spec definition time?

ag22:08:11

so when I do (gen/generate (s/gen ::foo-or-bar-or-baz)) it would give me one of the maps of accounts vector

bfabry22:08:06

assuming accounts is known at spec definition time, and accounts is reasonably small, then (s/def ::foo-or-bar-or-baz (set accounts))

emrehan22:08:14

Is there a nice way to combine to clojure.spec definitions? I have a definition that must conform two defined schemas and I defined it as below:

(s/def ::spec3 (s/and #(s/valid? ::spec1 %) #(s/valid? ::spec2 %)))
Is there a better way to define this?

bfabry23:08:42

s/and accepts specs

bfabry23:08:02

so (s/and ::spec1 ::spec2) is fine

bfabry23:08:55

that's actually not that clear from the docstring is it? it just says "spec-forms"

emrehan23:08:12

I didn’t look at the spec but I tried that form. It didn’t work, maybe I’m missing something.

bfabry23:08:50

if ::spec1 conforms then the conformed value will be passed to ::spec2 instead of the original value

bfabry23:08:02

maybe that's your issue?

bfabry23:08:18

if it is you could (s/and any? ::spec1 ::spec2) I suppose

emrehan23:08:22

thanks for the tip. this behaviour is not obvious though. let me try that

bfabry23:08:20

I think s/and conforming with the first spec has tripped a few people up so the docstrings are probably being worked I imagine. iirc the guide does cover it though

ag23:08:48

is there equivalent of clojure.test.check.generators/let? in clojure.spec?

emrehan23:08:48

Here’s a simplified example

emrehan23:08:50

(s/def ::type (s/or :string string? :number number?))
(s/def ::a-map (s/map-of keyword? ::type))

(s/def ::id int?)
(s/def ::with-id (s/keys :req-un [::id]))

(s/def ::metadata (s/and #(s/valid? ::a-map %) #(s/valid? ::with-id %)))
(s/valid? ::metadata {:id 1 :n 1 :s "dsa"})
> true 

(s/def ::metadata (s/and ::a-map ::with-id))
(s/valid? ::metadata {:id 1 :n 1 :s "dsa"})
> false

(s/def ::metadata (s/and any? ::a-map ::with-id))
(s/valid? ::metadata {:id 1 :n 1 :s "dsa"})
> false

emrehan23:08:54

It seems to me that s/and passes the return value of (s/conform ::a-map {:id 1 :n 1 :s "dsa"}) to the second conform. However, the when s/or is conformed the return value is different from the input.

bfabry23:08:45

yes, conforming means changing the value to explain how it is true given the spec, ie if the spec has a branch

bfabry23:08:19

I think maybe s/and has changed so it successively conforms the values now

bfabry23:08:35

yes, it does

bfabry23:08:40

boot.user=> (s/conform any? {:id 1 :n 1 :s "dsa"})
{:id 1, :n 1, :s "dsa"}
boot.user=> (s/conform (s/and any? ::a-map) {:id 1 :n 1 :s "dsa"})
{:id [:number 1], :n [:number 1], :s [:string "dsa"]}
boot.user=> (s/conform (s/and any? ::a-map ::with-id) {:id 1 :n 1 :s "dsa"})
:clojure.spec/invalid

bfabry23:08:01

anyway, you have options. if you want to preserve the conforming behaviour, I would just re-order your predicates so that ::with-id comes first. if you want to do away with it then creating a new predicate with #(s/valid? ::a-map %) just around the spec that you don't want to conform anymore seems sensible

bfabry23:08:26

or you could use the new merge function

bfabry23:08:35

which is specifically for map specs

bfabry23:08:53

boot.user=> (s/valid? (s/merge ::a-map ::with-id) {:id 1 :n 1 :s "dsa"}) true boot.user=> (s/conform (s/merge ::a-map ::with-id) {:id 1 :n 1 :s "dsa"}) {:id 1, :n 1, :s "dsa"}

bfabry23:08:52

totally forgot about that

bfabry23:08:49

merge appears to conform using the last spec. is that right @alexmiller ?

bfabry23:08:59

boot.user=> (s/conform (s/merge ::with-id ::a-map) {:id 1 :n 1 :s "dsa"})
{:id [:number 1], :n [:number 1], :s [:string "dsa"]}

Alex Miller (Clojure team)23:08:17

Merge does not flow conformed values like and, it's more like union

ag23:08:16

is it possible to have “inlined” spec in keys I don’t think it’s working for me.

(s/def ::foo (s/keys :req [(s/def ::bar string?)]))

ag23:08:55

(g/generate (s/gen ::foo))

bfabry23:08:04

I think something named def should always be at the top level

bfabry23:08:40

and in that particular case I don't think it will work because keys is a macro

bfabry23:08:30

so the keys macro is passed the form '[(s/def ::bar string?)] instead of [::bar]

Alex Miller (Clojure team)23:08:48

@bfabry s/and has always flowed conformed values. We may change it to not flow and add s/and-> still as both are sometimes useful

bfabry23:08:13

ah, cheers, not sure where I ever got the idea that only the first did

Alex Miller (Clojure team)23:08:10

@ag s/keys always requires registered keys - that's part of the design that is talked about at http://clojure.org/about/spec

ag23:08:56

ok… thanks..

ag23:08:00

how do I create a spec for uuid? this doesn’t seem to work:

(s/def ::id g/uuid)

ag23:08:45

I don’t want to use java.util.UUID/randomUUID I may have to make it later work on cljs side too

bfabry23:08:18

@ag there's a uuid? predicate in core

ag23:08:57

oh, ok… thanks!