          v)))))
 
 (defn reduce
+  "f should be a function of 2 arguments. If val is not supplied,
+  returns the result of applying f to the first 2 items in coll, then
+  applying f to that result and the 3rd item, etc. If coll contains no
+  items, f must accept no arguments as well, and reduce returns the
+  result of calling f with no arguments.  If coll has only 1 item, it
+  is returned and f is not called.  If val is supplied, returns the
+  result of applying f to val and the first item in coll, then
+  applying f to that result and the 2nd item, etc. If coll contains no
+  items, returns val and f is not called."
   ([f coll]
    (let [[x & xs] coll] (reduce f x xs)))
-  ([f z coll]
+  ([f val coll]
    (if (empty? coll)
-     z
+     val
      (let [[x & xs] coll]
-       (reduce f (f z x) xs)))))
+       (reduce f (f val x) xs)))))
+
+(defn reduce-kv
+  "Reduces an associative collection. f should be a function of 3
+  arguments. Returns the result of applying f to init, the first key
+  and the first value in coll, then applying f to that result and the
+  2nd key and value, etc. If coll contains no entries, returns init
+  and f is not called. Note that reduce-kv is supported on vectors,
+  where the keys will be the ordinals."
+  ([f init coll]
+   (if (nil? coll)
+     init
+     (-kv-reduce coll f init))))
 
 (defn identity [x] x)
 

(defprotocol IReduce
  (-reduce [coll f] [coll f start]))

(defprotocol IPrintable
  (-pr-str [o]))


  (-reduce [coll f] (ci-reduce coll f start))
  (-reduce [coll f start] (ci-reduce coll f start))

  IKVReduce
  (-kv-reduce [coll f init]
    (let [len (-count coll)]
      (loop [i 0
             v init]
        (if (< i len)
          (recur (inc i) (f v i (-nth i coll)))
          v))))

  IFn
  (-invoke [coll k]
