malli

agorgl 2026-02-19T11:14:50.305269Z

I'm reading the malli documentation, and I'm kinda confused on the difference between mr/composite-registry and just mergeing schemas into a registry. There are lots of examples that just merge schemas into a registry like this:

(def registry (merge (m/default-schemas) (mu/schemas)))
but there are also examples that create a registry using mr/composite-registry like this:
(mr/set-default-registry!
  (mr/composite-registry
    (m/default-schemas)
    (met/schemas)))
what is the actual difference? I want to include mu/schemas for :merge schema into the default registry, what should I use and why?

2026-02-19T14:24:49.482679Z

If the registries you are merging are maps, there is no difference. Internally, mr/registry is always called coercing to Registry. mr/composite-registry, mr/set-default-registry! both do this. The one case you should be careful about when using merge on non-map registries like (merge (mr/composite-registry ..) (mr/composite-registry ..)). This won't work.

2026-02-19T14:28:11.279229Z

> I want to include mu/schemas for :merge schema into the default registry, what should I use and why? Either will work, because mu/schemas is a map, and set-default-registry! coerces to Registry internally.

2026-02-19T14:30:46.972669Z

It's worth noting that merge has a performance advantage over composite-registry. The latter iterates over multiple lookups instead of pouring them into the same map.

agorgl 2026-02-19T14:30:52.982669Z

ah thanks for the explanation!

👍 1
agorgl 2026-02-19T14:49:34.965529Z

It seems that circular schema references stop working when 'resetting' the default registry, poc:

(malli-registry/set-default-registry!
   (malli/default-schemas))

  (def sample-tree-schema
    [:map
     [:value :int]
     [:children {:optional true}
      [:vector
       [:ref #'sample-tree-schema]]]])

  (malli/assert
   sample-tree-schema
   {:value 1
    :children
    [{:value 2
      :children [{:value 4}]}
     {:value 3}]})

=> :malli.core/invalid-ref
any idea why?

opqdonut 2026-02-27T06:47:57.123939Z

Any ideas on how to improve the documentation?

2026-03-05T16:22:30.690429Z

@joel.kaasinen I brainstormed some here, just pushed what I had https://github.com/metosin/malli/pull/1261/changes

✅ 1
2026-02-19T15:05:39.797629Z

Guessing you need to add var-registry to the default registry https://github.com/metosin/malli?tab=readme-ov-file#var-registry

2026-02-19T15:06:34.547239Z

i.e., (composite-registry (default-schemas) (var-registry))

agorgl 2026-02-19T15:06:57.076319Z

so malli/default-registry is not malli/default-schemas by default

agorgl 2026-02-19T15:07:04.732389Z

kinda confusing

2026-02-19T15:07:22.596429Z

yeah check out the default default registry https://github.com/metosin/malli/blob/69366e440cfda6b402cc84a6e67d8694a89fa12c/src/malli/core.cljc#L3060

2026-02-19T15:21:55.329759Z

Under the hood, m/default-registry is a map of schemas, but mr/var-registry is a Registry. It's a good example of the difference between using merge and composite-registry here, merge wouldn't work.

2026-02-19T15:23:20.340749Z

It's a leaky abstraction but it might be a little less confusing in practice knowing that.

2026-02-19T15:24:33.590869Z

Leaky because default-registry doesn't document whether it's a map or a Registry, but we always assume it's a map just because it's more convenient (merge, assoc, select-keys).

agorgl 2026-02-19T15:33:48.232379Z

thanks again for the explanation, I too found out its a documentation issue

👍 1