Is there a version of :or that allows for specifying a :dispatch function the way :multi does? My specific use case is matching against a value's metadata - something like:
(let [s (m/schema [:or {:dispatch meta} [:map [:k1 :keyword]] [:map [:k2 :keyword]]])]
(m/validate s ^{:k1 :test} [1])) ;; => false
While it's possible to use a :fn schema for this, matching a transformed value against a schema (as opposed to :multi, which seems like it's for concrete values) could be a cleaner and more declarative method of solving this (and possibly other) problems.You can use multi with dispatch function comp boolean meta
could you show me an example?
Remind me in a couple of hours if I forget, I should get to it by then
Here you go
(m/validate
[:multi {:dispatch (comp boolean meta)}
[true vector?]
[false map?]]
(with-meta [1 2] {:some :thing}))
(m/validate
[:multi {:dispatch (comp boolean meta)}
[true vector?]
[false map?]]
(with-meta {1 2} {:some :thing}))
(m/validate
[:multi {:dispatch (comp boolean meta)}
[true vector?]
[false map?]]
{1 2})Thanks for this example - it seems like it can test for the presence or absence of metadata on values, and then match those values against a schema. However, I'm trying to validate against specific key/value pairs within a value's metadata map.
I think this may be more general than :or or :multi; the basic idea is "match value x against schema s after calling function f on it." (s may or may not be itself a :or / :multi schema). In function form:
(defn comply
"Call function f on the value and then match it against the given schema"
[f schema value]
(m/validate schema (f value)))
(comply meta
[:map [:k1 :string] [:k2 {:optional true} :int]]
^{:k1 "test"} [1 2 3])
(comply meta [:map [:k1 :string] [:k2 {:optional true} :int]] ^{:k2 3} [1 2 3])
(comply meta [:map [:k1 :string] [:k2 {:optional true} :int]] [1 2 3])While this may work for some cases, I think problems with the function approach would arise in cases where there's a local registry.
In that case I'd write a new schema type which refers to the object's meta.
Use: [:meta ?schema]
In implementing a custom schema, I think I'm struggling a bit with reify due to lack of experience. Here's a simplified example:
(require '[malli.core :as m :refer [AST -to-ast -from-ast -from-child-ast]])
(defn -meta-schema
[]
^{:type ::m/into-schema}
(reify
AST
(-from-ast [parent ast options (-from-child-ast parent ast options)])))
I get the following error:
Syntax error (IllegalArgumentException) compiling reify* at (notebooks/net/respatialized/kindly_spec.clj:578:3).
Can't define method not in interfaces: _from_astyou accidentally slurped the body into the params, it seems
Simplified:
(defn -meta-schema
[]
^{:type ::m/into-schema}
(reify
AST
(-from-ast [parent ast options]
(-from-child-ast parent ast options))))
PEBKAC, whoops, thanks!
https://github.clerk.garden/respatialized/kindly-spec/commit/86a33606f3e41a590fb0e59ae056b4607e8844c4/#a-custom-malli-schema-for-metadata
I have a very basic example of a custom schema for a value's metadata using m/-simple-schema, detailed a bit in this notebook that outlines a specification for https://github.com/scicloj/kindly values, which use metadata for annotation purposes. However, I did encounter an issue when trying to use :refs and local registries which I noted there. I'm wondering if -simple-schema isn't the right constructor for the schema I'm trying to build, but this is also my first time extending malli in this way so it may just be my lack of experience with custom schemas.
https://cloogle.phronemophobic.com/name-search.html?q=malli.core%2F-simple-schema&tables=var-usages was super helpful for finding examples of custom schemas, but all outside of malli appear to be made with m/-simple-schema. The only examples I could find of the m/-into-schema protocol were from malli.core itself - unsurprisingly they were quite complex! A more detailed doc or blog post on the topic of writing custom schemas would be super helpful for future guidance on the topic.
It's been a while since I messed around with registries, hope someone else can think of a straightforward solution
the problem is that -simple-schema's child is not a schema. its use of walk-leaf gives it away. instead of walking its children, it considers itself a leaf. (walking is how registries are propagated)
you need a constructor where the schema is a child. -collection-schema is an example.
I would adapt -maybe-schema instead.
Copy that constructor and change the -validator and -explainer to remove the nil? checks. Then make sure (validator (meta x)) is called instead of (validator x). Ditto in ->parser.
Change the occurrences of :maybe to ::meta.
And make sure to update the ::into-schema keywords to ::m/into-schema. Ditto ::schema.
you might also be able to use -proxy-schema but the tradeoffs aren't immediately clear to me. it might be fine.
thank you! this is really helpful guidance. I will try this out and update the notebook based on the revisions I make.