structural-typing.preds
A few predefined predicates, but also functions that take expected values and generate predicates.
all-of
(all-of & condensed-type-descriptions)
This is used with implies to group a collection of condensed-type-descriptions
into one.
(all-of (requires :x :y) (includes :Point) {:color string?})
at-most-keys
(at-most-keys & coll)
Produce a predicate that’s false when applied to a map or record with keys other than those given in coll
. Note: the value may be missing keys in coll
. See exactly-keys.
user=> (type! :X {:v1 string?
:v2 integer?}
(at-most-keys :v1 :v2))
user=> (built-like :X {:v1 "apple"})
=> {:v1 "apple"}
user=> (built-like :X {:v1 "apple" :v2 3})
=> {:v1 "apple", :v2 3}
user=> (built-like :X {:v1 "apple" :v2 3, :actual-is-too-big true})
Value has extra keys: #{:actual-is-too-big}; it is {:v1 "apple", ...
=> nil
Note: this predicate works only with keys, not paths.
Note also: all that matters is the presence of keys, not their values.
exactly
(exactly expected)
A predicate that succeeds exactly when its argument is =
to expected
. Here’s an example of defining a versioned type:
(type! :V5 {:version (exactly 5)
:request ...
:response ...
...}
You can also use exactly
as a way to check for the presence of a function, rather than applying that function as a checker:
(built-like? even? even?) ; false: the function `even?` is not an even number
(built-like? (exactly even?) even?) ; true
exactly-keys
(exactly-keys & coll)
Produce a predicate that’s false when applied to a map or record with keys at all different than those given in coll
. See at-most-keys for a variant that allows the value to be missing some of the coll
keys.
user=> (type! :X {:v1 string?
:v2 integer?}
(exactly-keys :v1 :v2))
user=> (built-like :X {:v1 "apple"})
=> Value has missing keys: #{:v2}; it is {:v1 "apple"}
nil
user=> (built-like :X {:v1 "apple" :v2 3})
{:v1 "apple", :v2 3}
user=> (built-like :X {:v1 "apple" :v2 3, :actual-is-too-big true})
Value has extra keys: #{:actual-is-too-big}; it is {:v1 "apple", ...
=> nil
Note: this predicate works only with keys, not paths.
Note also: all that matters is the presence of keys, not their values.
exactly==
(exactly== expected)
This predicate is like exactly except it uses ==
instead of =
. That is, it checks equality irrespective of type. That is, whereas ((exactly 1M) 1)
is false
, ((exactly== 1M) 1)
is true
.
implies
(implies if-part then-part if-part then-part ...)
There’s enough going on with implies
that it has its own page in the user documentation: http://bit.ly/1LeLTy9.
Both the if
and then
parts are either a single condensed type description (like (requires :a :b :c)
) or a collection of them wrapped in all-of.
Each if-part
is evaluated in turn. When the if-part
matches the whole value, then the then-part
is applied to check for type errors. Otherwise,the then-part
is ignored.
;; If `:a` is present, `:b` must also be present:
(type! :X (pred/implies :a :b))
;; If `:a` is absent (or nil), `:b` must be odd.
(type! :X (pred/implies {:a nil?} {:b [required-path odd?]}))
;; A use of `all-of`:
(type! :X (pred/implies :a (pred/all-of (includes :Point)
(requires :color))))
Warning: this “predicate” cannot be used outside of a structural-typing functions like type!
, named
, and built-like
.
kvs
(kvs maplike)
A variant of exactly that ignores the difference between records and maps.
In Clojure, compound values are usually equal if their contents are equal. For example:
(= (vector 1 2) (list 1 2)) ;=> true
That is not true when comparing records to maps. Therefore, the following will always be false if structure
is a record.
(built-like (pred/exactly {:a 1, :b 2}) structure)
However, the following will be true, given (defrecord R [a b])
:
(built-like (pred/kvs {:a 1, :b 2}) (R. 1 2))
Note: kvs
is false when given anything other than a map or record.
matches
(matches regex)
Produce a predicate that returns true when any part of a string matches regex
. (That is, re-find
is used instead of re-matches
.)
user=> (built-like (pred/matches #"ab+") "Look at this: abbb. Cool huh?")
"Look at this: abbb. Cool huh?"
member
(member coll)
Produce a predicate that’s false when applied to a value not a member of coll
. The explainer associated with member
prints coll
.
user=> (type! :small-primes {:n (member [2 3 5 7])})
user=> (built-like :small-primes {:n 2000})
:n should be a member of `[2 3 5 7]`; it is `2000`
=> nil
not-empty?
Provides a more pleasant error explanation than (complement empty?)
or seq
.
user=> (built-like pred/not-empty? [])
Value should be a non-empty collection; it is `[]`
=> nil