Good morning/evening all! Today I discovered that you cannot do something like
m/=> my-alias/my-function [:=> ...
It seems like I need the full namespace name if I want to have defs in another ns - am I doing something wrong?
No, this is a missing feature. Could you create an issue, I've done this before for Typed Clojure so I can easily propose a fix.
Hi all, two Malli questions: There appear to be two ways to define schemas from local registries:
(def reg {::foo :string})
(let [s (m/schema ::foo {:registry (merge (m/default-schemas) reg)})]
(m/validate s "blegh"))
(let [s (m/schema [:schema {:registry reg} ::foo])]
(m/validate s "blegh"))
These both work, but I'm trying to understand the practical difference. Why would I pick one over the other?
Second question: Let's say I have a deeply nested map, where some leaf supports different values in different contexts. I could define that schema with a function:
(defn my-schema [allowed-values]
[:map
[:whatever
[:map ...
[:key (into [:enum allowed-values])]]]])
But I'm wondering if Schema supports parameterized schemas more directly, so that I could compile the schema and provide the allowed values at schema checking time?1. The former is preferred because the schema can be represented as data. This is key to many of malli's features such as error reporting.
2. Kind of. Check out m/-proxy-schema . The only way to create a parameterized schema is to create new IntoSchema constructor and add it to the registry under a name which you then pass arguments to. See the implementation of :->.
Thanks!
Willing to elaborate or point me in the right direction to understand why the first option can be represented as data but the second option cannot?
Yes, try printing each schema. You'll probably see the latter has IntoSchema instances which aren't reproducibly serializable. This prevents roundtripping the schemas with (comp m/schema m/form)
Do you mean something different from just printing at the repl?
user> (m/schema ::foo {:registry (merge (m/default-schemas) reg)})
:user/foo
user> (m/schema [:schema {:registry reg} ::foo])
[:schema {:registry #:user{:foo :string}} :user/foo]
Oh sorry I misread the original code. Those two are equivalent.
I thought you were giving (m/default-schemas) to the local registry.
I suppose your misread of the code has illuminated something for me: in the first example I'm constructing a schema and passing the registry to use for the construction of that schema to the m/schema function, so the registry isn't part of the schema itself, but in the second example I'm constructing a schema that defines its own local registry. So in the second example if my registry had more stuff in it it could include things that can't be serialized, such as IntoSchema objects.
Yes, but it probably won't work. Try it.
I can't remember exactly where it falls down but local and global registries have subtle differences around IntoSchemas.
Yeah, (m/schema [:schema {:registry (m/default-schemas)} ::foo]) throws
Back to your original question. There is no difference in terms of expressivity. The main difference is scope.
The name is scoped differently. Malli uses dynamic scope, so this can be a subtle difference.
For those two exact schemas there's almost no difference. There's so many different ways to manage registries it's hard to summarize beyond that.
I think I understand well enough for now. Thanks again!
Is there any reason to only pretty print in dev ? https://github.com/metosin/malli/blob/master/README.md#prettyexplain it feels wierd to have top class error reports during development and solving riddles in prod whenever a coercion error is thrown
I believe the main reason these kinds of nice extras are optional is keep the core minimal. If you can spare the extra startup time, bundle size etc, then I don't see a problem.