Fork me on GitHub
#malli
<
2022-08-03
>
Stel Abrego05:08:43

Hey y'all, I'm wondering if it's possible in Malli to create a spec for a map with these qualifications: • There are some specific keyword keys that are required (this is simple in Malli) • There can also be an arbitrary amount of keys that are vectors, but they all must be vectors of keywords and their values must be maps. (not straightforward) • Besides the two possibilities above, the map is closed (ex no string keys for example, also not straightforward) The last two things are the weird requirements. Seems like :map can't really support this. I might need to use [:and [:map ... ] [:fn ...] but even then it might be awkward to satisfy #3.

pppaul16:08:11

using :fn is how i did exactly this

pppaul16:08:53

last post is the solution, unless you want to give malli a PR and do some hard work

pppaul16:08:24

[:and
 [:fn (fn [m]
        (->>
          (dissoc m :specified :optional :required)
          (m/validate (m/schema [:map-of keyword? string?]))))]
 [:map
  [:required [:enum :a :b]]
  [:optional {:optional true} [:enum :a :b]]
  [:specified :int]]]
make a function that outputs something like this

Stel Abrego22:08:12

@U0LAJQLQ1 thank you for the help! I'm going to look at this later.

pppaul23:08:39

just be aware that i didn't provide a general function, you need to create one that respects the keys in your malli map schema.

👍 1
Stel Abrego19:08:49

A little update, I realized that clojure.spec can solve this problem with actually great error messages so I might rewrite in spec. Might write a blog post about it later too.

Ben Sless15:08:09

What's the best way to express in malli "a map must contain at least one of a set of kv"?

Noah Bogart16:08:14

is that :or or :multi?

Ben Sless17:08:44

It's probably :or, but it's gross to have [:and M1 [:or M2.0 M2.1 M2.2]]

Ben Sless17:08:02

I also think this requires some awareness of implementation details to get coercion right, because when dealing with two keys, you'll want to try to coerce both, i.e. [:or [:merge Ma Mb] Ma Mb]]

Ben Sless17:08:11

That's even messier and for only two keys

Ben Sless17:08:26

now do that with three keys, the combinations become unreasonable

Ben Sless17:08:26

The correct thing to do in these situations is compile an execution plan

Ben Sless17:08:36

Sort of like pattern matching libraries do

Noah Bogart17:08:57

you could also do a function schema for this, make the various keys optional and then perform the check in the :fn

Ben Sless18:08:11

I am morally opposed to function schemas

😂 1
Noah Bogart18:08:15

any particular reason? seems appropriate for this kind of work

Ben Sless19:08:33

They're completely opaque, not well defined, not serializable, hard up to impossible to generate

Ben Sless19:08:47

The semantics of "at least one of this group" are clear, don't need a function. It can (should) be another schema type

Noah Bogart19:08:10

That makes sense to me. I wonder how hard it is to write a new schema type like this

Ben Sless20:08:24

A bad implementation is easy, an efficient implementation is hard 🙂

😂 1
Noah Bogart20:08:24

Is it? Naively, I would expect it to be an or:

(fn [obj]
  (or (contains? obj :a)
      (contains? obj :b)
      (contains? obj :c)))
or something similar. is there more to it?

Noah Bogart20:08:55

Maybe I'm misunderstanding the actual problem

Ben Sless20:08:46

because there's a combinatoric explosion, you need for two keys: [:or [:map [:k1 S1] [:k2 S2]] [:map [:k1 S1]] [:map [:k2 S2]]]

Ben Sless20:08:24

for three keys you have 7 options (I think, I'm tired)

Noah Bogart20:08:46

"At least one of a set" means as long as one exists, it doesn't matter if others exist, so you can just check each one

Noah Bogart20:08:31

could compile a list of the ones that do exist and then merge them after the fact too

Ben Sless20:08:35

Yes, but now make decode, encode, parse and explain work 🙂

Noah Bogart20:08:30

hah I won't be nerd sniped! but yes, if you don't want to directly implement a bunch of stuff, I think I can see the issue more clearly

Ben Sless20:08:58

That's why I dislike function schemas 🙂

👍 1