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 suggestion@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 🙂