Last active
June 16, 2016 14:03
-
-
Save robert-stuttaford/e1bc7bfbcdf62020277dda1a277394ca to your computer and use it in GitHub Desktop.
Clojure core.spec question: composing s/keys with s/and
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;; using Clojure 1.9-alpha7 | |
(ns keys-with-and-question | |
(:require [clojure.spec :as s] | |
[clojure.spec.gen :as gen])) | |
(s/def ::id string?) | |
(s/def ::active? boolean?) | |
(s/def ::extra int?) | |
(s/def ::base (s/keys :req-un [::id] :opt-un [::active?])) | |
(s/def ::base-with-extra (s/keys :req-un [::id ::extra] :opt-un [::active?])) | |
(s/def ::base-with-extra-with-and (s/and ::base (s/keys :req-un [::extra]))) | |
(comment | |
(s/conform ::base {:id "1" :active? true}) | |
;; {:id "1", :active? true} | |
(gen/generate (s/gen ::base)) | |
;; {:id "eXhlTPP6FvBCF", :active? true} | |
(s/conform ::base-with-extra {:id "1" :active? true :extra 123}) | |
;; {:id "1", :active? true, :extra 123} | |
(gen/generate (s/gen ::base-with-extra)) | |
;; {:id "6XGsmcQwui2hz7Tw10960E7Kw", :extra -13542} | |
;; {:id "BP6S79", :extra -5305490, :active? true} | |
(s/conform ::base-with-extra-with-and {:id "1" :active? true :extra 123}) | |
;; {:id "1", :active? true, :extra 123} | |
;; but... | |
(gen/generate (s/gen ::base-with-extra-with-and)) | |
;; Throws ex-info: "Couldn't satisfy such-that predicate after 100 tries." | |
;; My questions: | |
;; - Is composing `s/keys` with `s/and` offically supported? It certainly appears to validate and conform! | |
;; - If it is, how would we write a generator for it? | |
) |
Ok, great, thank you -- I had not realised the guide covers this.
So, the case I'm trying to solve for, is spec reuse. The with-extra
case is actually one of many; I don't want to duplicate all of base
into each of the many extra cases -- I want to reuse base with s/and
, as I have done there. And that works, when validating and conforming, which is awesome!
What I'd like to know is how to write a generator for it, because that'd be even more awesome.
Given what you've just answered, It seems like I may have to write a generator of my own that merges the base spec with the extra spec. I'm guess what I'm hoping is I can do this with out redeclaring the nature of the specs just for the generator.
Thanks, @puredanger!
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Yes, composing
s/keys
withs/and
is very much supported. The key here is knowing hows/and
generators work (this is covered in the guide btw) - they generate based on their first predicate, then filter based on subsequent predicates. So in::base-with-extra-with-and
it will generate maps based on::base
, then try to find instances that match(s/keys :req-un [::extra])
, and this will never be satisfied.Depending on what your actual need is, I could recommend a variety of answers. You already have a generator for this with
(s/gen ::base-with-extra)
.