malli

escherize 2025-01-28T21:29:31.219139Z

Hi, I am trying to see if I can use parse on some pretty hairy schemas to help bring clarity at work. I'm hitting something interesting, where validation functions tied to schemas are passed the parsed value when using m/parse. 🧵

escherize 2025-01-28T21:31:36.813779Z

It seems like that is not the intended behavior, but here's what I am seeing locally: These print different things, but that's not a big problem:

(mc/validate [:and [:catn ["a" :int] ["b" :keyword]]
              [:fn (fn [x] (prn x) true)]]
             [3 :x])
;; prints: [3 :x]

(mc/parse [:and [:catn ["a" :int] ["b" :keyword]]
           [:fn (fn [x] (prn x) true)]]
          [3 :x])
;; prints: {"a" 3, "b" :x}
The problem I'm hitting is when we actually try to validate these things with the attached :fn schema:
(mc/validate [:and [:catn ["a" :int] ["b" :keyword]]
              [:fn vector?]]
             [3 :x])
;; prints: [3 :x]

(mc/parse [:and [:catn ["a" :int] ["b" :keyword]]
           [:fn vector?]]
          [3 :x])
;; => :malli.core/invalid
I sort of can work around it by checking for e.g. (if it's a map with these keys, then it passes validation). but that's not really ideal. I'll look for an issue in the repo

escherize 2025-01-28T21:50:50.101999Z

I opened https://github.com/metosin/malli/issues/1166

2025-01-28T22:07:00.238609Z

I believe :and propagates parsed values through its children.

2025-01-28T22:07:37.536769Z

spec2 has a schema that doesn't, perhaps that idea is applicable here https://github.com/clojure/spec-alpha2/blob/4cbfa677c4cd66339f18e1c122222c05c69e0d8e/src/main/clojure/clojure/alpha/spec.clj#L777

escherize 2025-01-28T22:08:12.194849Z

nice find

2025-01-28T22:29:13.838379Z

ported to malli here https://github.com/metosin/malli/pull/1167

❤️ 1
2025-01-28T22:52:31.944049Z

needs a bit more thought, not sure why this case fails

(let [s [:and {:parse :non-flowing}
                        [:catn ["a" :int] ["b" :keyword]]
                        [:fn vector?]]]
                 (m/unparse s (m/parse s [3 :x])))

2025-03-28T23:25:12.132839Z

@escherize I've fixed the :and parser's reliability, please give it a shot. Also comes with a shiny new schema :andn https://github.com/metosin/malli/pull/1182

2
escherize 2025-01-29T16:44:07.172259Z

swapping the order of all children of :ands so the :fn validations come before the :catns actually works in my case (and this is a truly complex schema) https://github.com/metabase/metabase/blob/master/src/metabase/lib/schema/parameter.cljc#L200-L204 if you're curious. Also I can unparse it back if I do it this way.

escherize 2025-01-29T17:00:41.678779Z

Maybe if the :and parser sorts the children in the correct order that will fix it w/o needing to figure out how to unparse w/ :parser :non-flowing

2025-01-29T17:55:31.171709Z

[:and :catn :fn] is better for malli.generator and [:and :fn :catn] is better for m/parse...gah

➕ 1
2025-01-29T18:01:54.962809Z

Another idea is to only allow one transforming parser, and treat the rest as predicates. but you don't know which it is until you do the parsing, which means it's unclear how to unparse. both ideas need to distinguish between parsers that transform and those that don't somehow.

2025-01-29T18:03:50.265089Z

properties on :and seem to be a good way to communicate this.

escherize 2025-01-29T22:18:04.537669Z

wouldn't it be cool to never care about the order tho? Could it even work tho... probably not, consider:

(m/validate [:and
             [:catn ["a" :int] ["b" :keyword]]
             [:tuple :int :keyword]]
            [1 :a])

2025-01-29T23:14:07.863479Z

absolutely

2025-01-29T23:14:38.244659Z

I've been working to make the malli.generator for :and order-agnostic for about a year 🙂

❤️ 1