      (checked @global-type-repo name kvs)))
 
 (defn instance? 
+  "Return `true` iff the map or record `kvs` typechecks against the type named `name` in
+   `type-repo`. If `type-repo` is omitted, the global repo is used.
+   
+       (type/instance? :frobbable kvs)
+"
   ([type-repo name kvs]
      (checked (assoc type-repo
                      :failure-handler (constantly false) 

(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))))))

(defn named [type-repo name keys]
  (checked-internal own-types :type-repo type-repo)
  (named-internal type-repo name keys))

(defn checked
  ([type-repo name kvs]
     (checked-internal own-types :type-repo type-repo)
     (checked-internal type-repo name kvs))

(ns structural-typing.type
  "Structural types, loosely inspired by Elm's way of looking at [records](http://elm-lang.org/learn/Records.elm)."
  (:refer-clojure :exclude [instance?])
  (:require [clojure.string :as str]
            [bouncer.core :as b]
            [bouncer.validators :as v]
            [such.immigration :as immigrate]))

;;; About our type

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

(defn default-failure-handler
  "This failure handler prints each error message on a separate line. It returns
   `nil`, allowing constructs like this:
   
        (some-> (type/checked :frobnoz x)
                (assoc :goodness true)
                ...)
"
  [messages]
  (doseq [s messages]
    (println s))
  nil)

(defn throwing-failure-handler 
  "In contrast to the default failure handler, this one throws a
   `java.lang.Exception` whose message is the concatenation of the
   type-mismatch messages.
   
   To make all type mismatches throw failures, do this:
   
          (type/set-failure-handler! type/throwing-failure-handler)
"
  [messages]
  (throw (new Exception (str/join "\n" messages))))

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

(def empty-type-repo
  "A repository that contains no type descriptions. It contains
   default behavior for both success and failure cases. Here's
   an example of changing the behavior and adding a type:
   
         (-> type/empty-type-repo
             (assoc :failure-handler type/throwing-failure-handler)
             (type/named :frobable [:left :right :arrow]))
"
  {:failure-handler default-failure-handler
   :success-handler default-success-handler
   :formatter default-formatter})

(declare ^:private own-types)

