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