This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
(def registry
{:my/group [:vector :my/item]
:my/item [:int {:min 0 :max 480}]})
For :my/group I’d like to know that it’s a collection of :my/item’s. I tried the following
(let [k :my/group
s (some->> (m/schema [:schema {:registry registry} k])
(m/deref-all))
t (m/type s)
sub (first (mu/subschemas s))]
(if (contains? #{:vector :set} t)
{:type t :item-type (m/children (:schema sub))}))
; => {:type :vector, :item-type [:my/item]}
The :item-type
is almost what I need but even though the returned [:my/item] looks like a list of one keyword
, it’s actually of type :malli.core/schema
I can’t figure out, one, if I’m going about this reasonably, and two, how to get that keyword looking schema to be an actual keyword as it appears in the original schema.you can call m/form
to get the data representation out:
(let [schema (m/deref-all
[:schema {:registry {:my/group [:vector :my/item]
:my/item [:int {:min 0 :max 480}]}}
:my/group])
type (m/type schema)]
(if (#{:vector :set} type)
{:type type
:item-type (-> schema (m/children) (first) (m/form))}))
; => {:type :vector, :item-type :my/item}

if you need to do this recursive, you can use m/walk
:
(m/walk
[:schema {:registry {:my/group [:vector :my/item]
:my/item [:int {:min 0 :max 480}]}}
:my/group]
(fn [schema _ children _]
(cond-> {:type (m/type schema)}
children (assoc :children children)))
{::m/walk-schema-refs true})
;{:type :schema,
; :children [{:type :malli.core/schema,
; :children [{:type :vector
; :children [{:type :malli.core/schema
; :children [{:type :int}]}]}]}]}
would like to:
• rename m/-type
-> m/-name
• m/kind
-> m/type
(reading :type
property)
… but this would be a massive breaking change.
anyway, this gets rid of a lot of extra code in generators, tranformers, error messages and applications like json-schema
e.g. no need to map separately all of int?
, pos-int?
, neg-int?
, nat-int?
, :int
… just :int
and map everything to it via the new :kind
Do you see some new category of errors here? Users are anyway in control of mostly everything already…
Yes. Adding the kind property can contradict what the code actually does. Same problem with doc strings accumulating skew but with actual behavioral effect
Would it be possible to limit it to only :fn schemas?
[:fn {:kind :string} pos?]
Enjoy finding the source of this bug in a large code base
how is that different to:
[:and :string [:fn pos?]]
also, many other ways to create invalid logic:
[:int {:gen/schema :string}]
[:string {:decode/string parse-long}]
limiting only to :fn
- anything is possible, just would need reasoning for limiting just to that, e.g.
[:enum {:kind :uuid}
#uuid"f4ba0d9c-7b93-4030-8bf4-d731d5c3dc4d"
#uuid"954cb679-a36e-4b85-aaae-0844ad79a219"]
is the simplest way to describe a enum of uuids that also contains explicit type information (for decoding, encoding, clj-kondo etc)as the :and
works already, you could say:
[:and :uuid [:enum #uuid"f4ba0d9c-7b93-4030-8bf4-d731d5c3dc4d" #uuid"954cb679-a36e-4b85-aaae-0844ad79a219"]]
… and get almost the same benefits.for :type-properties
, something like this is anyway good to get rid of those extras:
'double? {:error/message {:en "should be a double"}}
'boolean? {:error/message {:en "should be a boolean"}}
'string? {:error/message {:en "should be a string"}}
It isn't, right, everything is a compromise. It's also one of the reasons I'm generally averse to fn schemas. This is just what I'm paranoid about. The more subtle the detail the more interesting the bug will end up being. Maybe in the grand scheme of things it's an acceptable tradeoff
How one can create a relation between the generated values? For instance I want to make sure that when the :type property in map is number the generator generate only numbers in :content property of the map.
Cool. Was not aware of multischema existence. Thanks!
I just opened this feature request to support ClojureDart on GH, but I'd like to see some discussion here https://github.com/metosin/malli/issues/996.
I see. If ClojureDarts implements multimethods, and properly handle reader conditionals, it becomes relatively straight forward to enable malli in ClojureDart.
As an example in namespace malli.impl.util
the following can be changed to use the :default
reader conditional instead of :cljs
as that functionality would be expected to be provided by any clojure implementation.
(defn -vmap
([os] (-vmap identity os))
([f os] #?(:clj (let [c (count os)]
(if-not (zero? c)
(let [oa (object-array c), iter (.iterator ^Iterable os)]
(loop [n 0] (when (.hasNext iter) (aset oa n (f (.next iter))) (recur (unchecked-inc n))))
#?(:bb (vec oa)
:clj (LazilyPersistentVector/createOwning oa))) []))
:cljs (into [] (map f) os))))