metosin / malli

High-performance data-driven data specification library for Clojure/Script.
Eclipse Public License 2.0
1.47k stars 209 forks source link

Ability to use Vars as schemas #651

Closed vemv closed 8 months ago

vemv commented 2 years ago

Context / Problem statement

Malli schemas, naturally, can be defined in terms of previously defined schemas, for example:

(def foo [:map
          [:name :string]])

(def bar [:map
          [:foos [:vector foo]]])

i.e. the value of the foo var is implicitly derefenced and used to compose #'bar.

However, this means of composition is lossy:

...importantly, AIUI, with this pattern it is impossible for Malli to 'see' that foo has a parent/child relationship with bar. For bar, there's no foo - there's just a value, with unknown provenance.

So one cannot introspect schema hierarchies, which arbitrary tooling could benefit from.

Proposal

Vars should be schemas exactly equivalent to the value of dereferencing a given var.

So:

(def bar [:map
          [:foos [:vector #'foo]]])

...would be a valid schema, with identical semantics to the snippet without #' syntax.

I am currently able to achieve this by extending the Schemas and Schema protocols.

It works, but the protocols appear to be private. I'd appreciate something dependable, and that more people can benefit from.

Use cases

Tools that emit documentation can greatly benefit from getting schema names, and file/name/column information.

Other var metadata might also be handy for arbitrary purposes.

Alternatives

https://github.com/metosin/malli/tree/cd71928c5c5e6f731a00a87006452828a94621eb#dot hints how to accomplish something similar OOTB.

However:

Unknowns

Not sure if it would be easy to make recursive or mutually-dependent schemas work.


Hope you find the idea interesting.

Cheers - V

ikitommi commented 2 years ago

Thanks for a well-written Issue

Thinking Aloud:

(def Kikka (m/schema [:int {:min 0}]))
(m/defschema Kikka [:int {:min 0}])
vemv commented 2 years ago

Thanks for the quick response! (m/defschema Kikka [:int {:min 0}]) would sound great to me considering the abstracted-away performance advantage.

I had seen in a couple codebases in the wild using vanilla defs i.e. not everyone is necessarily aware of m/schema.

Nothing else to add from my side, will be happy for any hammocking/news.

vemv commented 2 years ago

Additional thought: for [:fn string?], if one used instead [:fn #'string?], then one could grab string?'s fully-qualified name more reliably, without reverse-engineering it like clojure.spec does. And of course extra metadata would also be available.

i.e., the #' syntax has various use cases, so inviting people to use it has more than one benefit.

viebel commented 2 years ago

I am working with @vemv on this feature and we have an interesting use case for this feature in the context of creating a schema explorer visual tool.

vemv commented 8 months ago

TIL they made it in - happy to read that , thanks!

https://www.metosin.fi/blog/2024-01-16-malli-data-modelling-for-clojure-developers