This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2023-03-15
Channels
- # babashka (4)
- # beginners (136)
- # calva (63)
- # clerk (7)
- # clj-kondo (8)
- # clojure (43)
- # clojure-boston (1)
- # clojure-europe (37)
- # clojure-nl (1)
- # clojure-norway (11)
- # clojure-uk (3)
- # clojurescript (6)
- # clr (1)
- # code-reviews (16)
- # cursive (45)
- # datomic (2)
- # docker (32)
- # emacs (10)
- # events (2)
- # exercism (1)
- # fulcro (3)
- # hugsql (1)
- # hyperfiddle (47)
- # leiningen (3)
- # lsp (30)
- # malli (39)
- # missionary (1)
- # off-topic (24)
- # pathom (2)
- # portal (14)
- # practicalli (5)
- # rdf (13)
- # reagent (18)
- # reitit (18)
- # releases (7)
- # remote-jobs (1)
- # sci (2)
- # shadow-cljs (45)
- # sql (7)
- # tools-build (11)
- # xtdb (13)
Could take a stab at merging :map
and :map-of
(https://github.com/metosin/malli/issues/43), best options so far:
Given data:
{:id #uuid"839a35ba-1be1-43d1-af31-0c8cff06690b"
123 "kikka"
456 "kukka"}
Schema to describe it could be:
1️⃣ ::m/default
would validate just the extra keys as a map
[:map
[:id :uuid]
[::m/default [:map-of :int :string]]]
2️⃣ ::m/default
would validate all extra key-value pairs via :tuple
[:map
[:id :uuid]
[::m/default [:tuple :int :string]]]
thoughs?What about just [::m/default [:int :string]]
? Just "special case" like other :map
values.
But OK, I see the value that it is a real Malli schema.
I think it would be nice to make it explicit that it's either a tuple of values or a map-of values, rather than relying upon special "knowledge" that [:int :string]
means a map. However, I'm have not that strong a feeling, and thus I can see the value in just [:int :string]
too 🙂
I think option 2 is strange. Because the extra values are some number of those tuples, which :map-of
describes. If we want to match regular :map
keys drop :tuple
.
1 it is then. Draft here https://github.com/metosin/malli/pull/871
by default stripping works like this:
(m/decode
[:map
[:x :boolean]
[:y :int]]
{:x 1, "kikka" "kukka", 3 4}
(mt/strip-extra-keys-transformer))
; => {:x 1}
disabled for explixitely open maps:
(m/decode
[:map {:closed false}
[:x :boolean]
[:y :int]]
{:x 1, "kikka" "kukka", 3 4}
(mt/strip-extra-keys-transformer))
; => {:x 1, "kikka" "kukka", 3 4}
whatbout :map
+ ::m/default
?
(m/decode
[:map
[:x :boolean]
[:y :int]
[::m/default [:map-of :int :int]]]
{:x 1, "kikka" "kukka", 3 4}
(mt/strip-extra-keys-transformer))
; 1) => {:x 1, "kikka" "kukka", 3 4}
; 2) => {:x 1, 3 4}
I think: • 1️⃣ is more consistent with current behavior (and easier to implement) • 2️⃣ sounds what I would expect
"kikka" "kukka"
is an extra entry, so I think it should be stripped. so 2️⃣ seems more right
perhaps we need a separate strip-extra-keys-transformer and strip-unknown-keys-transformer
One can run apply the mt/strip-extra-keys-transformer
in two phases:
1. before the ::m/default
values have been transformed, e.g. we don’t know the validity of extra keys/values. With this, I would like to keep all extra keys
2. after ::m/default
values have been transformed -> we could validate all (non-explicitly defined key-values) and strip away everything that doesn’t match the schema
same numbers (1 & 2) as before, just with more context. For example, in current reitit, the mt/strip-extra-keys-transformer
is applied BEFORE the normal transformers, so 2 would not work => the childs are not transformed and thus, all the entries could be invalid.
I think this could be handled in mt/strip-extra-keys-transformer
:
1. check if the schema has ::m/default
key
2. If it has, mount a :leave
transformer that validates all the extra key-values and strips away the invalid ones.
boils down to what does “unknown” keys mean, ping @US1LTFF6D.
> after ::m/default
values have been transformed -> we could validate all (non-explicitly defined key-values) and strip away everything that doesn’t match the schema
I think this is the right way to go
that is non-trivial to solve. Given schema:
[:map
[:x :int]
[::m/default [:map-of :int :int]]]
For value:
{:x 1, 1 1, 2 2, "3" "3", "4" "4"}
… we can trip the explicit keys easily so it’s:
{1 1, 2 2, "3" "3", "4" "4"}
… but how do we know which entries are valid?
1. validate them 1-by-1 {1 1}
, {2 2}
, … and merge all valid submaps together. This kinda works but as one can provide any schema as ::m/default
, this might not always correct, e.g. given [::m/default [:map-of {:max 2} :int :int]]
2. validate everything once => doesn’t work
3. try all combinations => bad idea
ideas welcome, @US1LTFF6D.for extra keys:
{1 1, 2 2, "3" "3", "4" "4"}
correct answer after stripping the invalid keys would be:
{1 1, 2 2}
only way to do that I can think of is the 1) - validate key-values 1-by-1 and merge all valid ones together. This should work for many/most of the cases. but not for all.I would've expected
(m/decode [:map-of :int :int] {1 1, 2 2, "three" "3"} (mt/strip-extra-keys-transformer))
to be {1 1, 2 2}
so (m/decode [:map [:x :int] [::m/default def]] val)
would delegate to (m/decode def (dissoc v :x))
the -transformer
of :map
already covered the delegation, so just needed to add the :map-of
stripping and it works. I’m surprised how well this played out!
(m/decode
[:map
[:x :int]
[::m/default [:map
[:y :int]
[::m/default [:map-of :int :int]]]]]
{:x 1, :y 2, :z 3, 1 1, "2" 2, 3 "3", "4" "4"}
(mt/strip-extra-keys-transformer))
; => {:x 1, :y 2, 1 1}
https://github.com/metosin/malli/pull/871/commits/a1bb82f03e6e30a2d4b05f0404abdac8e2f58e90… and contructed a :tuple
schema of the :map-of
so we can iterate over key-values without stuffing those into a map. less garbage and don’t have to worry about :map-of
level constraints like :min
and :max
properties.