Fork me on GitHub
#clojure-spec
<
2017-11-14
>
qqq00:11:53

(defn tag-is [t]
  (fn [x]
    (= (:tag x) t)))

;; ** vec2 
(s/def ::x number?) 
(s/def ::y number?)
(s/def ::vec2 (s/and (s/keys :req [::x ::y])
                     (tag-is ::vec2)))

;; ** bbox
(s/def ::x0 number?)
(s/def ::x1 number?)
(s/def ::y0 number?)
(s/def ::y1 number?)
(s/def ::bbox (s/and (s/keys :req [::x0 ::x1 ::y0 ::y1])
                     (tag-is ::bbox)))

(defn vec2-new [{:keys [x y]}]
  {:tag ::vec2
   ::x x
   ::y y})

(defn bbox-new [{:keys [x0 x1 y0 y1]}]
  {:tag ::bbox
   ::x0 x0
   ::x1 x1
   ::y0 y0
   ::y1 y1})
Am I using spec right nere? the vec2-new and bbox-new seem very UN-clojurish 'every is data' approach. However, given that I'm using ::x ::y ::x0 ::y0 ... I don't know how else to nicely create vec2/bbox objects

taylor01:11:18

multi-spec seems like it might better fit that use case https://clojure.org/guides/spec#_multi_spec

taylor01:11:18

that would obviate the tag-is thing. the *-new fns at the bottom seem like they’re just converting map keys from unnamespaced to the current namespace, why not use the namespaced keys upstream?

taylor01:11:35

(using unnamespaced keys in your spec is also an option)

qqq01:11:18

@taylor: because namespace 'object' is constructed != namespace 'object' is specced

seancorfield02:11:38

@qqq In the upstream code, do you already have maps with unqualified keys? If so, where did they come from?

seancorfield02:11:50

If you don't want the keys namespaced, change :req to :req-un (then bbox-new becomes just (assoc data :tag ::bbox))

qqq02:11:00

@seancorfield: all code is code I have written (and can change -- am rewriting with spec -- and trying to figure out best practices)

qqq02:11:27

I actually think qualified makes more sense than unqialified, since a word may mean different things n different namesapces

deg08:11:27

@hiredman I did not know that, thanks!. Was it always the case that s/keys checks other keys too? Where is this documented? Meanwhile, I realized that another part of the solution for me is to use s/merge. That way, the central spec needs to only about the existence of each module, not its behavior.

ajmagnifico20:11:23

Question: In general, do you find it’s better to define specs in the namespaces that use them? Or in a separate “appname.specs” namespace?

ghadi20:11:15

personally, separate

ajmagnifico20:11:03

I think that the advantage of defining them in the namespaces where they are used is one of brevity.

ajmagnifico20:11:14

For example, with nested data structures

ajmagnifico20:11:31

#:longappname.specs{:a 1 :b 2}

ajmagnifico20:11:28

this is less verbose than {:longappname.specs/a 1 :longappname.specs/b 2}

ajmagnifico20:11:43

But the #: syntax only applies to the top level of the map

taylor20:11:47

you can alias the namespaces for brevity

ajmagnifico20:11:18

So, I’m loading a config file that looks like this

ajmagnifico20:11:26

and I’m trying to spec it

ajmagnifico20:11:44

I know I can use (spec/keys :req-un …)

ajmagnifico20:11:49

But I’d like to use namespaces

ajmagnifico20:11:13

Anyway, all of the pieces of this config are currently in my appname.specs namespace

ajmagnifico20:11:28

When I load it and try to validate it, it fails, because all of my specs are namespaced

ajmagnifico20:11:55

But I’d rather not have to put #:appname.specs in front of each map in the config

ajmagnifico20:11:55

So if I use the alias strategy like taylor said, I’d need to choose an arbitrary alias ahead of time that I would use in the config file, and that would need to match up with an alias that I would create in the namespace where I’m actually using and validating this config data.

ajmagnifico20:11:17

Or, if I defed the specs in the namespace where the data will be loaded and used,

ajmagnifico20:11:33

then I can just put a :: in front of each of the keys and everything will work as intended

ajmagnifico20:11:05

But am I going to get burned down the road if I have all of my spec definitions scattered throughout a bunch of namespaces instead of keeping them all in one location?

taylor20:11:34

I guess I’d ask myself what problem am I solving by namespacing all the keywords in my config map

taylor20:11:53

or is it instead creating more problems

taylor20:11:06

where’s your config defined, how is it loaded?

ajmagnifico20:11:02

Okay, that’s a fair question taylor. I guess at this point it’s me still trying to learn the idiomatic way of doing spec as it was intended by its creators. And they seem to think that namespaced keywords are a beautiful thing. I’m still trying to figure out how they’re beautiful. 🙂

ajmagnifico20:11:21

best practices

ajmagnifico20:11:37

so I’m wondering if “best practices” here are, like you said, creating more problems

ajmagnifico20:11:41

or if I’m just doing it wrong.

taylor20:11:03

I use specs for maps with unnamespaced keywords all the time, personally. I don’t think there’s anything necessarily wrong with it

ajmagnifico20:11:58

Anyway, the config is stored in edn format in a hand-edited file, loaded with edn/read-string during app initialization

taylor20:11:22

but then again most of the maps I work with are deserialized from some format that doesn’t support namespaces

taylor20:11:38

and FWIW you could use any “made up” namespace for your config keywords, it doesn’t necessarily have to match a real namespace in your project

ajmagnifico20:11:03

that’s an interesting idea

ajmagnifico20:11:20

thanks for the discussion, taylor

ajmagnifico20:11:55

I’ll probably just end up using :req-un

taylor20:11:11

that’s what I’d do, but I’m lazy 😆

ajmagnifico20:11:20

the hallmark of every great programmer