structural-typing.type
Structural types, loosely inspired by Elm’s way of looking at records.
<>all-built-like
(<>all-built-like candidates type-repo type-shorthand)(<>all-built-like candidates type-shorthand)The same as all-built-like but intended to be used in -> pipelines. Consequently, the candidates argument comes first.
  (-> emr-patients
      augment           (<>all-built-like [:Decidable Patient])
      audit
      decide
      schedule)
(The <> is intended to remind you of swiss arrows.)
<>built-like
(<>built-like candidate type-repo type-shorthand)(<>built-like candidate type-shorthand)The same as built-like but intended to be used in -> pipelines. Consequently, the candidate argument comes first.
  (-> emr-patient
      augment           (<>built-like [:Decidable Patient])
      audit
      decide
      schedule)
(The <> is intended to remind you of swiss arrows.)
ALL
When included in a path, expects a collection and applies the rest of the path to each element.
all-built-like
(all-built-like type-repo type-shorthand candidates)(all-built-like type-shorthand candidates)Check each of the candidates. Perform the type-repo’s error behavior if any of the candidates fail. Otherwise, return the original candidates.
(some->> (all-built-like :Point [{:x 1, :y 2}
                                 {:why "so serious?"}])
         (map process-points))
Error messages will include the index of the structure that failed.
built-like
(built-like type-repo type-shorthand candidate)(built-like type-shorthand candidate)type-shorthand is either a type-signifier (typically a keyword like :Point), a condensed type description (like (requires :x :y)), or a vector containing either or both. built-like checks the candidate against the shorthand. 
By default, built-like will either return the candidate or, if the candidate doesn’t match the shorthand, print an error message and return nil. If the type-repo is omitted, the global one is used.
(type/built-like :Point {:x 1 :y 2})
(type/built-like [:Colorful :Point] {:x 1, :y 2, :color "red"})
(type/built-like [:Colorful (requires :x :y)] {:x 1, :y 2, :color "red"})
Types are defined with named or type!. Default behavior is changed with replace-success-handler, replace-error-handler, on-success!, and on-error!.
built-like?
(built-like? type-repo type-shorthand candidate)(built-like? type-shorthand candidate)type-shorthand is either a type-signifier (typically a keyword like :Point), a condensed type description (like (requires :x :y)), or a vector containing either or both.
Returns true iff the candidate structure matches everything in the shorthand.
With three arguments, the check is against the type-repo. If type-repo is omitted, the global repo is used.
(type/built-like? :Point candidate)
(type/built-like? [:Colorful :Point] candidate)
default-error-handler
This error handler takes the output of type checking (a sequence of oopsies) and prints each one’s explanation to standard output. It returns nil, allowing constructs like this:
 (some-> (type/built-like :Patient p)
         (assoc :handled true)
         ...)
default-success-handler
The default success handler just returns the original candidate structure passed to built-like.
description
(description type-repo type-signifier)(description type-signifier)Returns the canonical (expanded) description of the type-signifier. Uses the global type repo if none is given. 
The result is not a string, but rather a structure tweaked to look nice either at the repl or as the output from pprint. However, that means it’s not a real type description; you can’t feed it back to named or type!.
each-of
Use each-of to describe a “forking” path. This is convenient when two parts of a bigger data structure should be built the same way.
(type! :Plat {[:corners (each-of :nw :ne :sw :se)] (includes :GeoPoint)})
through-each is a synonym. I tend to use each-of for the end of the path, through-each for a fork earlier than that.
empty-type-repo
A type repo that contains no types and uses the default success and error handlers.
ensure-standard-functions
macro
(ensure-standard-functions type-repo-sym)Suppose you are creating a type repo inside a namespace, as is done in the logging example. You’d like that namespace to provide functions that use that type repo without having to constantly refer to it:
 (my.types/built-like? :Point xy)
 ;; instead of:
 (my.types/built-like? my.types/type-repo :Point xy)
This function takes a type repo and creates type-repo-specific functions for you.
 (in-ns 'my.types)
 (type/ensure-standard-functions type-repo)
See the examples directory more details.
explain-with
(explain-with explainer predicate)After the predicate fails, the failure will need to be explained. Arrange for explainer to be called with the oopsie that results from the failure.
 (explain-with #(format "Yo! %s has %s characters, which is WAY too long."
                        (:leaf-value %)
                        (count (:leaf-value %)))
               #(< (count %) 8)))
includes
(includes type-signifier)named
(named type-repo type-signifier & condensed-type-descriptions)Define type-signifier inside the type-repo in terms of the condensed-type-descriptions.
Returns the augmented type-repo. See also named!.
not-nil
(not-nil & args)WARNING: #’structural-typing.guts.preds.pseudopreds/not-nil is deprecated. Deprecated in favor of required-path, reject-nil, or reject-missing.
origin
(origin type-repo type-signifier)(origin type-signifier)Returns the original condensed type description associated with the type-signifier. Uses the global type repo if none is given.
The result is not a string, but rather a structure tweaked to look nice either at the repl or as the output from pprint. However, that means it’s not a real type description; you can’t feed it back to named or type!.
paths-of
(paths-of type-signifier-or-map)Include all the paths of a type (or a literal map) within a path.
 (type! :StrictX (includes :X)
                 (requires (paths-of :X)))
The above example constructs a stricter version of :X by insisting all of its paths are required.
When the argument is a map, it is flattened before the paths are extracted, so that {:a {:b even?}} and {[:a :b] even?} have the same effect. (Included types are already flat.)
RANGE
(RANGE inclusive-start exclusive-end)Use this in a path to select a range of values in a collection. The first argument is inclusive; the second exclusive.
(type! :ELEMENTS-1-AND-2-ARE-EVEN {[(RANGE 1 3)] even?})
reject-missing
This appears in a predicate list, but it is never called directly. Its appearance means that cases like the following are rejected:
user=> (type! :X {:a [string? reject-missing]})
user=> (built-like :X {})
:a does not exist
user=>  (type! :X {[(RANGE 0 3)] [reject-missing even?]})
user=> (built-like :X [])
[0] does not exist
[1] does not exist
[2] does not exist
See also reject-nil and required-path.
reject-nil
False iff the value given is nil. By default, type descriptions allow nil values, following Clojure’s lead. To reject nils, use type descriptions like this:
(type! :X {:a [reject-nil string?]})
… or, when checking types directly:
(built-like [string? reject-nil] nil)
See also reject-missing and required-path.
replace-error-handler
(replace-error-handler type-repo handler)For this type-repo, pass oopsies generated by type failures to handler as the last step in built-like. Thus, built-like will return the handler’s result.
replace-success-handler
(replace-success-handler type-repo handler)For this type-repo, handle candidates that typecheck successfully by passing them to handler as the last step in built-like. Thus, built-like will return the handler’s result.
required-path
False iff a key/path does not exist or has value nil. See also reject-missing and reject-nil.
requires
(requires & args)Often, all you want to say about some parts of a type is that they’re required. requires is a shorthand way to do that.
(type! :Point (requires :x :y))
(type! :Line (requires [:start :x] [:start :y]))
(type! :Line (requires [(through-each :start :end) (:each-of :x :y)]))
requires-mentioned-paths
(requires-mentioned-paths & condensed-type-descriptions)Canonicalizes the type descriptions into a single path->pred map and adds required-path to each path’s predicates.
 (type! :X (requires-mentioned-paths (includes :Point)
                                     {:color rgb-string?}))
Note: It can’t require paths you don’t mention. The easiest way to mention a path is to name it in a requires - which may be either an argument to this function or outside it:
 (type! :X (requires-mentioned-paths (requires :name)
                                     (includes :Point)))
 (type! :X (requires :name)
           (requires-mentioned-paths (includes :Point)))
show-as
(show-as name predicate)Associate the given name string with the predicate for use when predicate failures are explained.
  (show-as "less than 3" (partial >= 3))
through-each
(through-each & alternatives)Use through-each to describe a “forking” path. This is convenient when two parts of a bigger data structure should be built the same way.
(type! :Line {[(through-each :start :end) :location] (includes :Point)})
each-of is a synonym. I tend to use each-of for the end of the path, through-each for a fork earlier than that.
throwing-error-handler
(throwing-error-handler oopsies)In contrast to the default error handler, this one throws a java.lang.Exception whose message is the concatenation of the explanations of the oopsies.
To make all type mismatches throw failures, do this:
   (global-type/on-error! type/throwing-error-handler) ; for the global type repo
   (type/replace-error-handler type-repo type/throwing-error-handler) ; local repo