I was thinking about something like
[:vector
[:map [:type :work] [:value string?]]
[:map [:type :personal] [:value string?]]]
but it didn’t work
Thank you @alpox i’ll probably go with your first suggestionboth emit results like:
([{:type :personal, :value ""} {:type :work, :value ""}]
[{:type :work, :value "l"} {:type :personal, :value ""}]
[{:type :personal, :value "z1"} {:type :personal, :value ""}]
[{:type :personal, :value "ZG"} {:type :personal, :value "N"}]
[{:type :personal, :value "qQ6"} {:type :work, :value "A8h"}]
[{:type :personal, :value "2588"} {:type :personal, :value "Djv12"}]
[{:type :work, :value ""} {:type :personal, :value "c"}]
[{:type :work, :value "U2uHMI"} {:type :personal, :value "376ihr"}]
[{:type :personal, :value "n6Ct4d"} {:type :personal, :value "V09xL8"}]
[{:type :work, :value "9"} {:type :personal, :value ""}])
if the first map is always work and second personal, :tuple
for the win:
[:tuple
[:map [:type :work] [:value string?]]
[:map [:type :personal] [:value string?]]]
Thank you, the :multi
works for my use case 🙏
@ikitommi In other languages people usually define this as an ADT. Maybe :enum
can also be used for this?
(I didn't see any docs about :enum
in the README, only examples)
:enum
is about values, :or
is about schemas:
[:or
[:map [:type [:= :work]] [:value string?]]
[:map [:type [:= :personal]] [:value string?]]]
fixed, thanks to @borkduderight, that's what I was thinking
but, :or
does a linear scan over schema, :multi
can be much more efficient.
why did you have to write [:work [:map [:type [:= :work]] [:value string?]]]
with :multi
but [:map [:type :work] [:value string?]]
with :or
?
(`either` was deprecated in Plumatic Schema because of this, malli supports that as it’s usefull to describe the real world, despite being slow)
could be:
[:work [:map [:type string?] [:value string?]]]
if you support :case
for a closed world of possibilities it could possibly be even faster?
… but the generator would then just generate any keyword for the :a:
what kind of code would the :case
emit?
I was referring to [:type [:= work]]
vs [:type :work]
oh, that’s my bug. [:type :work]
doesn’t work.
looks for :work
from the registry, which doesn’t exist.
ah ok then
(I have no idea what :case
would emit, just bouncing an idea)
[:= :work]
is basically same as [:enum :work]
so [:map [:enum :work :personal] [:value string?]]
would also work
[:map [:type [:enum :work :personal]] [:value string?]]
yes
but if the :type
effects the :value
, or any other parts of the schema, then :multi
is the way.
does it generate a multi-method behind the scenes?
no, it’s closed and immutable by design.
just a dispatch map from key -> schema. and the key is looked up using the :dispatch
function.
:dispatch
function is applied to value, which returns the :multi
key, which selects the schema
and, :dispatch
can be a sci-function, of course 🙂
(m/validate
[:multi {:dispatch 'first}
[:sized [:tuple keyword? [:map [:size int?]]]]
[:human [:tuple keyword? [:map [:name string?] [:address [:map [:country keyword?]]]]]]]
[:human {:name "seppo", :address {:country :sweden}}])
; true
so, there is always a map-lookup, so not as fast as case
, but it would require code-generation, which would mean a macro.
yeah, that makes sense
could add a :conditional
, where the entry keys are functions, and the first one will match. Like :or
, but more explicit and short-circuits on first match:
[:conditional
[map? [:map [:x int?]]]
[int? [:int {:min 1, :max 2}]]]
malli.core is distilled into tons of helper functions to build new schemas, really easy to add things like this atm.
doesn't :or
short-circuit?
btw, I also implemented case
as a map-lookup in sci, same problem ;)
sorry, it does. I think the issue is that you can have multiple branches that would match, making the rest effectively unreachable.
also, the :or
tranformation is bit hacky, depending whether you are doing decoding or encoding, we need to check if the transformation turned the value valid or not. that’s slow.
… but, works 🙂