malli 2025-05-28

Hi all, is this a good approach for recursively making map keys optional?

(defn recursive-optional-keys
  ([?schema] (recursive-optional-keys ?schema nil))
  ([?schema options]
   (m/walk
    ?schema
    (m/schema-walker
     (fn [schema]
       (case (m/type schema options)
         ::m/schema (recursive-optional-keys (m/deref schema) options)
         :ref (recursive-optional-keys (m/deref schema) options)
         :map (mu/optional-keys schema)
         schema)))
    options)))
Does it seem like I've missed anything that I need to handle in order to correctly descend into all the referenced/contained sub-schemas?

1. perhaps a check for -ref-schema? would be more general to handle all refs. or even let-bind (let [schema (m/deref-all schema)] ...). 2. I don't think think this works for recursive schemas. that might be tricky to handle.

Perhaps the most reliable approach would be to create your own ref schema.

e.g., [:optional [:schema {:registry {::foo [:or L R]} ::foo]] => m/deref => [:or [:optional L] [:optional R]]

otherwise, if you don't need support for recursive schemas, you'll just get a stackoverflow if you pass one to recursive-optional-keys and otherwise your approach looks fine.

Since my schemas aren't recursive right now I'll stick w/ what I have, but if my needs change I'll try the ref schema approach and post it here. Thanks!

👍 1

You might need to use my 1) suggestion to support :merge , or think through whether optionalizing :merge 's children is equivalent to optionalizing its expansion.

ditto for :select-keys and other malli.util ref schemas.