clojure-spec

About: http://clojure.org/about/spec Guide: http://clojure.org/guides/spec API: https://clojure.github.io/spec.alpha/clojure.spec.alpha-api.html
alexmiller 2020-12-17T00:13:07.486500Z

Can you explain?

kenny 2020-12-17T00:33:06.486700Z

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.

kenny 2020-12-17T00:33:59.486900Z

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

dgb23 2020-12-17T00:38:44.487100Z

So it is a separate schema for order?

kenny 2020-12-17T00:39:19.487300Z

What do you mean?

dgb23 2020-12-17T00:40:51.487500Z

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 ๐Ÿ˜„

kenny 2020-12-17T00:41:24.487700Z

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

dgb23 2020-12-17T00:42:09.487900Z

using spec/or to distinguish the order types

dgb23 2020-12-17T00:42:54.488100Z

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

kenny 2020-12-17T00:42:57.488300Z

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.

kenny 2020-12-17T00:43:30.488500Z

In our case there may be hundreds of different "types". The explain-data is crazy ๐Ÿ˜ต

dgb23 2020-12-17T00:43:42.488700Z

i see

dgb23 2020-12-17T00:44:41.488900Z

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

kenny 2020-12-17T00:46:49.489100Z

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.

dgb23 2020-12-17T00:47:50.489300Z

ah I see it now!

kenny 2020-12-17T00:49:01.489500Z

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.

dgb23 2020-12-17T00:55:06.489700Z

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...

dgb23 2020-12-17T00:55:25.489900Z

each returning a select

dgb23 2020-12-17T00:55:52.490100Z

that is specifically tailored to query a variant

kenny 2020-12-17T00:56:03.490300Z

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

dgb23 2020-12-17T00:56:15.490500Z

me neither ๐Ÿ˜„

kenny 2020-12-17T00:56:59.490700Z

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

dgb23 2020-12-17T00:57:04.490900Z

got me hooked Iโ€™m going to play a bit with spec2 now

dgb23 2020-12-17T00:57:18.491100Z

what do you mean with required keys?

dgb23 2020-12-17T00:57:23.491300Z

schema keys are not required

kenny 2020-12-17T00:59:20.491500Z

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

dgb23 2020-12-17T01:53:43.491700Z

(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"}])

kenny 2020-12-17T01:54:23.491900Z

You've changed the problem ๐Ÿ™‚

dgb23 2020-12-17T01:54:39.492100Z

I get the feeling that I didnโ€™t understand it in the first place

dgb23 2020-12-17T01:54:43.492300Z

๐Ÿ˜„

kenny 2020-12-17T01:55:32.492500Z

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

kenny 2020-12-17T01:55:44.492700Z

Without renaming my :user/orders key.

dgb23 2020-12-17T02:00:18.492900Z

i see but those are different specs

kenny 2020-12-17T02:00:57.493100Z

Same schema, different selected keys.

dgb23 2020-12-17T02:01:42.493300Z

the schema doesnโ€™t know about select (just tried it)

dgb23 2020-12-17T02:02:41.493500Z

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

kenny 2020-12-17T02:03:18.493700Z

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
                                              ]}])

dgb23 2020-12-17T02:06:09.494Z

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.

kenny 2020-12-17T02:07:16.494200Z

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.

dgb23 2020-12-17T02:08:44.494500Z

i mean this sound pretty powerful (from the wiki): https://github.com/clojure/spec-alpha2/wiki/Differences-from-spec.alpha#creating-specs-programmatically

kenny 2020-12-17T02:10:31.494700Z

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.

๐Ÿ‘ 1