Hello everyone! I have a couple of questions for you malli experts 🙂
1. How do I define a function schema for a multimethod?
Say I have (defmulti wine :style), and I have in my global registry {:red [:map [:age :int]] :white [:map [:region :keyword]]}
How do I define a m/=> style function schema for wine that applies the right schema based on the multimethod's dispatch function?
I'm aware of :multi but I'm looking for open dispatch
2. Say that my multimethod's dispatch function is a bit more complex, like (defmulti wine (fn [w] [(:style w) (:body w)]))
How do I deal with that? as I understand it, the keys in a malli registry must be either keywords or strings, a registry can't have {[:red :light] [:map [:age :int]]}
p.s. I'm working in clojurescript in case that changes anything
Awesome, yes it does! And this approach of mutating the :multi schema is elegant 👍 Thank you very much @ambrosebs
I simulated mutable multimethods using immutable structures when I modeled clojure's semantics for my phd
I think my advisor suggested it
I came up with this
(malli/-simple-schema
{:type :multi-dynamic
:compile (fn [_properties [spec-key] _options]
{:min 1 :max 1
:pred (fn [Val] (malli/validate (spec-key Val) Val))})})
But I still have the 2nd problemindeed, but this is closed dispatch (i.e. you have to explicitly list all kv pairs), I'm looking for open dispatch based on the global registry
pardon, "default registry", which I configure as a mutable registry just like in this example: https://github.com/metosin/malli?tab=readme-ov-file#mutable-registry
Repro:
(ns test
(:require [malli.core :as malli]
[malli.registry :as malli-reg]))
(def registry* (atom {[:red :blue] :int}))
(malli-reg/set-default-registry!
(malli-reg/composite-registry
(malli/default-schemas)
(malli-reg/mutable-registry registry*)))
(malli/validate [:red :blue] 123)
Fails with
; Execution error (ExceptionInfo) at (:1).
; :malli.core/invalid-schema what do you by "open dispatch"? Not sure if I understand how it supposes to work.
like call validate in runtime?
I mean the two schemas in your example should be looked up in the default registry instead of being provided statically inside of the :multi definition
something like this? https://malli.io/?value=%5B%7B%3Atype%20%7B%3Ax%201%7D%2C%20%3Asize%2010%7D%0A%20%7B%3Atype%20%7B%3Ax%202%7D%2C%20%3Aname%20%22tiina%22%2C%20%3Aaddress%20%7B%3Astreet%20%22kikka%22%7D%7D%5D&schema=%5B%3Avector%0A%20%5B%3Amulti%0A%20%20%7B%3Adispatch%20%3Atype%0A%20%20%20%3Aregistry%20%7B%3Ax%20%5B%3Amap%20%5B%3Asize%20int%3F%5D%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Ay%20%5B%3Amap%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5B%3Aname%20string%3F%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5B%3Aaddress%20%5B%3Amap%20%5B%3Astreet%20string%3F%5D%5D%5D%5D%7D%7D%0A%20%20%5B%7B%3Ax%201%7D%20%3Ax%5D%0A%20%20%5B%7B%3Ax%202%7D%20%3Ay%5D%5D%5D so there are static dispatch values that refer to schemas known to the registry (in this case local, but there is no difference with using a global one) references are also static values but they don't have to be the same as dispatch values
Not quite, my bad if my intent is not coming across... Let me take a different approach and explain what my goal is.
I make use of function schemas ( m/=> ) in my code, and I'd like to extend this practice to multimethods as well.
However the schema of the input arguments of a multimethod depend on the dispatch value, so while a multimethod has the dispatch function definition (defmulti mymulti my-dispatch-fn) and the methods definition (defmethod my-dispatch-value ...)
Likewise, I'd like to define a function spec for the multimethod in a similar fashion, say: (m/=> [:multi {:dispatch my-schema-dispatch-fn}]) so that when I define a new method, I can also define a schema for it in the global registry (register! :my-method-schema-dispatchvalue [:map ...])
does something like multi-spec (for spec) look like what you'd need? https://clojuredocs.org/clojure.spec.alpha/multi-spec
yes, exactly!
not aware of anything but seems like a good insight to use the registry for extensions somehow.
maybe the "multi-spec" schema itself could be mutated with new cases (for a theoretical malli equivalent)
ah, maybe you just need a function that does a functional update on a :multi in the registry. (swap! registry update :my-multi add-case ...)
@simon900 does this solve your problem? https://github.com/metosin/malli/pull/1162/files
Hi i have a question on silencing 'special' field(s) with malli.generator/generate . Most of my malli schemas contain a :? field which is needed to pass validation, but not ideal to be generated. An example is below.
(def schema1 {:ns/MyMsg [:map {:closed true}
[:int32_val :int]
[:? {:optional true} :any]]})
(def registry1 {:registry (malli.registry/composite-registry
malli.core/default-registry
schema1)})
(malli.generator/generate [:ref :ns/MyMsg] registry1)
; {:int32_val 1770645}
; {:int32_val 9, :? #{}} ; <------- remove :?
; {:int32_val 3978, :? nil} ; <------- remove :?
Is there a way to write the spec so that :? will not be generated?something like [:map {:gen/fmap #(dissoc % :?)}]
thanks it works perfectly