Fork me on GitHub
#clojure-spec
<
2017-02-10
>
bsima00:02:14

can I use a multispec on a vector of tags, instead of just a single keyword?

bsima00:02:52

how would I write such a defmulti?

bsima00:02:10

(defmulti event-type
  (fn [m] (->> m :event/type (into #{}))))

bsima00:02:39

here's what I came up with

bsima00:02:37

turns the vector of tags into a set, so the multispec can use the set as a predicate function for the multimethod dispatch

bsima00:02:14

this way I can combine multiple defmethods. If we take the example of :event/type from the official clojure spec guide, then I could do:

{:event/type [:event/search :event/error]
 :event/timestamp 1463970123000
 :error/message "Invalid host"
 :error/code 500
 :search/url ""}

bsima00:02:45

and so both :event/search and :event/error would be valid specs

Alex Miller (Clojure team)01:02:04

Yeah, there are no constraints on multi-spec as long as you follow the pattern

dragoncube02:02:05

In docs for s/keys there is “Note: there is no support for inline value specification, by design.”. Does some expanded explanation of this exist somethere?

dragoncube02:02:48

is it possible to specify spec for value referred by specific key in the map? I.e I want to say that {:id “my-id”} “my-id” should be validated by spec ::resource-id

dragoncube02:02:47

currently if you use s/keys there is only implicit assignment of the spec in case the keys are same

Alex Miller (Clojure team)02:02:20

re the second question, the key must match the spec name

Alex Miller (Clojure team)02:02:51

the idea here (also in the rationale) is that we are assigning enduring semantics to an attribute that can be used throughout the system

Alex Miller (Clojure team)02:02:17

it is admittedly a different philosophy than something like Schema

dragoncube02:02:39

yeah, but it is quite hard to work with the external data which shape you don’t control

dragoncube02:02:59

for example, {:id “res-1” :name “My resource 1” :sub {:id “sub-res-1” “Sub-resource 1"}}

dragoncube03:02:12

should I use synthesized keys for registering specs or I need to mimic data structure with packages structures?

dragoncube03:02:20

It is so tempting to use :: for specs registration

dragoncube03:02:32

for example above I would like to register two specs: ::resource-id and ::sub-resource-id in the same namespace

Alex Miller (Clojure team)03:02:54

well you can certainly do that along with s/keys and :req-un which will only match the name part, not the namespace part

dragoncube03:02:54

yes, it is possible but in this case you need either give it synthesized (not existing) namespace part like :rest-resources/id and :rest-resources.subresource/id or literally create ‘subresource’ package and define ::id spec there

dragoncube03:02:15

both of these are far from ideal

luxbock13:02:06

having s/vcat would definitely be quite helpful

tcoupland13:02:48

especially if you could unform back to a vector

Alex Miller (Clojure team)13:02:41

Rich is as well I think but hasn't decided how it should be implemented yet

tcoupland13:02:20

how do people feel about something like this:

(defn coerce
  [spec data]
  (let [conformed (s/conform spec data)]
     (if (= :clojure.spec/invalid conformed)
       conformed
       (s/unform spec conformed))))
I've been avoiding conform == coerce, after getting bitten, but this has got me thinking of dabbling again.

luxbock15:02:59

I wish the :gen overrides of stest/instrument behaved the same way as the :gen of stest/check

luxbock15:02:14

right now you can only use it for functions which have been stubbed

mss17:02:45

hey all, quick q about how instrument and check work in cljs. I’m running a figwheel repl in cursive. the repl is choking and timing out when I eval an instrument or check call for an fdef‘ed function . further evaluations – even of simple forms (e.g. (+ 1 1) – then result in an eval timeout. posted in the figwheel channel already but feel like I might just be misunderstanding a core spec concept and how I’m supposed to use it

Alex Miller (Clojure team)18:02:03

instrument should not take a long time to execute

Alex Miller (Clojure team)18:02:36

check certainly can take a while as it runs 1000 tests by default - you can change that by passing it additional options. although most often adjusting your generators is what really needs to happen.

mss18:02:12

yeah it feels like I must be doing something wrong. fwiw my spec is just a set of a few dozen string values (i.e. (cljs.spec/def ::my-spec #{”something” “something-else” …}). my fdef looks something like

(cljs.spec/fdef my-bool-returning-func
  :args (s/cat :my-spec-val ::my-spec)
  :ret boolean?)
simply calling "cljs.spec.test/instrument `my-bool-returning-func” or “cljs.spec.test/check `my-bool-returning-func” causes the same choking behavior

mss18:02:43

don’t know if that’s idiomatic/correct or not

assoc-in21:02:59

I am looking at using spec for validation for a SPA app. I am hoping that I can use the same specs for both the front and backend. I am guessing that I would place them in a cljc file, but I was wondering how the s/def would work with s referring to clojure.spec and cljs.spec. Any ideas?

bbloom21:02:46

what’s the preferred idiom to “optionalize” some map with :req keys? specifically, something like (s/merge ::foo (s/or ::bar (s/keys)))

bbloom21:02:07

that is, defining a new spec that is a ::foo and optionally also a ::bar

Alex Miller (Clojure team)22:02:53

@assoc-in the new cljs feature for automatic aliasing means that I think you can just use the clojure namespace in the cljc file and it will work for both

Alex Miller (Clojure team)22:02:02

but you should check with David in #clojurescript

Alex Miller (Clojure team)22:02:41

@bbloom not sure I understand what you’re asking?

bbloom22:02:04

@alexmiller to make a simplification of what i’m doing concrete: imagine i have some spec (s/def ::syntax (s/keys :req [::line ::column])) and i have some other AST nodes that might or might not have line/column information on it: (s/def ::ast (s/keys :req [::type ::op ::whatever] :opt [::line ::column]))

bbloom22:02:32

however, i have it’s not just two fields, it’s quite a few and i want to basically say “an ast node is these keys and optionally a ::syntax"

bbloom22:02:34

is that clearer?

Alex Miller (Clojure team)22:02:19

you could define ::syntax with :opt instead and just s/merge?

bbloom22:02:34

ideally not, since the parser requires everything be a ::syntax with proper line and column info

bbloom22:02:44

right now i’m doing s/or with an empty (s/keys)

Alex Miller (Clojure team)22:02:54

then why did you say optionally above?

Alex Miller (Clojure team)22:02:06

in that case, just merge ::syntax and ::ast ?

bbloom22:02:16

there’s two parts of the system

assoc-in22:02:28

@alexmiller okay thanks I'll ask

bbloom22:02:28

the parser requires all the output have ::syntax, and the analyzer allows it

bbloom22:02:45

ie not every node that comes out of the analyzer has line/col info on it - but in the parser, it is required

bbloom22:02:21

i don’t really want :opt tho, since if there is a ::line, i expect there to also be a ::column

bbloom22:02:26

they aren’t individually optional

bbloom22:02:57

hence (s/def ::ast (s/merge (s/keys :req [.....]) (s/or ::syntax (s/keys)))

Alex Miller (Clojure team)22:02:57

why do you need to do anything in the analyzer? the s/keys you already have in ::ast will validate those

bbloom22:02:09

b/c it only analyzes them independently

bbloom22:02:24

so if i do (assoc foo ::line 1) that should be a problem b/c there’s no column

bbloom22:02:38

the s/or doesn’t really solve that problem tho

bbloom22:02:33

the s/or does help generation tho

Alex Miller (Clojure team)22:02:51

I don’t think there is a way to do it in keys, so you’d need an extra constraint

bbloom22:02:40

i figured as much 🙂 was wondering if there was a particular pattern that has been successful for this sort of thing

Alex Miller (Clojure team)22:02:49

don’t think I’ve seen it

Alex Miller (Clojure team)22:02:35

I think I would leave the :opt in ::ast to get gen and and that with a custom predicate

Alex Miller (Clojure team)22:02:15

the pred should filter any generated maps that don’t satisfy the pred

bbloom22:02:22

it feels like a symmetric operation to s/?

bbloom22:02:31

or maybe s/nilable

bbloom22:02:54

would that work? (s/merge (s/keys …) (s/nilable ::syntax))

bbloom22:02:38

ah, sadly not

bbloom22:02:52

well wait… hmmm maybe it sorta does? just doesn’t seem to do what i’d want in gen

assoc-in22:02:17

@alexmiller Thanks I asked David and he said I can just use clojure.spec everywhere in the cljc file and I'll be good to go. I am really happy to see that spec's will be usable across both clojurescript and clojure so easily

bbloom22:02:00

that’s a pretty minimal repo of a shortcoming of generate on s/merge with s/nilable