 (defn- checked-internal [type-repo name kvs]
   (let [[errors actual] (b/validate kvs (get-in type-repo [:validators name]))]
     (if (empty? errors)
-      ((success-handler type-repo) kvs)
-      (-> ( (formatter type-repo) errors (dissoc actual :bouncer.core/errors))
-          ((failure-handler type-repo))))))
+      ((:success-handler type-repo) kvs)
+      (-> ( (:formatter type-repo) errors (dissoc actual :bouncer.core/errors))
+          ((:failure-handler type-repo))))))
 
-(defn named [type-repo name keys]
+(defn named 
+  "Define the type `name` as being a map or record containing all of the given `keys`.
+   Returns the augmented `type-repo`. See also [[named!]].
+"
+  [type-repo name keys]
   (checked-internal own-types :type-repo type-repo)
   (named-internal type-repo name keys))
 
 (defn checked
+  "Check the map `kvs` against the previously-defined type `name` in the given
+   `type-repo`. If the `type-repo` is omitted, the global one is used.
+   
+       (type/checked :frobbish {:twerk true, :tweek false})
+   
+   Types are defined with [[named]] or [[named!]]. By default, `checked` returns
+   the `kvs` argument if it checks out, `nil` otherwise. Those defaults can be
+   changed.
+"
   ([type-repo name kvs]
      (checked-internal own-types :type-repo type-repo)
      (checked-internal type-repo name kvs))

(ns structural-typing.type
  ""
  (:refer-clojure :exclude [instance?])
  (:require [clojure.string :as str]
            [bouncer.core :as b]
            [bouncer.validators :as v]
            [such.immigration :as immigrate]))

(immigrate/selection 'bouncer.validators '[required])

;;; About our type

(defn- default-formatter [error-map checked-map]
  (flatten (vals error-map)))

(defn- default-failure-handler [msgs]
  (doseq [s msgs]
    (println s))
  nil)

(defn throwing-failure-handler [messages]
  (throw (new Exception (str/join "\n" messages))))

(def ^:private default-success-handler identity)

(defn- formatter [type-repo]
  (get type-repo :formatter default-formatter))
(defn- failure-handler [type-repo]
  (get type-repo :failure-handler default-failure-handler))
(defn- success-handler [type-repo]
  (get type-repo :success-handler default-success-handler))

(def empty-type-repo {:failure-handler default-failure-handler
                      :success-handler default-success-handler
                      :formatter default-formatter})

(declare ^:private own-types)


  ([name kvs]
     (instance? @global-type-repo name kvs)))

(defn coercion 
  "Register function `f` as one that can coerce a map or record into 
   one that matches type `name`. The updated `type-repo` is returned.
   See also [[coercion!]], which updates the global type repo."
  [type-repo name f]
  (checked-internal own-types :type-repo type-repo)
  (assoc-in type-repo [:coercions name] f))

(defn coerce
  "Coerce the map or record `kvs` into the type named `name` in the `type-repo`
   and check the result with [[checked]]. The coerced version of `kvs` is returned.
   
   If the type repo is omitted, the global type repo is used.
   Coercions are defined with [[coercion]] or [[coercion!]].
   
        (some-> (coerce :user-v2 legacy-json)
                (update-in [:stats :logins] inc))

   If `name` hasn't been defined (via [[name]] or [[named!]]), the final
   call to `checked` is omitted.
"
  ([type-repo name kvs]
     (checked-internal own-types :type-repo type-repo)
     (let [coercer (get-in type-repo [:coercions name] identity)]
