malli 2026-07-10

Say I have a schema like this:

(def ^:private Config
  [:map
   [:runtime-config [:map
                     [:authentication
                      [:map
                       [:show-request-cookies {:default false} [:enum true false]]]]
                     [:caches
                      [:map
                       [:websockets
                        [:map
                         [:ttl-in-milliseconds {:default 300000} pos-int?]]]]] ;; 5 minutes
                     [:metrics
                      [:map
                       [:prometheus
                        [:map
                         [:enabled {:default false} [:enum true false]]]]]]     
                     [:sentry [:map [:dsn [:maybe :string]]]]]]])

I find that if I use (m/decode Config ,,,, mt/default-value-transfomer), I don't get default values for the maps unless I also do this:

(def ^:private Config
  [:map
   [:runtime-config [:map
                     [:authentication
                      [:map {:default {}}
                       [:show-request-cookies {:default false} [:enum true false]]]]
                     [:caches
                      [:map {:default {}}
                       [:websockets
                        [:map {:default {}}
                         [:ttl-in-milliseconds {:default 300000} pos-int?]]]]] ;; 5 minutes
                     [:metrics
                      [:map {:default {}}
                       [:prometheus
                        [:map {:default {}}
                         [:enabled {:default false} [:enum true false]]]]]]
                     [:sentry [:map [:dsn [:maybe :string]]]]]]])

Note the {:default {}} against the`:map` entries

For a small config, like this, it's okay(ish), but imagine a bigger config with lots of nested maps, where I define default values. Having to put {:default {}} against the parent(s) key(s) starts to look like noise.

Have I misunderstood something, about how to get the maps to have defaults when "applying-defaults" in the (m/decode ....)?