This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2018-10-10
Channels
- # 100-days-of-code (2)
- # announcements (2)
- # aws (11)
- # beginners (114)
- # boot (6)
- # calva (11)
- # cider (11)
- # cljdoc (2)
- # cljs-dev (7)
- # clojure (126)
- # clojure-berlin (1)
- # clojure-conj (4)
- # clojure-dev (1)
- # clojure-germany (12)
- # clojure-italy (22)
- # clojure-spec (96)
- # clojure-uk (111)
- # clojurescript (27)
- # core-async (8)
- # cursive (17)
- # datomic (26)
- # devops (5)
- # editors (15)
- # emacs (13)
- # events (1)
- # figwheel-main (27)
- # fulcro (64)
- # hyperfiddle (29)
- # jobs (8)
- # jobs-discuss (7)
- # liberator (4)
- # off-topic (46)
- # om (9)
- # onyx (1)
- # overtone (1)
- # perun (8)
- # re-frame (28)
- # reagent (35)
- # reitit (5)
- # shadow-cljs (96)
- # spacemacs (1)
- # testing (10)
- # timbre (8)
- # tools-deps (63)
- # unrepl (1)
- # yada (10)
Hi all, I don’t slack much. I have a case where I model data as a map, some keys required, some not. I already use one key as a “type” or “kind” indicator. The problem I have is I have a set of keys where all are optional, but at least one of them must be present.
I have not had luck figuring out how to spec that
Any thoughts or links to point me at?
use s/and to combine a predicate that checks that condition
Mostly the problems I run ino with spec’ing like that is it seems to imply/expect more hierarchy than o have
I’m sure it’s user error, but examples are hard to come by
Sorry for disrupting your chat. I’m wondering how to programmatically register a spec.
(defmacro draft-make-spec
[spec-name spec-pred]
(eval `(list 'spec/def ~spec-name ~spec-pred)))
(def spec-name ::g)
(def spec-pred int?)
(draft-make-spec spec-name spec-pred)
(spec/valid? ::g 4)
;; works correctly
;; => true
(let [spec-name ::h]
(draft-make-spec spec-name int?)
(spec/valid? ::h 4))
;; => throws in java.lang.InstantiationException
@scottstack (s/and (s/keys :opt [::a ::b ::c]) #(some #{::a ::b ::c} (keys %)))
@piotr2b you’re doing too much there I think
That’s interesting. I tried something similar with s/alt but didn’t quite get there.
Thanks @alexmiller , I’ll see where I get with that. It at least tells me I’m heading in the right direction.
@alexmiller, at first I thought it would as easy as this:
(defmacro draft-make-spec
[spec-name spec-pred]
`(spec/def ~spec-name ~spec-pred))
but it doesn’t work:
(let [spec-name ::h]
(draft-make-spec spec-name int?)
(spec/valid? ::h 4))
;; => CompilerException java.lang.Exception: Unable to resolve spec: ::h
😞 any tiny help from you would be greatly appreciated! 💪I was wondering how I could write a small library to generate specs from avro schema, so I need to call spec/def in a let and I only know the spec name at runtime.
@piotr2b have you looked at the macro expansions of your macro and of s/def? I did a quick try and it didn’t come out how I expected.
I’ve done some stuff like this elsewhere but can’t put my finger on it right now and I need to log off, sorry
I put the macro expansion of the s/def form into the let in place of the macro call, ie (s/def-impl ‘spec-name ‘int? int?) and it fails an assertion that “k” is a keyword...
Nomenclature fails me but it is like spec-name in your macro needs a double deref
@piotr2b If you take the expansion of s/def, and use that in your draft-make-spec macro, it works
wow, wait!
Could you show me that? 😄
It’s funny because we’ve swapped problems
I was working on solving yours
Yeah, on my phone tho
Here is what I’ve got so far:
(spec/def :entity/kind #{:entity/car :entity/person})
(spec/def :person/name string?)
(spec/def :person/age pos-int?)
(spec/def :car/model-name string?)
(spec/def :car/age pos-int?)
(spec/def ::scottstack-spec
(and (spec/keys :opt-un [:entity/kind
:person/name
:person/age
:car/model-name
:car/age])
#(condp = (:entity/kind %)
:entity/car (every? (set (keys %)) #{:car/model-name :car/age})
:entity/person (every? (set (keys %)) #{:person/name :person/age})
false)))
(spec/valid? ::scottstack-spec {:entity/kind :entity/car
:person/name "scottstack"
:person/age 21})
;; => false
(spec/valid? ::scottstack-spec {:entity/kind :entity/person
:person/name "scottstack"
:person/age 21})
;; => true
Does it suit your need?
I will have to check
Basically I’m a bit new with macros, so if you could write down the code in addition to your previous explanations, that would be wonderful 🙂
Let me switch to something with a keyboard
@alexmiller how sad! if you could find it back it would be awesome. Anyway, thanks for everything you do for this amazing language 🙂
@scottstack, I fixed a typo, it’s better with :opt-un
And the last test case:
(spec/valid? ::scottstack-spec {:entity/kind :entity/person
:person/name "scottstack"})
;; => false
(defmacro draft-make-spec [spec-name spec-pred]
`(s/def-impl spec-name ‘spec-pred ~spec-pred))
(let [sname ::something]
(draft-make-spec sname int?)
(s/valid? ::something 4)
I am very good with macros either. I depend heavily on cider's macroexpansion
S/def would expand to (s/def-impl 'sname 'int? int?)
And that would fail an assertion that the first arg be a keyword
I see I goofed up, maybe it still doesn't work
Nevermind, I just confused myself momentarily... I think it is ok
Afternoon, is anyone aware of any projects to transform JSON Schema into Clojure Spec?
@lilactown Yea tks.. Do people think that would be valuable? There are lots of standards
defined in terms of JSON Schema and would be beneficial to auto-generate specs so they can be consumes without the tedious nature of crafting data structures from scratch
I think it could be pretty useful to help bootstrap some of the commonly needed specs, yeah 😄
This may be a test.check question more than a spec question, but here goes:
I have a map named foo
:
{:a 1 :b nil :c 32}
If a
is present, then b
should be nil
, and c
should be a pos-int?
.
{:a nil :b true :c nil}
When a
is nil, c
must also be nil
, and b
must hold a value.
I want to be able to generate examples of both types of maps.
As a pure t.c question it's easy - use gen/one-of; not sure about making that more spectomatic
multi-spec sounds plausible but I am not a dentist
The way I explained it above is kind of crappy:
(s/def :my/a #{1 2 nil})
(s/def :my/b (s/or :has-b pos-int? :no-b nil?))
(s/def ::thing (s/keys :req-un [:my/a :my/b])
example:
(let [a-thing {:a 1 :b nil}
(when (and (:a a-thing) (not (:b a-thing))) (println "it's a foo")
(when (and (not (:a a-thing)) (:b a thing)) (println "it's a bar")))
I think I need some such-that
magic maybe?So what I want is if I then ran (gen/sample (s/gen ::thing))
, I'd never get back records which have both :a
and :b
populated, only one or the other.
Have you tried writing a map spec for each case and using s/or?
just to be clear, what I think you're suggesting is this:
(s/def :myA/a #{1 2})
(s/def :myA/b nil?)
(s/def :myB/a nil?)
(s/def :myB/b pos-int?)
(s/def ::thing-a (s/keys :req-un [:myA/a :myA/b])
(s/def ::thing-b (s/keys :req-un [:myB/a :myB/b])
(s/def ::either-thing (s/or ::thing-a ::thing-b))
follow on question is then perhaps, how to tune the distribution of thing-a and thing-b's generated
haven’t tried it, but my guess in a custom generator (See s/with-gen
) + http://clojure.github.io/test.check/clojure.test.check.generators.html#var-frequency
untested, but then you can do something like (s/def ::either-thing (s/with-gen (s/or ::thing-a ::thing-b) #(gen/frequency [[2 (s/gen ::thing-a)] [2 (s/gen ::thing-b)]]) ))
hm, probably a bug in my code, but in general with-gen
should override the default generator. remember the function needs to return a generator
i tried changing the frequency from 2 to 0 for one of them, and it doesn't vary the samples
is what I wind up with when I try to make it return only one of the types, so perhaps something else is amiss here
hmmm, yes, i’m trying a simpler example and not seeing what i expect, so I’m clearly missing something
@bbrinck here's a thing: if i switch the order of the s/or
, it works in one direction, but not the other