Fork me on GitHub
#clojure-spec
<
2020-12-16
>
kenny22:12:11

Curious if there are any ideas around how spec2's select would allow for requiring deeply nested keys based on some dispatch function? e.g., (if the event is type "a" then a specific set of keys are required, if event type is "b" then ...)

dgb2323:12:49

@kenny if I understand correctly select is there to be used in specific contexts. Not sure if that answers your question.

kenny23:12:03

No. Essentially I'm curious what the story is wrt deeply nested multi-spec and select since that is a common use case.

kenny00:12:06

Perhaps an example is best.

(s/def ::order (s/schema [:order/id
                          :order/type
                          :order/a
                          :order/b
                          :order/c]))

(s/def :user/orders (s/coll-of ::order))

(s/def ::user (s/schema [:user/orders]))

(s/select ::user [:user/orders {:user/orders [:order/id
                                       :order/type
                                       ;; if :order/type is :order.type/a then require :order/a
                                       ]}])
The required order keys depend on the order's type.

kenny00:12:59

I only require :order/a when :order/type is :order.type/a.

dgb2300:12:44

So it is a separate schema for order?

kenny00:12:19

What do you mean?

dgb2300:12:51

I’m curious (never used spec2) as well. My intuition is that you are trying to solve something with select when it should be a separate schema for order types. But I have to think about it/read a bit more 😄

kenny00:12:24

Sorry, I don't understand what you mean by separate schema. Could you clarify?

dgb2300:12:09

using spec/or to distinguish the order types

dgb2300:12:54

and each order type has a separate spec that holds the context of order/a order/b etc

kenny00:12:57

Oh. Yes, that is possible here. It results in really bad error messages though (if you have a bad input, every single case of the or is outputted). multi-specs output a really nice error.

kenny00:12:30

In our case there may be hundreds of different "types". The explain-data is crazy 😵

dgb2300:12:41

have you tried to parse explain-data to kind of narrow it down? just talking off my ass here

kenny00:12:49

You can manipulate the output however you normally would 🙂 The thing is, multi-specs solve this problem nicely (albeit a bit cumbersome to work with). They just don't work with select, and it seems that there is a good fit for something there.

dgb2300:12:50

ah I see it now!

kenny00:12:01

So I was curious if there were any thoughts on how/if something like this would be a part of spec2. For us, this use case happens everywhere.

dgb2300:12:06

since select is just a spec too, then you would dispatch with a multimethod too right? so it would be something like (defmulti order-type :order/type...

dgb2300:12:25

each returning a select

dgb2300:12:52

that is specifically tailored to query a variant

kenny00:12:03

Not sure I follow how that'd work with the above.

dgb2300:12:15

me neither 😄

kenny00:12:59

The main thing is that you always declare required keys "top-level." Pretty sure what you have there wouldn't work :thinking_face:

dgb2300:12:04

got me hooked I’m going to play a bit with spec2 now

dgb2300:12:18

what do you mean with required keys?

dgb2300:12:23

schema keys are not required

kenny00:12:20

No matter the "level" (read nesting) you're at, you're always able to select which keys are required.

dgb2301:12:43

(s/def ::order (s/schema [::order-id
                          ::order-type
                          ::order-a
                          ::order-b
                          ::order-c]))

(s/def ::orders (s/coll-of ::order))

(defmulti order-type ::order-type)
(defmethod order-type ::order.type-a [_]
  (s/select ::order [::order-a]))
(defmethod order-type ::order.type-b [_]
  (s/select ::order [::order-b]))

(s/def ::order-typed (s/multi-spec order-type ::order-type))

(s/valid? ::order-typed {::order-type ::order.type-b ::order-b "foo"})

(s/def ::orders-typed (s/coll-of ::order-typed))

(s/valid? ::orders-typed [{::order-type ::order.type-b ::order-b "foo"}])

kenny01:12:23

You've changed the problem 🙂

dgb2301:12:39

I get the feeling that I didn’t understand it in the first place

kenny01:12:32

At the level of the "user" map, I want to select what keys are required in each map under :user/orders.

kenny01:12:44

Without renaming my :user/orders key.

dgb2302:12:18

i see but those are different specs

kenny02:12:57

Same schema, different selected keys.

dgb2302:12:42

the schema doesn’t know about select (just tried it)

dgb2302:12:41

i have to leave it at that (it’s late) but was fun so far. maybe someone actually knowledgeable can find a better way

kenny02:12:18

Not sure what you mean exactly 🙂 This is what I'd like.

(s/select ::user [:user/orders {:user/orders [:order/id
                                              :order/type
                                              ;; if :order/type is :order.type/a then require :order/a
                                              ]}])

dgb2302:12:09

What I mean is that I’m still a beginner with these things. I don’t really see a solution right now other than dispatching the select and conform/validate on the select spec.

kenny02:12:16

Oh, I understand. Yes, I was 99% certain spec2 does not cover this. I am interested in thoughts on this particular problem and how spec2 might solve it.

kenny02:12:31

For sure! I'm confident something could be built to solve this (likely even in spec1). Curious if this use-case is in scope for spec2 though. It seems like a core problem.

👍 3
Alex Miller (Clojure team)14:12:11

Two things here - first selects do not currently have the ability to handle maps of colls of maps of ... , only nested maps. That is something we are thinking about though as it is very common. Second, there is not currently any way to indicate any sort of predicate or condition on the nested data like you're talking about but that is something we're thinking about. What you're asking for might be beyond where we end up though, not sure yet.

Alex Miller (Clojure team)14:12:44

select is currently only about nested map structures (what keys are provided at each level)

👍 3
kenny22:12:20

> there is not currently any way to indicate any sort of predicate or condition on the nested data like you're talking about but that is something we're thinking about. This sounds exactly like what I'm after 🙂 This is such a big issue for us I may need to tackle it internally for now. Would love to know what those thoughts are. Though, I assume they are not public yet?

Alex Miller (Clojure team)22:12:18

sorry, not working on this right now

3