Fork me on GitHub

@dcj there is poor man's documentation about that change in CHANGELOG. The m/children returns the [key properties schema] tuple3s, so just use that instead. The new m/entries (renamed iit so it's not a silent/evil breakage) is needed to effectively use the entry properties in schema applications like value transformation and schema transformations.


there is a long discussion about the options and the second in the related issue ( and it's also documented in docstrings now:

thanks3 3
Dave Simmons05:09:54

Morning - I've been using Malli for the last few month. Last night when compiling my code lein pulled down a new version. I now get a failure when trying to compile my project Exception in thread "main" Syntax error compiling at (malli/core.cljc:1164:1).

Dave Simmons05:09:40

I pulled down an earlier version of my project but in case I'd done something daft but still get the same problem. Has anyone else experienced this issue? cheers.


@shortlyportly i can reproduce. the new dynaload works differently (better!), will fix that.

Dave Simmons06:09:33

@ikitommi - awesome - many thanks.


@shortlyportly should be fixed in master & [metosin/malli "0.0.1-20200924.063109-27"], ping @sergey.tkachenko too.


also, sci is now loaded when it’s first used. Using preload (cljs) or direct require (cljs, graalvm, jvm) makes it eager. But: having sci on classpath and not needing it, means it wont get loaded by malli, yielding faster startup time:

(time (require '[malli.core :as m]))
"Elapsed time: 466.76624 msecs"
=> nil
(time (m/eval "(+ 1 1)"))
"Elapsed time: 1591.515317 msecs"
=> 2
(time (m/eval "(+ 1 1)"))
"Elapsed time: 0.587728 msecs"
=> 2
(time (m/eval "(+ 1 1)"))
"Elapsed time: 0.772357 msecs"
=> 2


when clj command line tooling 3rd party library aot caching works, this can be made eager again.

Dave Simmons07:09:20

@ikitommi - thank you - seems to be back and working. cheers


Next change, m/fn-schema went away? How should I change the below....

(def registry
  (merge (m/predicate-schemas)
         {:zoned-date-time (m/fn-schema :zoned-date-time #'zoned-date-time?)
          :local-date      (m/fn-schema :local-date      #'local-date?)}))


@dcj there were 4 variants of the same thing, fn-schema, leaf-schema, predicate-schema, partial-predicate-schena , all replaced by m/-simple-schema. Also, there is way to reset the default registry now. But the schema:

(m/-simple-schema {:type :local-date, :pred #'local-date?})


it also give you a way to read the schema properties and use them for the validation or transformation:

(def Date
    (fn [{:keys [format type min]} children]
      ;; check props and children here, at schema instance creation time
      (let [string->date (create-formatter-somehow format)
            min-date (string->date min]
        {:type :local-date
         :pred (fn [x] (and (instance? type x) (check-that-is-greater-than min))
         :type-properties {:decode/string string->date
                           :error/message {:en "should be date"}
                           :gen/gen generator-for-date}})))

(def LocalDateThisYear
  [Date {:format "YYYY-MM-DD", :min "2020-01-01", :type java.time.LocalDate}])

(m/validate LocalDateThisYear #time/local-date "2020-12-12") ; => true

(m/decode LocalDateThisYear "2020-12-12" mt/string-decoder) ; => #time/local-date "2020-12-12"


(def registry
  (merge (m/-predicate-schemas)
         (m/-type-schema) ;; <--- new too
         {:zoned-date-time (m/-simple-schema {:type :zoned-date-time, :pred #'zoned-date-time?})
          :local-date      (m/-simple-schema {:type :local-date,      :pred #'local-date?})}))


@ikitommi: Thank you for all this help/context/insight!


added tests and merged the lazy registries & lazy multi. Here’s the final api:

(def registry
    (fn [type registry]
      ;; simulates pulling CloudFormation Schemas when needed
      (let [lookup {"AWS::ApiGateway::UsagePlan" [:map {:closed true}
                                                  [:Type [:= "AWS::ApiGateway::UsagePlan"]]
                                                  [:Description {:optional true} string?]
                                                  [:UsagePlanName {:optional true} string?]]
                    "AWS::AppSync::ApiKey" [:map {:closed true}
                                            [:Type [:= "AWS::AppSync::ApiKey"]]
                                            [:ApiId string?]
                                            [:Description {:optional true} string?]]}]
        (println "... loaded" type)
        (some-> type lookup (m/schema {:registry registry}))))))

;; lazy multi, doesn't realize the schemas
(def CloudFormation
    [:multi {:dispatch :Type, :lazy-refs true}
    {:registry registry}))

  {:Type "AWS::ApiGateway::UsagePlan"
   :Description "laiskanlinna"})
; ... loaded AWS::ApiGateway::UsagePlan
; => true

  {:Type "AWS::ApiGateway::UsagePlan"
   :Description "laiskanlinna"})
; => true

❤️ 6

has also the :multi key spell-checker:

(deftest multi-error-test
  (let [schema [:multi {:dispatch :type}
                ["plus" [:map [:value int?]]]
                ["minus" [:map [:value int?]]]]]

    (is (= {:type ["invalid dispatch value"]}
           (-> schema
               (m/explain {:type "minuz"})

    (is (= {:type ["did you mean minus"]}
           (-> schema
               (m/explain {:type "minuz"})