### Install Locally Source: https://github.com/metosin/malli/blob/master/README.md Commands to build and install the project locally. ```bash clj -Mjar clj -Minstall ``` -------------------------------- ### Install and Watch ClojureScript Instrumentation Source: https://github.com/metosin/malli/blob/master/docs/cljs-instrument-development.md Commands to install dependencies and start the shadow-cljs watch process for instrumentation. ```bash npm i ./node_modules/.bin/shadow-cljs watch instrument ``` -------------------------------- ### Run Tests Source: https://github.com/metosin/malli/blob/master/README.md Commands to install dependencies and execute the test suite. ```bash npm install ./bin/kaocha ./bin/node ``` -------------------------------- ### Example: Calculated Default Value for Cost Source: https://github.com/metosin/malli/blob/master/docs/tips.md Shows how to calculate the `:cost` field based on `:qty` and `:price` using a function provided to `default-fn-value-transformer`. This example also includes a standard `mt/default-value-transformer` for comparison. ```clojure (def Purchase [:map [:qty {:default 1} number?] [:price {:optional true} number?] [:cost {:default-fn '(fn [m] (* (:qty m) (:price m)))} number?]]) (def decode-autonomous-vals (m/decoder Purchase (mt/transformer (mt/string-transformer) (mt/default-value-transformer)))) (def decode-interconnected-vals (m/decoder Purchase (default-fn-value-transformer))) (-> {:qty "100" :price "1.2"} decode-autonomous-vals decode-interconnected-vals) ;; => {:price 1.2, :qty 100.0, :cost 120.0} (-> {:price "1.2"} decode-autonomous-vals decode-interconnected-vals) ;; => {:qty 1, :price 1.2, :cost 1.2} (-> {:prie "1.2"} decode-autonomous-vals decode-interconnected-vals) ;; => {:prie "1.2", :qty 1} ``` -------------------------------- ### Configure pretty error reporting Source: https://github.com/metosin/malli/blob/master/docs/function-schemas.md Examples of configuring the reporter, thrower, and custom printer settings for runtime errors. ```clojure (defn plus1 [x] (inc x)) (m/=> plus1 [:=> [:cat :int] [:int {:max 6}]]) (dev/start! {:report (pretty/reporter)}) (plus1 "2") ; =stdout=> ; -- Schema Error ----------------------------------- malli.demo:13 -- ; ; Invalid function arguments: ; ; ["2"] ; ; Input Schema: ; ; [:cat :int] ; ; Errors: ; ; {:in [0], ; :message "should be an integer", ; :path [0], ; :schema :int, ; :type nil, ; :value "2"} ; ; More information: ; ; https://cljdoc.org/d/metosin/malli/LATEST/doc/function-schemas ; ; -------------------------------------------------------------------- ; =throws=> Execution error (ClassCastException) at malli.demo/plus1 (demo.cljc:13). ; java.lang.String cannot be cast to java.lang.Number ``` ```clojure (dev/start! {:report (pretty/thrower)}) ``` ```clojure (dev/start! {:report (pretty/reporter (pretty/-printer {:width 80 :print-length 30 :print-level 2 :print-meta true}))}) ``` -------------------------------- ### Vector Syntax Usage Example Source: https://github.com/metosin/malli/blob/master/README.md Demonstrates creating a schema for a non-empty string using vector syntax and validating it. Requires importing `malli.core`. ```clojure (require '[malli.core :as m]) (def non-empty-string (m/schema [:string {:min 1}])) (m/schema? non-empty-string) ;; => true (m/validate non-empty-string "") ;; => false (m/validate non-empty-string "kikka") ;; => true (m/form non-empty-string) ;; => [:string {:min 1}] ``` -------------------------------- ### Instrument functions with malli.dev Source: https://github.com/metosin/malli/blob/master/docs/function-schemas.md Demonstrates starting and stopping development instrumentation, including function schema definition and runtime validation. ```clojure (defn plus1 [x] (inc x)) (m/=> plus1 [:=> [:cat :int] [:int {:max 6}]]) (dev/start!) ; malli: instrumented 1 function var ; malli: dev-mode started (plus1 "6") ; =throws=> :malli.core/invalid-input {:input [:cat :int], :args ["6"], :schema [:=> [:cat :int] [:int {:max 6}]]} (plus1 6) ; =throws=> :malli.core/invalid-output {:output [:int {:max 6}], :value 9, :args [8], :schema [:=> [:cat :int] [:int {:max 6}]]} (m/=> plus1 [:=> [:cat :int] :int]) ; =stdout=> ..instrumented #'user/plus1 (plus 6) ; => 7 (dev/stop!) ; malli: unstrumented 1 function vars ; malli: dev-mode stopped ``` -------------------------------- ### Map Syntax Usage Example Source: https://github.com/metosin/malli/blob/master/README.md Demonstrates creating a schema for a non-empty string using map syntax (Schema AST) and validating it. Requires importing `malli.core`. ```clojure (def non-empty-string (m/from-ast {:type :string :properties {:min 1}})) (m/schema? non-empty-string) ;; => true (m/validate non-empty-string "") ;; => false (m/validate non-empty-string "kikka") ;; => true (m/ast non-empty-string) ;; => {:type :string ;; :properties {:min 1}} ``` -------------------------------- ### Define function schemas using :=> and :function Source: https://github.com/metosin/malli/blob/master/docs/function-schemas.md Examples of defining function signatures including arguments and return values. ```clojure ;; no args, no return [:=> :cat :nil] ;; int -> int [:=> [:cat :int] :int] ;; x:int, xs:int* -> int [:=> [:catn [:x :int] [:xs [:+ :int]]] :int] ;; arg:int -> ret:int, arg > ret (defn guard [[arg] ret] (> arg ret)) [:=> [:cat :int] :int [:fn guard]] ;; multi-arity function [:function [:=> [:cat :int] :int] [:=> [:cat :int :int [:* :int]] :int]] ``` -------------------------------- ### Add Generated Example Values to Schema Source: https://github.com/metosin/malli/blob/master/README.md Walks a schema and adds generated example values to properties using `mu/update-properties`. This is useful for documentation or testing purposes. ```clojure (m/walk [:map [:name :string] [:description :string] [:address [:map [:street :string] [:country [:enum "finland" "poland"]]]]] (m/schema-walker (fn [schema] (mu/update-properties schema assoc :examples (mg/sample schema {:size 2, :seed 20}))))) ``` -------------------------------- ### Vector Syntax Examples Source: https://github.com/metosin/malli/blob/master/README.md Illustrates the basic vector syntax for defining types, types with properties, and types with properties and children. Also shows function schema definition. ```clojure type [type & children] [type properties & children] ``` ```clojure ;; just a type (String) :string ;; type with properties [:string {:min 1, :max 10}] ;; type with properties and children [:tuple {:title "location"} :double :double] ;; a function schema of :int -> :int [:=> [:cat :int] :int] [:-> :int :int] ``` -------------------------------- ### Setup Global Registry for Spec-like Schemas Source: https://github.com/metosin/malli/blob/master/docs/reusable-schemas.md Configure a mutable global registry and a helper function to register schemas. This enables spec-like schema management. ```clojure (require '[malli.registry :as mr]) (defonce *registry (atom {})) (defn register! [type ?schema] (swap! *registry assoc type ?schema)) (mr/set-default-registry! (mr/composite-registry (m/default-schemas) (mr/mutable-registry *registry))) ``` -------------------------------- ### Map Syntax Examples Source: https://github.com/metosin/malli/blob/master/README.md Shows the alternative map syntax for defining types, types with properties, and types with properties and children. Also includes function schema definitions. ```clojure ;; just a type (String) {:type :string} ;; type with properties {:type :string :properties {:min 1, :max 10}} ;; type with properties and children {:type :tuple :properties {:title "location"} :children [{:type :double} {:type :double}]} ;; a function schema of :int -> :int {:type :=> :input {:type :cat, :children [{:type :int}]} :output :int} {:type :-> :children [{:type :int} {:type :int}]} ``` -------------------------------- ### Install JS Timezone Packages Source: https://github.com/metosin/malli/blob/master/README.md Installs the required npm packages for using time schemas in ClojureScript. `@js-joda/core` is for core functionality, and `@js-joda/timezone` is for timezone data. ```bash npm install @js-joda/core @js-joda/timezone ``` -------------------------------- ### Define a simple Clojure function Source: https://github.com/metosin/malli/blob/master/docs/function-schemas.md A basic example of a function definition and invocation. ```clojure (defn plus [x y] (+ x y)) (plus 1 2) ;; => 3 ``` -------------------------------- ### Applying Right-Distributive Rule with Concrete Schemas Source: https://github.com/metosin/malli/blob/master/README.md Example of dereferencing a schema that applies the right-distributive rule of :merge over :multi. ```clojure ;; right-distributive (m/deref [:merge [:multi {:dispatch :y} [1 [:map [:y [:= 1]]]] [2 [:map [:y [:= 2]]]]] [:map [:x :int]]] {:registry registry}) ``` -------------------------------- ### Applying Left-Distributive Rule with Concrete Schemas Source: https://github.com/metosin/malli/blob/master/README.md Example of dereferencing a schema that applies the left-distributive rule of :merge over :multi. ```clojure ;; left-distributive (m/deref [:merge [:map [:x :int]] [:multi {:dispatch :y} [1 [:map [:y [:= 1]]]] [2 [:map [:y [:= 2]]]]]] {:registry registry}) ``` -------------------------------- ### Create optimized parser with m/parser Source: https://github.com/metosin/malli/blob/master/README.md Use `m/parser` to create an optimized parser from a schema for repeated use. The example defines a Hiccup schema and demonstrates parsing a nested structure. ```clojure (def Hiccup [:schema {:registry {"hiccup" [:orn [:node [:catn [:name keyword?] [:props [:? [:map-of keyword? any?]]] [:children [:* [:schema [:ref "hiccup"]]]]] [:primitive [:orn [:nil nil?] [:boolean boolean?] [:number number?] [:text string?]]]]}} "hiccup"]) (def parse-hiccup (m/parser Hiccup)) (parse-hiccup [:div {:class [:foo :bar]} [:p "Hello, world of data"]]) ;; => #malli.core.Tag ;; {:key :node, ;; :value ;; #malli.core.Tags ;; {:values {:name :div, ;; :props {:class [:foo :bar]}, ;; :children [#malli.core.Tag ;; {:key :node, ;; :value ;; #malli.core.Tags ;; {:values {:name :p, ;; :props nil, ;; :children [#malli.core.Tag ;; {:key :primitive, ;; :value ;; #malli.core.Tag ;; {:key :text, ;; :value "Hello, world of data"}}]}}}]}}} ``` -------------------------------- ### Custom JSON Schema Transformation with :json-schema Properties Source: https://github.com/metosin/malli/blob/master/README.md Allows customization of JSON Schema output by adding `:json-schema/` namespaced properties to Malli schema definitions. This example shows how to set type, description, and default values. ```clojure (json-schema/transform [:enum {:title "Fish" :description "It\'s a fish" :json-schema/type "string" :json-schema/default "perch"} "perch" "pike"]) ``` -------------------------------- ### Build Project with Tools Source: https://github.com/metosin/malli/blob/master/docs/jmh.md Run this command to build all necessary components of the project using the tools build. ```sh clj -T:build all ``` -------------------------------- ### Configure shadow-cljs entry and init Source: https://github.com/metosin/malli/blob/master/docs/clojurescript-function-instrumentation.md Define the application entry namespace and initialization function in your shadow-cljs.edn configuration. ```clojure {... :modules {:app {:entries [your-app.entry-ns] :init-fn your-app.entry-ns/init}} ...} ``` -------------------------------- ### Handle invalid schema lookups Source: https://github.com/metosin/malli/blob/master/README.md Example of an error thrown when a schema is not found in the registry. ```clojure (m/validate pos-int? 123 {:registry registry}) ; Syntax error (ExceptionInfo) compiling ; :malli.core/invalid-schema {:schema pos-int?} ``` -------------------------------- ### Define inlined schema references Source: https://github.com/metosin/malli/blob/master/docs/tips.md Example of defining a schema with inlined reference keys. ```clojure (def User [:map [::id :int] [:name :string] [::country {:optional true} :string]]) ``` -------------------------------- ### Handle global registry validation errors Source: https://github.com/metosin/malli/blob/master/README.md Example of an error thrown when a schema is missing after modifying the global registry. ```clojure (m/validate :int 42) ; =throws=> :malli.core/invalid-schema {:schema :int} ``` -------------------------------- ### Generate production build Source: https://github.com/metosin/malli/blob/master/README.md Use shadow-cljs to create a release build with pseudo-names enabled. ```bash npx shadow-cljs release app --pseudo-names ``` -------------------------------- ### Use schema registries Source: https://github.com/metosin/malli/blob/master/README.md Demonstrates using the default registry and passing an explicit registry via options. ```clojure ;; the default registry (m/validate [:maybe :string] "kikka") ;; => true ;; registry as explicit options (m/validate [:maybe :string] "kikka" {:registry m/default-registry}) ;; => true ``` -------------------------------- ### Test native image with sci Source: https://github.com/metosin/malli/blob/master/README.md Execute the native binary for a demo including the sci interpreter. ```bash ./bin/native-image demosci ./demosci '[:fn (fn [x] (and (int? x) (> x 10)))]]' '12' ``` -------------------------------- ### Generate Bundle Size Reports Source: https://github.com/metosin/malli/blob/master/README.md Commands to generate bundle size reports for different configurations. ```bash # no sci npx shadow-cljs run shadow.cljs.build-report app /tmp/report.html # with sci npx shadow-cljs run shadow.cljs.build-report app-sci /tmp/report.html # with cherry npx shadow-cljs run shadow.cljs.build-report app-cherry /tmp/report.html ``` ```bash # no sci npx shadow-cljs run shadow.cljs.build-report app2 /tmp/report.html # with sci npx shadow-cljs run shadow.cljs.build-report app2-sci /tmp/report.html # with cherry npx shadow-cljs run shadow.cljs.build-report app2-cherry /tmp/report.html ``` -------------------------------- ### Get Nested Schema Value Source: https://github.com/metosin/malli/blob/master/README.md Retrieves a nested value from a schema using a key path. Useful for inspecting complex schema structures. ```clojure (mu/get-in Address [:address :lonlat]) ; => [:tuple :double :double] ``` -------------------------------- ### Create a development preload namespace Source: https://github.com/metosin/malli/blob/master/docs/clojurescript-function-instrumentation.md Define a namespace with :dev/always metadata to initialize Malli instrumentation, ensuring the entry namespace is required. ```clojure (ns com.myapp.dev-preload {:dev/always true} (:require your-app.entry-ns ; <---- make sure you include your entry namespace [malli.dev.cljs :as dev])) (dev/start!) ``` -------------------------------- ### Apply Default Values Using Custom Function Source: https://github.com/metosin/malli/blob/master/README.md Use `mt/default-value-transformer` with a `default-fn` to dynamically generate default values, for example, by reading system properties. ```clojure (m/decode [:map [:os [:string {:property "os.name"}]] [:timezone [:string {:property "user.timezone"}]]] {} (mt/default-value-transformer {:key :property :default-fn (fn [_ x] (System/getProperty x))})) ; => {:os "Mac OS X", :timezone "Europe/Helsinki"} ``` -------------------------------- ### Lite Syntax with Custom Registry Source: https://github.com/metosin/malli/blob/master/README.md Configure `malli.experimental.lite/schema` with custom options, such as a registry, by binding the `l/*options*` Var. This allows for using custom schemas like `:user/id`. ```clojure (binding [l/*options* {:registry (merge (m/default-schemas) {:user/id :int})}] (l/schema {:id (l/maybe :user/id) :child {:id :user/id}})) ``` -------------------------------- ### Define Schema with Properties Source: https://github.com/metosin/malli/blob/master/README.md Shows how to define a schema with metadata properties like `:title`, `:description`, and `:json-schema/example` using a map. ```clojure (def Age [:and {:title "Age" :description "It's an age" :json-schema/example 20} :int [:> 18]]) (m/properties Age) ``` -------------------------------- ### Configure shadow-cljs for release registry switching Source: https://github.com/metosin/malli/blob/master/docs/clojurescript-function-instrumentation.md Uses :ns-aliases to swap Malli registry namespaces between development and release builds without changing source code. ```clojure {:target :browser :output-dir "resources/public/js/main" :asset-path "js/main" :dev {:modules {:main {:init-fn com.my-org.client.dev-entry/init}}} :release {:modules {:main {:init-fn com.my-org.client.release-entry/init}} :build-options {:ns-aliases {com.my-org.client.malli-registry com.my-org.client.malli-registry-release}}} :closure-defines {malli.registry/type "custom"} ,,, ``` -------------------------------- ### Explain Flat Arrow Function Schema with Custom Checker Source: https://github.com/metosin/malli/blob/master/docs/function-schemas.md Illustrates how `m/explain` works with a flat arrow schema (`:->`) and a custom function checker. The explanation output shows the schema, the value being checked, and detailed errors, including path, schema, and checker results. ```clojure (m/explain [:-> :int :int] (fn [x] (str x)) {::m/function-checker mg/function-checker}) ;{:schema [:-> :int :int], ; :value #object[...], ; :errors ({:path [:malli.core/in], ; :in [], ; :schema [:=> [:cat :int] :int], ; :value #object[...], ; :check {:total-nodes-visited 0, ; :result false, ; :result-data nil, ; :smallest [(0)], ; :time-shrinking-ms 0, ; :pass? false, ; :depth 0, ; :malli.core/result "0"} ; {:path [:malli.core/in 1] ; :in [], :schema :int ; :value "0"})} ``` -------------------------------- ### Sample values Source: https://github.com/metosin/malli/blob/master/README.md Generates a sequence of samples from a schema. ```clojure ;; sampling (mg/sample [:and :int [:> 10] [:< 100]] {:seed 123}) ; => (25 39 51 13 53 43 57 15 26 27) ``` -------------------------------- ### Example: Default Value from Primary Field Source: https://github.com/metosin/malli/blob/master/docs/tips.md Demonstrates using the custom `default-fn-value-transformer` to set the `:secondary` field's value to the `:primary` field's value if `:secondary` is missing. ```clojure (m/decode [:map [:primary :string] [:secondary {:default-fn '#(:primary %)} :string]] {:primary "blue"} (default-fn-value-transformer)) ``` -------------------------------- ### Custom Swagger2 Schema Transformation with :swagger and :json-schema Properties Source: https://github.com/metosin/malli/blob/master/README.md Enables customization of Swagger2 schema output using `:swagger/` and `:json-schema/` namespaced properties. This example sets the Swagger type and a JSON Schema default value. ```clojure (swagger/transform [:enum {:title "Fish" :description "It\'s a fish" :swagger/type "string" :json-schema/default "perch"} "perch" "pike"]) ``` -------------------------------- ### Define Singleton Schemas with nil Properties Source: https://github.com/metosin/malli/blob/master/README.md Explains and demonstrates the syntax for defining singleton schemas of `{}`, and `nil` when no schema properties are intended, using `[:enum nil {}]` and `[:enum nil nil]`. ```clojure [:enum nil {}] ``` ```clojure [:enum nil nil] ``` -------------------------------- ### Use Local Registry via Options Source: https://github.com/metosin/malli/blob/master/docs/reusable-schemas.md Provide a local registry as an option to `m/schema`. This merges the local registry with default schemas. ```clojure (m/schema ::user {:registry (merge (m/default-schemas) registry)}) ``` -------------------------------- ### Define Flat Arrow Function Schemas Source: https://github.com/metosin/malli/blob/master/docs/function-schemas.md Use the flat arrow schema `:->` for defining function schemas, which simplifies the input schema definition compared to `:=>` with `:cat`. This example shows `:->` for no-args/no-return, int-to-int, and multi-arity functions, including a guard. ```clojure ;; no args, no return [:-> :nil] ;; int -> int [:-> :int :int] ;; arg:int -> ret:int, arg > ret (defn guard [arg] ;; Corrected signature to match usage (> arg ret)) [:-> {:guard guard} :int :int] ;; multi-arity function [:function [:-> :int :int] [:-> :int :int [:* :int] :int]] ``` -------------------------------- ### Compare recursive schema generator behavior Source: https://github.com/metosin/malli/blob/master/README.md Illustrates best practices for using :ref to ensure proper generator behavior. ```clojure ;; Note: [:schema {:registry {::cons [:maybe [:tuple pos-int? [:ref ::cons]]]}} ::cons] ;; produces the same generator as the "unfolded" [:maybe [:tuple pos-int? [:schema {:registry {::cons [:maybe [:tuple pos-int? [:ref ::cons]]]}} ::cons]]] ;; while [:schema {:registry {::cons [:maybe [:tuple pos-int? [:ref ::cons]]]}} [:ref ::cons]] ;; has a direct correspondance to the following generator: (gen/recursive-gen (fn [rec] (gen/one-of [(gen/return nil) (gen/tuple rec)])) (gen/return nil)) ``` -------------------------------- ### Instrument Multi-Arity Function with Scopes and Custom Reporting Source: https://github.com/metosin/malli/blob/master/docs/function-schemas.md Instrument a multi-arity function with specified scopes (`:input`, `:output`) and a custom reporting function (`println`). This example shows how errors are reported to `println` and the function still returns its value. ```clojure (def multi-arity-pow (m/-instrument {:schema [:function [:=> [:cat :int] [:int {:max 6}]] [:=> [:cat :int :int] [:int {:max 6}]]] :scope #{:input :output} :report println} (fn ([x] (* x x)) ([x y] (* x y))))) (multi-arity-pow 4) ;; =stdout=> :malli.core/invalid-output {:output [:int {:max 6}], :value 16, :args [4], :schema [:=> [:cat :int] [:int {:max 6}]]} ;; => 16 (multi-arity-pow 5 0.1) ;; =stdout=> :malli.core/invalid-input {:input [:cat :int :int], :args [5 0.1], :schema [:=> [:cat :int :int] [:int {:max 6}]]} ;; :malli.core/invalid-output {:output [:int {:max 6}], :value 0.5, :args [5 0.1], :schema [:=> [:cat :int :int] [:int {:max 6}]]} ;; => 0.5 ``` -------------------------------- ### Configure Babashka dependencies Source: https://github.com/metosin/malli/blob/master/README.md Add Malli to the bb.edn configuration file. ```clojure {:deps {metosin/malli {:mvn/version "0.9.0"}}} ``` -------------------------------- ### Define and Validate Multi-arity Function Schema Source: https://github.com/metosin/malli/blob/master/docs/function-schemas.md Define a schema for a multi-arity function with specific argument and return types for each arity. Use `m/validate` to check if a given function conforms to the schema. The example demonstrates validation of both a correct and an incorrect implementation. ```clojure ;; multi-arity fn with function checking always on (def =>my-fn (m/schema [:function {:registry {::small-int [:int {:min -100, :max 100}]}} [:=> [:cat ::small-int] :int] [:=> [:cat ::small-int ::small-int [:* ::small-int]] :int]] {::m/function-checker mg/function-checker})) (m/validate =>my-fn (fn ([x] x) ([x y & z] (apply - (- x y) z)))) ; => true (m/validate =>my-fn (fn ([x] x) ([x y & z] (str x y z)))) ; => false (m/explain =>my-fn (fn ([x] x) ([x y & z] (str x y z)))) ;{:schema [:function {:registry {::small-int [:int {:min -100, :max 100}]}} [:=> [:cat ::small-int] :int] [:=> [:cat ::small-int ::small-int [:* ::small-int]] :int]], :value #object[malli.core_test$eval27255$fn__27256], :errors ({:path [], :in [], :schema [:function {:registry {::small-int [:int {:min -100, :max 100}]}} [:=> [:cat ::small-int] :int] [:=> [:cat ::small-int ::small-int [:* ::small-int]] :int]], :value #object[malli.core_test$eval27255$fn__27256], :check ({:total-nodes-visited 2, :depth 1, :pass? false, :result false, :result-data nil, :time-shrinking-ms 0, :smallest [(0 0)], :malli.generator/explain-output {:schema :int, :value "00", :errors ({:path [] :in [] :schema :int :value "00"})})})} ``` -------------------------------- ### Explain Sequence Errors with :catn and :altn Source: https://github.com/metosin/malli/blob/master/README.md Demonstrates how :catn and :altn provide named paths in error explanations for complex nested sequences. ```clojure (m/explain [:* [:catn [:prop :string] [:val [:altn [:s :string] [:b :boolean]]]]] ["-server" "foo" "-verbose" 11 "-user" "joe"]) ;; => {:schema [:* [:catn [:prop :string] [:val [:altn [:s :string] [:b :boolean]]]]], ;; :value ["-server" "foo" "-verbose" 11 "-user" "joe"], ;; :errors ({:path [0 :val :s], :in [3], :schema :string, :value 11} ;; {:path [0 :val :b], :in [3], :schema :boolean, :value 11})} ``` -------------------------------- ### Generate and Call Multi-arity Functions Source: https://github.com/metosin/malli/blob/master/docs/function-schemas.md Generate a function instance based on a multi-arity schema using `mg/generate`. The generated function will throw an error if called with an incorrect number of arguments, but will execute correctly with valid arguments. Examples show calls with invalid and valid argument counts. ```clojure (def my-fn-gen (mg/generate =>my-fn)) (my-fn-gen) ; =throws=> :malli.core/invalid-arity {:arity 0, :arities #{1 :varargs}, :args nil, :input nil, :schema [:function {:registry {::small-int [:int {:min -100, :max 100}]}} [:=> [:cat ::small-int] :int] [:=> [:cat ::small-int ::small-int [:* ::small-int]] :int]]} (my-fn-gen 1) ; => -3237 (my-fn-gen 1 2) ; => --543 (my-fn-gen 1 2 3 4) ; => -2326 ``` -------------------------------- ### Instrument Function with Generator for Return Value Source: https://github.com/metosin/malli/blob/master/docs/function-schemas.md Use the `:gen` option with `m/-instrument` to provide a generator function (e.g., `mg/generate`) for producing return values when the function body is omitted. This example instruments a multi-arity function using `mg/generate` and demonstrates calls that result in generated values and an arity error. ```clojure (def pow-gen (m/-instrument {:schema [:function [:=> [:cat :int] [:int {:max 6}]] [:=> [:cat :int :int] [:int {:max 6}]]] :gen mg/generate})) (pow-gen 10) ; => -253 (pow-gen 10 20) ; => -159 (pow-gen 10 20 30) ; =throws=> :malli.core/invalid-arity {:arity 3, :arities #{1 2}, :args (10 20 30), :input nil, :schema [:function [:=> [:cat :int] [:int {:max 6}]] [:=> [:cat :int :int] [:int {:max 6}]]]} ``` -------------------------------- ### Define Schema with Lite Syntax Source: https://github.com/metosin/malli/blob/master/README.md Use `malli.experimental.lite/schema` for a more concise way to define Malli schemas, similar to data-specs. This syntax is experimental and built for reitit. ```clojure (require '[malli.experimental.lite :as l]) (l/schema {:map1 {:x :int :y [:maybe :string] :z (l/maybe :keyword)} :map2 {:min-max [:int {:min 0 :max 10}] :tuples (l/vector (l/tuple :int :string)) :optional (l/optional (l/maybe :boolean)) :set-of-maps (l/set {:e :int :f :string}) :map-of-int (l/map-of :int {:s :string})}}) ``` -------------------------------- ### Instrument Function with Input and Return Constraints Source: https://github.com/metosin/malli/blob/master/docs/function-schemas.md Use `m/-instrument` to add runtime validation for function arguments and return values. This example defines a `pow` function with integer input and an integer output capped at 6. It demonstrates valid calls and various error cases like invalid input, output, and arity. ```clojure (def pow (m/-instrument {:schema [:=> [:cat :int] [:int {:max 6}]]} (fn [x] (* x x)))) (pow 2) ; => 4 (pow "2") ; =throws=> :malli.core/invalid-input {:input [:cat :int], :args ["2"], :schema [:=> [:cat :int] [:int {:max 6}]]} (pow 4) ; =throws=> :malli.core/invalid-output {:output [:int {:max 6}], :value 16, :args [4], :schema [:=> [:cat :int] [:int {:max 6}]]} (pow 4 2) ; =throws=> :malli.core/invalid-arity {:arity 2, :arities #{{:min 1, :max 1}}, :args [4 2], :input [:cat :int], :schema [:=> [:cat :int] [:int {:max 6}]]}) ``` -------------------------------- ### Sequence Alternatives with :alt and :altn Source: https://github.com/metosin/malli/blob/master/README.md Use :alt for simple alternatives in a sequence, and :altn to name alternatives for clearer pathing in errors. ```clojure (m/validate [:alt :keyword :string] ["foo"]) ;; => true ``` ```clojure (m/validate [:altn [:kw :keyword] [:s :string]] ["foo"]) ;; => true ``` -------------------------------- ### Generate :and schemas Source: https://github.com/metosin/malli/blob/master/README.md Explains how to order children in :and schemas to ensure successful generation and how to use :gen/fmap for complex types. ```clojure ;; BAD: :string is unlikely to generate values satisfying the schema (mg/generate [:and :string [:enum "a" "b" "c"]] {:seed 42}) ; Execution error ; Couldn't satisfy such-that predicate after 100 tries. ;; GOOD: every value generated by the `:enum` is a string (mg/generate [:and [:enum "a" "b" "c"] :string] {:seed 42}) ; => "a" ;; generate a non-empty vector starting with a keyword (mg/generate [:and [:cat {:gen/fmap vec} :keyword [:* :any]] vector?] {:size 1 :seed 2}) ;=> [:.+ [1]] ``` -------------------------------- ### Format Code Source: https://github.com/metosin/malli/blob/master/README.md Commands to format and clean namespaces using clojure-lsp. ```bash clojure-lsp format clojure-lsp clean-ns ``` -------------------------------- ### Define Singleton Schemas with Properties Source: https://github.com/metosin/malli/blob/master/README.md Illustrates the syntax for defining singleton schemas of `{}`, and `nil` when schema properties are present, using `[:enum {:foo :bar} {}]` and `[:enum {:foo :bar} nil]`. ```clojure [:enum {:foo :bar} {}] ``` ```clojure [:enum {:foo :bar} nil] ``` -------------------------------- ### Create Vector Schemas from Seqex with :and Source: https://github.com/metosin/malli/blob/master/README.md Combine sequence expressions with `vector?` using :and to create schemas that validate specific vector structures. ```clojure ;; non-empty vector starting with a keyword (m/validate [:and [:cat :keyword [:* :any]] vector?] [:a 1]) ;; => true (m/validate [:and [:cat :keyword [:* :any]] vector?] (:a 1)) ;; => false ``` -------------------------------- ### Validate Integer with Schema Instances and Vector Syntax Source: https://github.com/metosin/malli/blob/master/README.md Demonstrates validating an integer using both explicit schema instances and Malli's concise vector syntax. Shows successful and failed validations. ```clojure (m/validate (m/schema :int) 1) ``` ```clojure (m/validate :int 1) ``` ```clojure (m/validate :int "1") ``` -------------------------------- ### Persisting and Reading Schemas with EDN Source: https://github.com/metosin/malli/blob/master/README.md Demonstrates writing a schema to an EDN string and then reading it back, including a schema with a function. ```clojure (require '[malli.edn :as edn]) (-> [:and [:map [:x :int] [:y :int]] [:fn '(fn [{:keys [x y]}] (> x y))]] (edn/write-string) (doto prn) ; => "[:and [:map [:x :int] [:y :int]] [:fn (fn [{:keys [x y]}] (> x y))]]" (edn/read-string) (doto (-> (m/validate {:x 0, :y 1}) prn)) ; => false (doto (-> (m/validate {:x 2, :y 1}) prn))) ; => true ``` -------------------------------- ### Generate values with Malli Source: https://github.com/metosin/malli/blob/master/README.md Demonstrates basic value generation, including the use of seeds, regex patterns, and custom generator directives like :gen/return, :gen/elements, and :gen/fmap. ```clojure (require '[malli.generator :as mg]) ;; random (mg/generate :keyword) ; => :? ;; using seed (mg/generate [:enum "a" "b" "c"] {:seed 42}) ;; => "a" ;; using seed and size (mg/generate pos-int? {:seed 10, :size 100}) ;; => 55740 ;; regexs work too (only clj and if [com.gfredericks/test.chuck "0.2.10"+] available) (mg/generate [:re #"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,63}$"] {:seed 42, :size 10}) ; => "CaR@MavCk70OHiX.yZ" ;; :gen/return (note, not validated) (mg/generate [:and {:gen/return 42} :int]) ; => 42 ;; :gen/elements (note, are not validated) (mg/generate [:and {:gen/elements ["kikka" "kukka" "kakka"]} :string] {:seed 10}) ; => "kikka" ;; :gen/fmap (mg/generate [:and {:gen/fmap (partial str "kikka_")} :string] {:seed 10, :size 10}) ;; => "kikka_WT3K0yax2" ;; portable :gen/fmap (requires `org.babashka/sci` dependency to work) (mg/generate [:and {:gen/fmap '(partial str "kikka_")} :string] {:seed 10, :size 10}) ;; => "kikka_nWT3K0ya7" ;; :gen/schema (mg/generate [:any {:gen/schema [:int {:min 10, :max 20}]}] {:seed 10}) ; => 19 ;; :gen/min & :gen/max for numbers and collections (mg/generate [:vector {:gen/min 4, :gen/max 4} :int] {:seed 1}) ; => [-8522515 -1433 -1 1] ;; :gen/min & gen/max works for :+ and :* as well (mg/generate [:+ {:gen/min 2 :gen/max 10} :int] {:seed 10}) ; => [-109024846 -2 25432] ;; When composing sequence schemas, the directives effect the definition they are ;; associated with, such that: (mg/generate [:* {:gen/min 2 :gen/max 3} ; 2 - 3 repetitions of [:cat [:+ {:gen/min 2 :gen/max 3} :int] ; 2 - 3 repetitions of int [:* {:gen/min 1 :gen/max 2} :string]]] ; followed by 1-2 repetitions of string {:seed 10}) ; => (-812 1283 "Q9beps1Yn3c3VP9" "4XHdn1mgudSlNpVyxOrQIiR5pd5ocs" 114 -14284153 "8SSR9033czAO05") ;; :gen/infinite? & :gen/NaN? for :double (mg/generate [:double {:gen/infinite? true, :gen/NaN? true}] {:seed 1}) ; => ##Inf (require '[clojure.test.check.generators :as gen]) ;; gen/gen (note, not serializable) (mg/generate [:sequential {:gen/gen (gen/list gen/neg-int)} :int] {:size 42, :seed 42}) ; => (-37 -13 -13 -24 -20 -11 -34 -40 -22 0 -10) ``` -------------------------------- ### Connect to ClojureScript REPL Source: https://github.com/metosin/malli/blob/master/docs/cljs-instrument-development.md Command to evaluate in the editor to connect to the instrumented REPL. ```clojure (shadow/repl :instrument) ``` -------------------------------- ### Describe Schemas in English Source: https://github.com/metosin/malli/blob/master/README.md Use the describe function to generate human-readable descriptions of Malli schemas. ```clojure (require '[malli.experimental.describe :as med]) (med/describe [:map {:closed true} [:x {:optional true} int?] [:y :boolean]]) ;; => "map where {:x (optional) -> , :y -> } with no other keys" ``` -------------------------------- ### Register preload in shadow-cljs Source: https://github.com/metosin/malli/blob/master/docs/clojurescript-function-instrumentation.md Add the development preload namespace to the shadow-cljs configuration to enable instrumentation. ```clojure {... :modules {:app {:entries [your-app.entry-ns] :preloads [com.myapp.dev-preload] :init-fn your-app.entry-ns/init}} ...} ``` -------------------------------- ### Test native image without sci Source: https://github.com/metosin/malli/blob/master/README.md Execute the native binary for a demo without the sci interpreter. ```bash ./bin/native-image demo ./demo '[:set :keyword]' '["kikka" "kukka"]' ``` -------------------------------- ### Configure Instrumentation with Filters Source: https://github.com/metosin/malli/blob/master/docs/function-schemas.md Configure instrumentation with options like `:filters`, `:scope`, and `:report`. Filters allow selective instrumentation of Vars based on namespace, specific Vars, or metadata. ```clojure (mi/instrument! {:filters [;; everything from user ns (mi/-filter-ns 'user) ;; ... and some vars (mi/-filter-var #{#'power}) ;; all other vars with :always-validate meta (mi/-filter-var #(-> % meta :always-validate))] ;; scope :scope #{:input :output} ;; just print :report println}) (power 6) ; =stdout=> :malli.core/invalid-output {:output [:int {:max 6}], :value 36, :args [6], :schema [:=> [:cat :int] [:int {:max 6}]]} ; => 36 ``` -------------------------------- ### Explain Sequence Errors with :cat and :alt Source: https://github.com/metosin/malli/blob/master/README.md Illustrates how :cat and :alt use numeric indices for paths in error explanations, which can be less intuitive than named alternatives. ```clojure (m/explain [:* [:cat :string [:alt :string :boolean]]] ["-server" "foo" "-verbose" 11 "-user" "joe"]) ;; => {:schema [:* [:cat :string [:alt :string :boolean]]], ;; :value ["-server" "foo" "-verbose" 11 "-user" "joe"], ;; :errors ({:path [0 1 0], :in [3], :schema :string, :value 11} ;; {:path [0 1 1], :in [3], :schema :boolean, :value 11})} ``` -------------------------------- ### Parsing with :and Schema Source: https://github.com/metosin/malli/blob/master/README.md Demonstrates how the :and schema parses a value using one of its conjuncts. The choice of parser is usually automatic. ```clojure (m/parse [:and [:orn [:left :int] [:right :int]] [:fn number?]] 1) ;; => #malli.core.Tag{:key :left, :value 1} ``` ```clojure (m/parse [:and [:fn number?] [:orn [:left :int] [:right :int]]] 1) ;; => #malli.core.Tag{:key :left, :value 1} ``` -------------------------------- ### Define Inline Schemas with mx/defn Source: https://github.com/metosin/malli/blob/master/docs/function-schemas.md Use experimental inline schema hints similar to Plumatic Schema. ```clojure (require '[malli.experimental :as mx]) (mx/defn times :- :int "x times y" [x :- :int, y :- small-int] (* x y)) ``` ```clojure (m/function-schemas) ;{user {plus1 {:schema [:=> [:cat :int] [:int {:max 6}]] ; :ns user ; :name plus1}, ; minus {:schema [:=> [:cat :int] [:int {:max 6}]] ; :ns user ; :name minus}, ; times {:schema [:=> [:cat :int [:int {:max 6}]] :int] ; :ns user ; :name times}}} ``` ```clojure (times 10 10) ;; => 100 ``` ```clojure (mi/instrument!) ; =stdout=> ..instrumented #'user/plus1 ; =stdout=> ..instrumented #'user/minus ; =stdout=> ..instrumented #'user/times (times 10 10) ; =throws=> :malli.core/invalid-input {:input [:cat :int [:int {:max 6}]], :args [10 10], :schema [:=> [:cat :int [:int {:max 6}]] :int]} ``` ```clojure (mx/defn ^:malli/always times :- :int "x times y" [x :- :int, y :- small-int] (* x y)) ``` ```clojure user=> (times 10 5) 50 user=> (times 10 10) Execution error (ExceptionInfo) at malli.core/-exception (core.cljc:138). :malli.core/invalid-input ``` -------------------------------- ### Define and Generate User Schema Source: https://github.com/metosin/malli/blob/master/README.md Defines a User schema with nested structures and generates a sample instance. Requires importing `malli.core` and `malli.generator`. ```clojure (require '[malli.core :as m]) (def UserId :string) (def Address [:map [:street :string] [:country [:enum "FI" "UA"]]]) (def User [:map [:id #'UserId] [:address #'Address] [:friends [:set {:gen/max 2} [:ref #'User]]]]) (require '[malli.generator :as mg]) (mg/generate User) ;{:id "AC", ; :address {:street "mf", :country "UA"}, ; :friends #{{:id "1dm", ; :address {:street "8", :country "UA"}, ; :friends #{}}}} (m/validate User *1) ; => true ``` -------------------------------- ### Enable clj-kondo static checking Source: https://github.com/metosin/malli/blob/master/docs/clojurescript-function-instrumentation.md Include the clj-kondo preload namespace in the shadow-cljs configuration to generate static analysis files. ```clojure {... :modules {:app {:entries [your-app.entry-ns] :preloads [com.myapp.dev-preload malli.dev.cljs-kondo-preload ;; <---- ] :init-fn your-app.entry-ns/init}} ...} ```