malli

sohalt 2025-07-23T17:52:51.416509Z

I'm trying to implement a custom transformer for a schema in my registry but cannot get it to work. I have

(ns test
  (:require
   [malli.core :as m]
   [malli.registry :as mr]
   [malli.transform :as mt]))

(defrecord Foo [x])

(def foo? (partial instance? Foo))

(mr/set-default-registry!
 (mr/composite-registry
  (m/default-schemas)
  (mr/simple-registry {:foo [:fn foo?]})))

(def string->foo-transformer (mt/transformer {:name :string-foo-transformer
                                              :encoders {:foo (fn [x] (->Foo x))
                                                         [:fn (partial instance? Foo)]
                                                         (fn [x] (->Foo x))
                                                         'foo?
                                                         (fn [x] (->Foo x))}
                                              :decoders {:foo (fn [x] (->Foo x))
                                                         [:fn (partial instance? Foo)]
                                                         (fn [x] (->Foo x))
                                                         'foo?
                                                         (fn [x] (->Foo x))}}))

(m/decode [:map [:x :foo]] {:x 1} string->foo-transformer)
;; => {:x 1}
(m/decode :foo 1 string->foo-transformer)
;; => 1
I would expect to get something like
;; => {:x #test.Foo{:x 1}}
;; => #test.Foo{:x 1}
Any pointers what I'm doing wrong?

2025-07-23T18:20:26.568929Z

I'm not familiar with passing the :encoders and :decoders when creating a named transformer but I might just be ignorant about that. I usually do something like this:

(def string->foo-transformer (mt/transformer {:name :string-foo-transformer}))

  (m/decode [:map
             [:x {:decode/string-foo-transformer ->Foo} :foo]]
            {:x 1}
            string->foo-transformer)

2025-07-23T18:21:32.976249Z

It looks like the decoders test is applied on the top level of the schema, e.g. this works:

(def string->foo-transformer (mt/transformer {:name :string-foo-transformer
                                                :decoders {:map ->Foo}}))
  (m/decode [:map [:x :foo]]
            {:x 1}
            string->foo-transformer)

sohalt 2025-07-23T18:29:09.211089Z

Thank you! I think the issue is that the lookup does not respect the schemas from the custom registry:

(m/decode :string "foo" (mt/transformer {:name :string->foo-transformer
                                         :decoders {:string ->Foo}}))
;; => #test.Foo{:x "foo"}

(m/decode :foo "foo" (mt/transformer {:name :string->foo-transformer
                                      :decoders {:foo ->Foo}}))
;; => "foo"

👍 1
sohalt 2025-07-23T18:29:35.034179Z

e.g. it works with the malli built-in :string but it does not work with my custom :foo schema

sohalt 2025-07-23T18:53:35.723589Z

The dispatch is on m/type so this works:

(m/decode :foo "foo" (mt/transformer {:name :string->foo-transformer
                                      :decoders {:fn ->Foo}}))
;; => #test.Foo{:x "foo"}
But it's not really what I was expecting.

2025-07-23T18:57:53.529889Z

Ah I guess that makes sense if you think of :foo as the schema name or key and :fn as the type of the schema

sohalt 2025-07-23T18:58:31.171769Z

Yes. But I would like to be able to use different transformers for different schemas. I don't think that's possible like this, is it?

2025-07-23T19:06:56.277359Z

It looks like you need to set the type of the custom schema:

(mr/set-default-registry!
    (mr/composite-registry
      (m/default-schemas)
      {:foo (m/-simple-schema {:type :foo
                               :pred foo?})}))

sohalt 2025-07-23T19:12:03.259759Z

Ah, thanks!

👍 1