balli.provider¶
Schema inference (Malli's malli.provider): provide takes a collection of sample values and returns a schema FORM that validates all of them. Two passes: 1. Stats fold -- classify every sample as nil / map / set / vector / sequential / scalar; maps recurse per key (tracking per-key presence counts + total map count), collections recurse over their elements (union element stats), scalars record which of the preference-ordered scalar types accept them (via balli.core validators). 2. Synthesis -- turn the stats into a form: a single scalar type-set picks the most specific type accepting ALL samples (preference order [:int :double :keyword :symbol :string :boolean :uuid]); collections become [type elem-schema] ([type :any] when no elements were seen); maps become [:map [k v] ...] with {:optional true} on keys present in fewer samples than the map total (entries in first-seen order), or [:map-of k v] per the heuristic below; mixed classifications join under [:or ...] (first-seen classification order), and nil samples wrap the rest in [:maybe ...]. No samples at all -> :any. Optional inference upgrades: - {:infer-enums true :enum-threshold n} emits [:enum ...] for scalar sample sets with at most n distinct values (default 8). - {:infer-tuples true} emits [:tuple ...] for vector samples that all share one arity. - {:closed-maps true} emits record-shaped maps as [:map {:closed true} ...]. - {:optional-threshold f} annotates optional map keys that appear in at least f of record samples with {:balli.provider/common true}; schemas still validate every source sample. - {:prefer-map-of false} disables the [:map-of ...] heuristic. :map-of heuristic (opts key :map-of-threshold, default 3): at least threshold map samples AND every per-key value schema equal AND every key schema equal (keys inferred as scalar samples) AND distinct keys across samples > (total keys)^0.7 -> [:map-of k v]. Deviations from malli documented here on purpose: - Mixed int + float samples synthesize [:or :int :double], NOT :double or number?: Balli's float? rejects ints ((float? 1) -> false), unlike JVM malli where double accepts any number, so no single scalar type accepts a mixed int/float sample set. - Tuple and enum inference are opt-in so existing broad provider output remains stable for users who do not request Malli's narrower hints. - Scalars none of the preference types accept (e.g. arbitrary Python objects) infer :any; :any among mixed branches collapses the whole union to :any.
provide¶
Kind: defn
Infer a schema FORM from samples (any seqable of values). opts supports {:map-of-threshold n} (default 3) for the [:map-of ...] heuristic, {:infer-enums true :enum-threshold n}, {:infer-tuples true}, and {:closed-maps true}. {:optional-threshold f} annotates optional keys present in at least f of samples with {:balli.provider/common true}; it never makes source samples invalid. {:prefer-map-of false} disables the [:map-of ...] heuristic. Every sample validates against the returned form. No samples -> :any.
provide-report¶
Kind: defn
Infer a schema and return evidence about how it fits the source samples. The report is data-only: {:schema form :samples n :valid-samples n :valid? bool :confidence f :invalid-samples [...]} :confidence is the fraction of samples validated by the inferred schema. A correct provider run should normally report 1.0; exposing it makes provider fuzzing and user audits straightforward.