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