A library for data shape definition and validation. A Schema is just Clojure data, which can be used to document and validate Clojure functions and data.
For example,
(def FooBar {:foo Keyword :bar [Number]}) ;; a schema
(check FooBar {:foo :k :bar [1.0 2.0 3.0]}) ==> nil
representing successful validation, but the following all return helpful errors describing how the provided data fails to measure up to schema FooBar's standards.
(check FooBar {:bar [1.0 2.0 3.0]}) ==> {:foo missing-required-key}
(check FooBar {:foo 1 :bar [1.0 2.0 3.0]}) ==> {:foo (not (keyword? 1))}
(check FooBar {:foo :k :bar [1.0 2.0 3.0] :baz 1}) ==> {:baz disallowed-key}
Schema lets you describe your leaf values using the Any, Keyword, Symbol, Number, String, and Int definitions below, or (in Clojure) you can use arbitrary Java classes or primitive casts to describe simple values.
From there, you can build up schemas for complex types using Clojure syntax (map literals for maps, set literals for sets, vector literals for sequences, with details described below), plus helpers below that provide optional values, enumerations, arbitrary predicates, and more.
Assuming you (:require [schema.core :as s :include-macros true]), Schema also provides macros for defining records with schematized elements (s/defrecord), and named or anonymous functions (s/fn and s/defn) with schematized inputs and return values. In addition to producing better-documented records and functions, these macros allow you to retrieve the schema associated with the defined record or function. Moreover, functions include optional validation, which will throw an error if the inputs or outputs do not match the provided schemas:
(s/defrecord FooBar [foo :- Int bar :- String])
(s/defn quux :- Int [foobar :- Foobar mogrifier :- Number] (* mogrifier (+ (:foo foobar) (Long/parseLong (:bar foobar)))))
(quux (FooBar. 10 "5") 2) ==> 30
(fn-schema quux) ==> (=> Int (record user.FooBar {:foo Int, :bar java.lang.String}) java.lang.Number)
(s/with-fn-validation (quux (FooBar. 10.2 "5") 2)) ==> Input to quux does not match schema: [(named {:foo (not (integer? 10.2))} foobar) nil]
As you can see, the preferred syntax for providing type hints to schema's defrecord, fn, and defn macros is to follow each element, argument, or function name with a :- schema. Symbols without schemas default to a schema of Any. In Clojure, class (e.g., clojure.lang.String) and primitive schemas (long, double) are also propagated to tag metadata to ensure you get the type hinting and primitive behavior you ask for.
If you don't like this style, standard Clojure-style typehints are also supported:
(fn-schema (s/fn [^String x])) ==> (=> Any java.lang.String)
You can directly type hint a symbol as a class, primitive, or simple schema.
See the docstrings of defrecord, fn, and defn for more details about how to use these macros.
A library for data shape definition and validation. A Schema is just Clojure data, which can be used to document and validate Clojure functions and data. For example, (def FooBar {:foo Keyword :bar [Number]}) ;; a schema (check FooBar {:foo :k :bar [1.0 2.0 3.0]}) ==> nil representing successful validation, but the following all return helpful errors describing how the provided data fails to measure up to schema FooBar's standards. (check FooBar {:bar [1.0 2.0 3.0]}) ==> {:foo missing-required-key} (check FooBar {:foo 1 :bar [1.0 2.0 3.0]}) ==> {:foo (not (keyword? 1))} (check FooBar {:foo :k :bar [1.0 2.0 3.0] :baz 1}) ==> {:baz disallowed-key} Schema lets you describe your leaf values using the Any, Keyword, Symbol, Number, String, and Int definitions below, or (in Clojure) you can use arbitrary Java classes or primitive casts to describe simple values. From there, you can build up schemas for complex types using Clojure syntax (map literals for maps, set literals for sets, vector literals for sequences, with details described below), plus helpers below that provide optional values, enumerations, arbitrary predicates, and more. Assuming you (:require [schema.core :as s :include-macros true]), Schema also provides macros for defining records with schematized elements (s/defrecord), and named or anonymous functions (s/fn and s/defn) with schematized inputs and return values. In addition to producing better-documented records and functions, these macros allow you to retrieve the schema associated with the defined record or function. Moreover, functions include optional *validation*, which will throw an error if the inputs or outputs do not match the provided schemas: (s/defrecord FooBar [foo :- Int bar :- String]) (s/defn quux :- Int [foobar :- Foobar mogrifier :- Number] (* mogrifier (+ (:foo foobar) (Long/parseLong (:bar foobar))))) (quux (FooBar. 10 "5") 2) ==> 30 (fn-schema quux) ==> (=> Int (record user.FooBar {:foo Int, :bar java.lang.String}) java.lang.Number) (s/with-fn-validation (quux (FooBar. 10.2 "5") 2)) ==> Input to quux does not match schema: [(named {:foo (not (integer? 10.2))} foobar) nil] As you can see, the preferred syntax for providing type hints to schema's defrecord, fn, and defn macros is to follow each element, argument, or function name with a :- schema. Symbols without schemas default to a schema of Any. In Clojure, class (e.g., clojure.lang.String) and primitive schemas (long, double) are also propagated to tag metadata to ensure you get the type hinting and primitive behavior you ask for. If you don't like this style, standard Clojure-style typehints are also supported: (fn-schema (s/fn [^String x])) ==> (=> Any java.lang.String) You can directly type hint a symbol as a class, primitive, or simple schema. See the docstrings of defrecord, fn, and defn for more details about how to use these macros.
(=> output-schema & arg-schemas)
Convenience macro for defining function schemas with a single arity; like =>*, but there is no vector around the argument schemas for this arity.
Convenience macro for defining function schemas with a single arity; like =>*, but there is no vector around the argument schemas for this arity.
(=>* output-schema & arity-schema-specs)
Produce a function schema from an output schema and a list of arity input schema specs, each of which is a vector of argument schemas, ending with an optional '& more-schema' specification where more-schema must be a sequence schema.
Currently function schemas are purely descriptive; there is no validation except for functions defined directly by s/fn or s/defn
Produce a function schema from an output schema and a list of arity input schema specs, each of which is a vector of argument schemas, ending with an optional '& more-schema' specification where more-schema must be a sequence schema. Currently function schemas are purely descriptive; there is no validation except for functions defined directly by s/fn or s/defn
(both & schemas)
A value that must satisfy every schema in schemas.
A value that must satisfy every schema in schemas.
(check schema x)
Return nil if x matches schema; otherwise, returns a value that looks like the 'bad' parts of x with ValidationErrors at the leaves describing the failures.
Return nil if x matches schema; otherwise, returns a value that looks like the 'bad' parts of x with ValidationErrors at the leaves describing the failures.
(checker schema)
Compile an efficient checker for schema, which returns nil for valid values and error descriptions otherwise.
Compile an efficient checker for schema, which returns nil for valid values and error descriptions otherwise.
(conditional & preds-and-schemas)
Define a conditional schema. Takes args like cond, (conditional pred1 schema1 pred2 schema2 ...), and checks the first schema where pred is true on the value. Unlike cond, throws if the value does not match any condition. :else may be used as a final condition in the place of (constantly true). More efficient than either, since only one schema must be checked.
Define a conditional schema. Takes args like cond, (conditional pred1 schema1 pred2 schema2 ...), and checks the first schema where pred is true on the value. Unlike cond, throws if the value does not match any condition. :else may be used as a final condition in the place of (constantly true). More efficient than either, since only one schema must be checked.
(def & def-args)
Like def, but takes a schema on the var name (with the same format as the output schema of s/defn), requires an initial value, and asserts that the initial value matches the schema on the var name (regardless of the status of with-fn-validation). Due to limitations of add-watch!, cannot enforce validation of subsequent rebindings of var. Throws at compile-time for clj, and client-side load-time for cljs.
Example:
(s/def foo :- long "a long" 2)
Like def, but takes a schema on the var name (with the same format as the output schema of s/defn), requires an initial value, and asserts that the initial value matches the schema on the var name (regardless of the status of with-fn-validation). Due to limitations of add-watch!, cannot enforce validation of subsequent rebindings of var. Throws at compile-time for clj, and client-side load-time for cljs. Example: (s/def foo :- long "a long" 2)
(defmethod multifn dispatch-val & fn-tail)
Like clojure.core/defmethod, except that schema-style typehints can be given on the argument symbols and after the dispatch-val (for the return value).
See (doc s/defn) for details.
Examples:
(s/defmethod mymultifun :a-dispatch-value :- s/Num [x :- s/Int y :- s/Num] (* x y))
;; You can also use meta tags like ^:always-validate by placing them ;; before the multifunction name:
(s/defmethod ^:always-validate mymultifun :a-dispatch-value [x y] (* x y))
Like clojure.core/defmethod, except that schema-style typehints can be given on the argument symbols and after the dispatch-val (for the return value). See (doc s/defn) for details. Examples: (s/defmethod mymultifun :a-dispatch-value :- s/Num [x :- s/Int y :- s/Num] (* x y)) ;; You can also use meta tags like ^:always-validate by placing them ;; before the multifunction name: (s/defmethod ^:always-validate mymultifun :a-dispatch-value [x y] (* x y))
(defn & defn-args)
Like clojure.core/defn, except that schema-style typehints can be given on the argument symbols and on the function name (for the return value).
You can call s/fn-schema on the defined function to get its schema back, or use with-fn-validation to enable runtime checking of function inputs and outputs.
(s/defn foo :- s/Num [x :- s/Int y :- s/Num] (* x y))
(s/fn-schema foo) ==> (=> java.lang.Number Int java.lang.Number)
(s/with-fn-validation (foo 1 2)) ==> 2
(s/with-fn-validation (foo 1.5 2)) ==> Input to foo does not match schema: [(named (not (integer? 1.5)) x) nil]
See (doc schema.core) for details of the :- syntax for arguments and return schemas.
The overhead for checking if run-time validation should be used is very small -- about 5% of a very small fn call. On top of that, actual validation costs what it costs.
You can also turn on validation unconditionally for this fn only by putting ^:always-validate metadata on the fn name.
Gotchas and limitations:
The output schema always goes on the fn name, not the arg vector. This means that all arities must share the same output schema. Schema will automatically propagate primitive hints to the arg vector and class hints to the fn name, so that you get the behavior you expect from Clojure.
Schema metadata is only processed on top-level arguments. I.e., you can use destructuring, but you must put schema metadata on the top-level arguments, not the destructured variables.
Bad: (s/defn foo [{:keys [x :- s/Int]}]) Good: (s/defn foo [{:keys [x]} :- {:x s/Int}])
Only a specific subset of rest-arg destructuring is supported:
Unlike clojure.core/defn, a final attr-map on multi-arity functions is not supported.
Like clojure.core/defn, except that schema-style typehints can be given on the argument symbols and on the function name (for the return value). You can call s/fn-schema on the defined function to get its schema back, or use with-fn-validation to enable runtime checking of function inputs and outputs. (s/defn foo :- s/Num [x :- s/Int y :- s/Num] (* x y)) (s/fn-schema foo) ==> (=> java.lang.Number Int java.lang.Number) (s/with-fn-validation (foo 1 2)) ==> 2 (s/with-fn-validation (foo 1.5 2)) ==> Input to foo does not match schema: [(named (not (integer? 1.5)) x) nil] See (doc schema.core) for details of the :- syntax for arguments and return schemas. The overhead for checking if run-time validation should be used is very small -- about 5% of a very small fn call. On top of that, actual validation costs what it costs. You can also turn on validation unconditionally for this fn only by putting ^:always-validate metadata on the fn name. Gotchas and limitations: - The output schema always goes on the fn name, not the arg vector. This means that all arities must share the same output schema. Schema will automatically propagate primitive hints to the arg vector and class hints to the fn name, so that you get the behavior you expect from Clojure. - Schema metadata is only processed on top-level arguments. I.e., you can use destructuring, but you must put schema metadata on the top-level arguments, not the destructured variables. Bad: (s/defn foo [{:keys [x :- s/Int]}]) Good: (s/defn foo [{:keys [x]} :- {:x s/Int}]) - Only a specific subset of rest-arg destructuring is supported: - & rest works as expected - & [a b] works, with schemas for individual elements parsed out of the binding, or an overall schema on the vector - & {} is not supported. - Unlike clojure.core/defn, a final attr-map on multi-arity functions is not supported.
(defrecord name field-schema extra-key-schema? extra-validator-fn? & opts+specs)
Define a record with a schema.
In addition to the ordinary behavior of defrecord, this macro produces a schema for the Record, which will automatically be used when validating instances of the Record class:
(m/defrecord FooBar [foo :- Int bar :- String])
(schema.utils/class-schema FooBar) ==> (record user.FooBar {:foo Int, :bar java.lang.String})
(s/check FooBar (FooBar. 1.2 :not-a-string)) ==> {:foo (not (integer? 1.2)), :bar (not (instance? java.lang.String :not-a-string))}
See (doc schema.core) for details of the :- syntax for record elements.
Moreover, optional arguments extra-key-schema? and extra-validator-fn? can be passed to augment the record schema.
The remaining opts+specs (i.e., protocol and interface implementations) are passed through directly to defrecord.
Finally, this macro replaces Clojure's map->name constructor with one that is more than an order of magnitude faster (as of Clojure 1.5), and provides a new strict-map->name constructor that throws or drops extra keys not in the record base.
Define a record with a schema. In addition to the ordinary behavior of defrecord, this macro produces a schema for the Record, which will automatically be used when validating instances of the Record class: (m/defrecord FooBar [foo :- Int bar :- String]) (schema.utils/class-schema FooBar) ==> (record user.FooBar {:foo Int, :bar java.lang.String}) (s/check FooBar (FooBar. 1.2 :not-a-string)) ==> {:foo (not (integer? 1.2)), :bar (not (instance? java.lang.String :not-a-string))} See (doc schema.core) for details of the :- syntax for record elements. Moreover, optional arguments extra-key-schema? and extra-validator-fn? can be passed to augment the record schema. - extra-key-schema is a map schema that defines validation for additional key-value pairs not in the record base (the default is to not allow extra mappings). - extra-validator-fn? is an additional predicate that will be used as part of validating the record value. The remaining opts+specs (i.e., protocol and interface implementations) are passed through directly to defrecord. Finally, this macro replaces Clojure's map->name constructor with one that is more than an order of magnitude faster (as of Clojure 1.5), and provides a new strict-map->name constructor that throws or drops extra keys not in the record base.
(defrecord+ name
field-schema
extra-key-schema?
extra-validator-fn?
&
opts+specs)
Like defrecord, but emits a record using potemkin/defrecord+. You must provide your own dependency on potemkin to use this.
Like defrecord, but emits a record using potemkin/defrecord+. You must provide your own dependency on potemkin to use this.
(defschema name form)
(defschema name docstring form)
Convenience macro to make it clear to reader that body is meant to be used as a schema. The name of the schema is recorded in the metadata.
Convenience macro to make it clear to reader that body is meant to be used as a schema. The name of the schema is recorded in the metadata.
(either & schemas)
A value that must satisfy at least one schema in schemas.
A value that must satisfy at least one schema in schemas.
(enum & vs)
A value that must be = to some element of vs.
A value that must be = to some element of vs.
(fn & fn-args)
s/fn : s/defn :: clojure.core/fn : clojure.core/defn
See (doc s/defn) for details.
Additional gotchas and limitations:
s/fn : s/defn :: clojure.core/fn : clojure.core/defn See (doc s/defn) for details. Additional gotchas and limitations: - Like s/defn, the output schema must go on the fn name. If you don't supply a name, schema will gensym one for you and attach the schema. - Unlike s/defn, the function schema is stored in metadata on the fn. Clojure's implementation for metadata on fns currently produces a wrapper fn, which will decrease performance and negate the benefits of primitive type hints compared to clojure.core/fn.
(fn-schema f)
Produce the schema for a function defined with s/fn or s/defn.
Produce the schema for a function defined with s/fn or s/defn.
(fn-validation?)
Get the current global schema validation setting.
Get the current global schema validation setting.
(if pred if-schema else-schema)
if the predicate returns truthy, use the if-schema, otherwise use the else-schema
if the predicate returns truthy, use the if-schema, otherwise use the else-schema
The local representation of #inst ...
The local representation of #inst ...
(isa parent)
(isa h parent)
A value that must be a child of parent.
A value that must be a child of parent.
(letfn fnspecs & body)
s/letfn : s/fn :: clojure.core/letfn : clojure.core/fn
s/letfn : s/fn :: clojure.core/letfn : clojure.core/fn
(make-fn-schema output-schema input-schemas)
A function outputting a value in output schema, whose argument vector must match one of input-schemas, each of which should be a sequence schema. Currently function schemas are purely descriptive; they validate against any function, regardless of actual input and output types.
A function outputting a value in output schema, whose argument vector must match one of input-schemas, each of which should be a sequence schema. Currently function schemas are purely descriptive; they validate against any function, regardless of actual input and output types.
(maybe schema)
A value that must either be nil or satisfy schema
A value that must either be nil or satisfy schema
(named schema name)
A value that must satisfy schema, and has a name for documentation purposes.
A value that must satisfy schema, and has a name for documentation purposes.
(one schema name)
A single required element of a sequence (not repeated, the implicit default)
A single required element of a sequence (not repeated, the implicit default)
(optional schema name)
A single optional element of a sequence (not repeated, the implicit default)
A single optional element of a sequence (not repeated, the implicit default)
(optional-key k)
An optional key in a map
An optional key in a map
(pair first-schema first-name second-schema second-name)
A schema for a pair of schemas and their names
A schema for a pair of schemas and their names
(pred p?)
(pred p? pred-name)
A value for which p? returns true (and does not throw). Optional pred-name can be passed for nicer validation errors.
A value for which p? returns true (and does not throw). Optional pred-name can be passed for nicer validation errors.
(protocol p)
A value that must satsify? protocol p.
Internaly, we must make sure not to capture the value of the protocol at schema creation time, since that's impossible in cljs and breaks later extends in Clojure.
A macro for cljs sake, since satisfies?
is a macro in cljs.
A value that must satsify? protocol p. Internaly, we must make sure not to capture the value of the protocol at schema creation time, since that's impossible in cljs and breaks later extends in Clojure. A macro for cljs sake, since `satisfies?` is a macro in cljs.
(queue x)
Defines a schema satisfied by instances of clojure.lang.PersistentQueue (clj.core/PersistentQueue in ClojureScript) whose values satisfy x.
Defines a schema satisfied by instances of clojure.lang.PersistentQueue (clj.core/PersistentQueue in ClojureScript) whose values satisfy x.
(record klass schema)
A Record instance of type klass, whose elements match map schema 'schema'.
A Record instance of type klass, whose elements match map schema 'schema'.
(recursive schema)
Support for (mutually) recursive schemas by passing a var that points to a schema, e.g (recursive #'ExampleRecursiveSchema).
Support for (mutually) recursive schemas by passing a var that points to a schema, e.g (recursive #'ExampleRecursiveSchema).
(required-key k)
A required key in a map
A required key in a map
(explain this)
Expand this schema to a human-readable format suitable for pprinting, also expanding class schematas at the leaves. Example:
user> (s/explain {:a s/Keyword :b [s/Int]} ) {:a Keyword, :b [Int]}
Expand this schema to a human-readable format suitable for pprinting, also expanding class schematas at the leaves. Example: user> (s/explain {:a s/Keyword :b [s/Int]} ) {:a Keyword, :b [Int]}
(walker this)
Produce a function that takes [data], and either returns a walked version of data (by default, usually just data), or a utils/ErrorContainer containing value that looks like the 'bad' parts of data with ValidationErrors at the leaves describing the failures.
If this is a composite schema, should let-bind (subschema-walker sub-schema) for each subschema outside the returned fn. Within the returned fn, should break down data into constituents, call the let-bound subschema walkers on each component, and then reassemble the components into a walked version of the data (or an ErrorContainer describing the validaiton failures).
Attempting to walk a value that already contains a utils/ErrorContainer produces undefined behavior.
User code should never call walker
directly. Instead, it should call start-walker
below.
Produce a function that takes [data], and either returns a walked version of data (by default, usually just data), or a utils/ErrorContainer containing value that looks like the 'bad' parts of data with ValidationErrors at the leaves describing the failures. If this is a composite schema, should let-bind (subschema-walker sub-schema) for each subschema outside the returned fn. Within the returned fn, should break down data into constituents, call the let-bound subschema walkers on each component, and then reassemble the components into a walked version of the data (or an ErrorContainer describing the validaiton failures). Attempting to walk a value that already contains a utils/ErrorContainer produces undefined behavior. User code should never call `walker` directly. Instead, it should call `start-walker` below.
(schema-name schema)
Returns the name of a schema attached via schema-with-name (or defschema).
Returns the name of a schema attached via schema-with-name (or defschema).
(schema-ns schema)
Returns the namespace of a schema attached via defschema.
Returns the namespace of a schema attached via defschema.
(schema-with-name schema name)
Records name in schema's metadata.
Records name in schema's metadata.
(schematize-fn f schema)
Attach the schema to fn f at runtime, extractable by fn-schema.
Attach the schema to fn f at runtime, extractable by fn-schema.
(set-fn-validation! on?)
Globally turn on (or off) schema validation for all s/fn and s/defn instances.
Globally turn on (or off) schema validation for all s/fn and s/defn instances.
(set-max-value-length! max-length)
Sets the maximum length of value to be output before it is contracted to a prettier name.
Sets the maximum length of value to be output before it is contracted to a prettier name.
(start-walker sub-walker schema)
The entry point for creating walkers. Binds the provided walker to subschema-walker, then calls it on the provided schema. For simple validation, pass walker as sub-walker. More sophisticated behavior (coercion, etc), can be achieved by passing a sub-walker that wraps walker with additional behavior.
The entry point for creating walkers. Binds the provided walker to subschema-walker, then calls it on the provided schema. For simple validation, pass walker as sub-walker. More sophisticated behavior (coercion, etc), can be achieved by passing a sub-walker that wraps walker with additional behavior.
Satisfied only by String. Is (pred string?) and not js/String in cljs because of keywords.
Satisfied only by String. Is (pred string?) and not js/String in cljs because of keywords.
The function to call within 'walker' implementations to create walkers for subschemas. Can be dynamically bound (using start-walker below) to create different walking behaviors.
For the curious, implemented using dynamic binding rather than making walker take a subschema-walker as an argument because some behaviors (e.g. recursive schema walkers) seem to require mind-bending things like fixed-point combinators that way, but are simple this way.
The function to call within 'walker' implementations to create walkers for subschemas. Can be dynamically bound (using start-walker below) to create different walking behaviors. For the curious, implemented using dynamic binding rather than making walker take a subschema-walker as an argument because some behaviors (e.g. recursive schema walkers) seem to require mind-bending things like fixed-point combinators that way, but are simple this way.
The local representation of #uuid ...
The local representation of #uuid ...
(validate schema value)
Throw an exception if value does not satisfy schema; otherwise, return value.
Throw an exception if value does not satisfy schema; otherwise, return value.
(with-fn-validation & body)
Execute body with input and output schema validation turned on for all s/defn and s/fn instances globally (across all threads). After all forms have been executed, resets function validation to its previously set value. Not concurrency-safe.
Execute body with input and output schema validation turned on for all s/defn and s/fn instances globally (across all threads). After all forms have been executed, resets function validation to its previously set value. Not concurrency-safe.
(without-fn-validation & body)
Execute body with input and output schema validation turned off for all s/defn and s/fn instances globally (across all threads). After all forms have been executed, resets function validation to its previously set value. Not concurrency-safe.
Execute body with input and output schema validation turned off for all s/defn and s/fn instances globally (across all threads). After all forms have been executed, resets function validation to its previously set value. Not concurrency-safe.
cljdoc is a website building & hosting documentation for Clojure/Script libraries
× close