diff src/clojure/core_deftype.clj @ 10:ef7dbbd6452c

added clojure source goodness
author Robert McIntyre <rlm@mit.edu>
date Sat, 21 Aug 2010 06:25:44 -0400
parents
children
line wrap: on
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/src/clojure/core_deftype.clj	Sat Aug 21 06:25:44 2010 -0400
     1.3 @@ -0,0 +1,769 @@
     1.4 +;   Copyright (c) Rich Hickey. All rights reserved.
     1.5 +;   The use and distribution terms for this software are covered by the
     1.6 +;   Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
     1.7 +;   which can be found in the file epl-v10.html at the root of this distribution.
     1.8 +;   By using this software in any fashion, you are agreeing to be bound by
     1.9 +;   the terms of this license.
    1.10 +;   You must not remove this notice, or any other, from this software.
    1.11 +
    1.12 +(in-ns 'clojure.core)
    1.13 +
    1.14 +;;;;;;;;;;;;;;;;;;;;;;;;;;;; definterface ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    1.15 +
    1.16 +(defn namespace-munge
    1.17 +  "Convert a Clojure namespace name to a legal Java package name."
    1.18 +  {:added "1.2"}
    1.19 +  [ns]
    1.20 +  (.replace (str ns) \- \_))
    1.21 +
    1.22 +;for now, built on gen-interface
    1.23 +(defmacro definterface 
    1.24 +  [name & sigs]
    1.25 +  (let [tag (fn [x] (or (:tag (meta x)) Object))
    1.26 +        psig (fn [[name [& args]]]
    1.27 +               (vector name (vec (map tag args)) (tag name) (map meta args)))
    1.28 +        cname (with-meta (symbol (str (namespace-munge *ns*) "." name)) (meta name))]
    1.29 +    `(let [] 
    1.30 +       (gen-interface :name ~cname :methods ~(vec (map psig sigs)))
    1.31 +       (import ~cname))))
    1.32 +
    1.33 +;;;;;;;;;;;;;;;;;;;;;;;;;;;; reify/deftype ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    1.34 +
    1.35 +(defn- parse-opts [s]
    1.36 +  (loop [opts {} [k v & rs :as s] s]
    1.37 +    (if (keyword? k)
    1.38 +      (recur (assoc opts k v) rs)
    1.39 +      [opts s])))
    1.40 +
    1.41 +(defn- parse-impls [specs]
    1.42 +  (loop [ret {} s specs]
    1.43 +    (if (seq s)
    1.44 +      (recur (assoc ret (first s) (take-while seq? (next s)))
    1.45 +             (drop-while seq? (next s)))
    1.46 +      ret)))
    1.47 +
    1.48 +(defn- parse-opts+specs [opts+specs]
    1.49 +  (let [[opts specs] (parse-opts opts+specs)
    1.50 +        impls (parse-impls specs)
    1.51 +        interfaces (-> (map #(if (var? (resolve %)) 
    1.52 +                               (:on (deref (resolve %)))
    1.53 +                               %)
    1.54 +                            (keys impls))
    1.55 +                       set
    1.56 +                       (disj 'Object 'java.lang.Object)
    1.57 +                       vec)
    1.58 +        methods (map (fn [[name params & body]]
    1.59 +                       (cons name (maybe-destructured params body)))
    1.60 +                     (apply concat (vals impls)))]
    1.61 +    (when-let [bad-opts (seq (remove #{:no-print} (keys opts)))]
    1.62 +      (throw (IllegalArgumentException. (apply print-str "Unsupported option(s) -" bad-opts))))
    1.63 +    [interfaces methods opts]))
    1.64 +
    1.65 +(defmacro reify 
    1.66 +  "reify is a macro with the following structure:
    1.67 +
    1.68 + (reify options* specs*)
    1.69 +  
    1.70 +  Currently there are no options.
    1.71 +
    1.72 +  Each spec consists of the protocol or interface name followed by zero
    1.73 +  or more method bodies:
    1.74 +
    1.75 +  protocol-or-interface-or-Object
    1.76 +  (methodName [args+] body)*
    1.77 +
    1.78 +  Methods should be supplied for all methods of the desired
    1.79 +  protocol(s) and interface(s). You can also define overrides for
    1.80 +  methods of Object. Note that the first parameter must be supplied to
    1.81 +  correspond to the target object ('this' in Java parlance). Thus
    1.82 +  methods for interfaces will take one more argument than do the
    1.83 +  interface declarations.  Note also that recur calls to the method
    1.84 +  head should *not* pass the target object, it will be supplied
    1.85 +  automatically and can not be substituted.
    1.86 +
    1.87 +  The return type can be indicated by a type hint on the method name,
    1.88 +  and arg types can be indicated by a type hint on arg names. If you
    1.89 +  leave out all hints, reify will try to match on same name/arity
    1.90 +  method in the protocol(s)/interface(s) - this is preferred. If you
    1.91 +  supply any hints at all, no inference is done, so all hints (or
    1.92 +  default of Object) must be correct, for both arguments and return
    1.93 +  type. If a method is overloaded in a protocol/interface, multiple
    1.94 +  independent method definitions must be supplied.  If overloaded with
    1.95 +  same arity in an interface you must specify complete hints to
    1.96 +  disambiguate - a missing hint implies Object.
    1.97 +
    1.98 +  recur works to method heads The method bodies of reify are lexical
    1.99 +  closures, and can refer to the surrounding local scope:
   1.100 +  
   1.101 +  (str (let [f \"foo\"] 
   1.102 +       (reify Object 
   1.103 +         (toString [this] f))))
   1.104 +  == \"foo\"
   1.105 +
   1.106 +  (seq (let [f \"foo\"] 
   1.107 +       (reify clojure.lang.Seqable 
   1.108 +         (seq [this] (seq f)))))
   1.109 +  == (\\f \\o \\o))"
   1.110 +  {:added "1.2"} 
   1.111 +  [& opts+specs]
   1.112 +  (let [[interfaces methods] (parse-opts+specs opts+specs)]
   1.113 +    (with-meta `(reify* ~interfaces ~@methods) (meta &form))))
   1.114 +
   1.115 +(defn hash-combine [x y] 
   1.116 +  (clojure.lang.Util/hashCombine x (clojure.lang.Util/hash y)))
   1.117 +
   1.118 +(defn munge [s]
   1.119 +  ((if (symbol? s) symbol str) (clojure.lang.Compiler/munge (str s))))
   1.120 +
   1.121 +(defn- imap-cons
   1.122 +  [^IPersistentMap this o]
   1.123 +  (cond
   1.124 +   (instance? java.util.Map$Entry o)
   1.125 +     (let [^java.util.Map$Entry pair o]
   1.126 +       (.assoc this (.getKey pair) (.getValue pair)))
   1.127 +   (instance? clojure.lang.IPersistentVector o)
   1.128 +     (let [^clojure.lang.IPersistentVector vec o]
   1.129 +       (.assoc this (.nth vec 0) (.nth vec 1)))
   1.130 +   :else (loop [this this
   1.131 +                o o]
   1.132 +      (if (seq o)
   1.133 +        (let [^java.util.Map$Entry pair (first o)]
   1.134 +          (recur (.assoc this (.getKey pair) (.getValue pair)) (rest o)))
   1.135 +        this))))
   1.136 +
   1.137 +(defn- emit-defrecord 
   1.138 +  "Do not use this directly - use defrecord"
   1.139 +  {:added "1.2"}
   1.140 +  [tagname name fields interfaces methods]
   1.141 +  (let [tag (keyword (str *ns*) (str tagname))
   1.142 +        classname (with-meta (symbol (str *ns* "." name)) (meta name))
   1.143 +        interfaces (vec interfaces)
   1.144 +        interface-set (set (map resolve interfaces))
   1.145 +        methodname-set (set (map first methods))
   1.146 +        hinted-fields fields
   1.147 +        fields (vec (map #(with-meta % nil) fields))
   1.148 +        base-fields fields
   1.149 +        fields (conj fields '__meta '__extmap)]
   1.150 +    (when (some #{:volatile-mutable :unsynchronized-mutable} (mapcat (comp keys meta) hinted-fields))
   1.151 +      (throw (IllegalArgumentException. ":volatile-mutable or :unsynchronized-mutable not supported for record fields")))
   1.152 +    (let [gs (gensym)]
   1.153 +    (letfn 
   1.154 +     [(eqhash [[i m]] 
   1.155 +        [i
   1.156 +         (conj m 
   1.157 +               `(hashCode [this#] (clojure.lang.APersistentMap/mapHash this#))
   1.158 +               `(equals [this# ~gs] (clojure.lang.APersistentMap/mapEquals this# ~gs)))])
   1.159 +      (iobj [[i m]] 
   1.160 +            [(conj i 'clojure.lang.IObj)
   1.161 +             (conj m `(meta [this#] ~'__meta)
   1.162 +                   `(withMeta [this# ~gs] (new ~tagname ~@(replace {'__meta gs} fields))))])
   1.163 +      (ilookup [[i m]] 
   1.164 +         [(conj i 'clojure.lang.ILookup 'clojure.lang.IKeywordLookup)
   1.165 +          (conj m `(valAt [this# k#] (.valAt this# k# nil))
   1.166 +                `(valAt [this# k# else#] 
   1.167 +                   (case k# ~@(mapcat (fn [fld] [(keyword fld) fld]) 
   1.168 +                                       base-fields)
   1.169 +                         (get ~'__extmap k# else#)))
   1.170 +                `(getLookupThunk [this# k#]
   1.171 +                   (let [~'gclass (class this#)]              
   1.172 +                     (case k#
   1.173 +                           ~@(let [hinted-target (with-meta 'gtarget {:tag tagname})] 
   1.174 +                               (mapcat 
   1.175 +                                (fn [fld]
   1.176 +                                  [(keyword fld) 
   1.177 +                                   `(reify clojure.lang.ILookupThunk 
   1.178 +                                           (get [~'thunk ~'gtarget] 
   1.179 +                                                (if (identical? (class ~'gtarget) ~'gclass) 
   1.180 +                                                  (. ~hinted-target ~(keyword fld))
   1.181 +                                                  ~'thunk)))])
   1.182 +                                base-fields))
   1.183 +                           nil))))])
   1.184 +      (imap [[i m]] 
   1.185 +            [(conj i 'clojure.lang.IPersistentMap)
   1.186 +             (conj m 
   1.187 +                   `(count [this#] (+ ~(count base-fields) (count ~'__extmap)))
   1.188 +                   `(empty [this#] (throw (UnsupportedOperationException. (str "Can't create empty: " ~(str classname)))))
   1.189 +                   `(cons [this# e#] ((var imap-cons) this# e#))
   1.190 +                   `(equiv [this# ~gs] 
   1.191 +                        (boolean 
   1.192 +                         (or (identical? this# ~gs)
   1.193 +                             (when (identical? (class this#) (class ~gs))
   1.194 +                               (let [~gs ~(with-meta gs {:tag tagname})]
   1.195 +                                 (and  ~@(map (fn [fld] `(= ~fld (. ~gs ~fld))) base-fields)
   1.196 +                                       (= ~'__extmap (. ~gs ~'__extmap))))))))
   1.197 +                   `(containsKey [this# k#] (not (identical? this# (.valAt this# k# this#))))
   1.198 +                   `(entryAt [this# k#] (let [v# (.valAt this# k# this#)]
   1.199 +                                            (when-not (identical? this# v#)
   1.200 +                                              (clojure.lang.MapEntry. k# v#))))
   1.201 +                   `(seq [this#] (seq (concat [~@(map #(list `new `clojure.lang.MapEntry (keyword %) %) base-fields)] 
   1.202 +                                              ~'__extmap)))
   1.203 +                   `(assoc [this# k# ~gs]
   1.204 +                     (condp identical? k#
   1.205 +                       ~@(mapcat (fn [fld]
   1.206 +                                   [(keyword fld) (list* `new tagname (replace {fld gs} fields))])
   1.207 +                                 base-fields)
   1.208 +                       (new ~tagname ~@(remove #{'__extmap} fields) (assoc ~'__extmap k# ~gs))))
   1.209 +                   `(without [this# k#] (if (contains? #{~@(map keyword base-fields)} k#)
   1.210 +                                            (dissoc (with-meta (into {} this#) ~'__meta) k#)
   1.211 +                                            (new ~tagname ~@(remove #{'__extmap} fields) 
   1.212 +                                                 (not-empty (dissoc ~'__extmap k#))))))])
   1.213 +      (ijavamap [[i m]]
   1.214 +                [(conj i 'java.util.Map 'java.io.Serializable)
   1.215 +                 (conj m
   1.216 +                       `(size [this#] (.count this#))
   1.217 +                       `(isEmpty [this#] (= 0 (.count this#)))
   1.218 +                       `(containsValue [this# v#] (boolean (some #{v#} (vals this#))))
   1.219 +                       `(get [this# k#] (.valAt this# k#))
   1.220 +                       `(put [this# k# v#] (throw (UnsupportedOperationException.)))
   1.221 +                       `(remove [this# k#] (throw (UnsupportedOperationException.)))
   1.222 +                       `(putAll [this# m#] (throw (UnsupportedOperationException.)))
   1.223 +                       `(clear [this#] (throw (UnsupportedOperationException.)))
   1.224 +                       `(keySet [this#] (set (keys this#)))
   1.225 +                       `(values [this#] (vals this#))
   1.226 +                       `(entrySet [this#] (set this#)))])
   1.227 +      ]
   1.228 +     (let [[i m] (-> [interfaces methods] eqhash iobj ilookup imap ijavamap)]
   1.229 +       `(deftype* ~tagname ~classname ~(conj hinted-fields '__meta '__extmap) 
   1.230 +          :implements ~(vec i) 
   1.231 +          ~@m))))))
   1.232 +
   1.233 +(defmacro defrecord
   1.234 +  "Alpha - subject to change
   1.235 +  
   1.236 +  (defrecord name [fields*]  options* specs*)
   1.237 +  
   1.238 +  Currently there are no options.
   1.239 +
   1.240 +  Each spec consists of a protocol or interface name followed by zero
   1.241 +  or more method bodies:
   1.242 +
   1.243 +  protocol-or-interface-or-Object
   1.244 +  (methodName [args*] body)*
   1.245 +
   1.246 +  Dynamically generates compiled bytecode for class with the given
   1.247 +  name, in a package with the same name as the current namespace, the
   1.248 +  given fields, and, optionally, methods for protocols and/or
   1.249 +  interfaces.
   1.250 +
   1.251 +  The class will have the (immutable) fields named by
   1.252 +  fields, which can have type hints. Protocols/interfaces and methods
   1.253 +  are optional. The only methods that can be supplied are those
   1.254 +  declared in the protocols/interfaces.  Note that method bodies are
   1.255 +  not closures, the local environment includes only the named fields,
   1.256 +  and those fields can be accessed directy.
   1.257 +
   1.258 +  Method definitions take the form:
   1.259 +
   1.260 +  (methodname [args*] body)
   1.261 +
   1.262 +  The argument and return types can be hinted on the arg and
   1.263 +  methodname symbols. If not supplied, they will be inferred, so type
   1.264 +  hints should be reserved for disambiguation.
   1.265 +
   1.266 +  Methods should be supplied for all methods of the desired
   1.267 +  protocol(s) and interface(s). You can also define overrides for
   1.268 +  methods of Object. Note that a parameter must be supplied to
   1.269 +  correspond to the target object ('this' in Java parlance). Thus
   1.270 +  methods for interfaces will take one more argument than do the
   1.271 +  interface declarations. Note also that recur calls to the method
   1.272 +  head should *not* pass the target object, it will be supplied
   1.273 +  automatically and can not be substituted.
   1.274 +
   1.275 +  In the method bodies, the (unqualified) name can be used to name the
   1.276 +  class (for calls to new, instance? etc).
   1.277 +
   1.278 +  The class will have implementations of several (clojure.lang)
   1.279 +  interfaces generated automatically: IObj (metadata support) and
   1.280 +  IPersistentMap, and all of their superinterfaces.
   1.281 +
   1.282 +  In addition, defrecord will define type-and-value-based equality and
   1.283 +  hashCode.
   1.284 +
   1.285 +  When AOT compiling, generates compiled bytecode for a class with the
   1.286 +  given name (a symbol), prepends the current ns as the package, and
   1.287 +  writes the .class file to the *compile-path* directory.
   1.288 +
   1.289 +  Two constructors will be defined, one taking the designated fields
   1.290 +  followed by a metadata map (nil for none) and an extension field
   1.291 +  map (nil for none), and one taking only the fields (using nil for
   1.292 +  meta and extension fields)."
   1.293 +  {:added "1.2"}
   1.294 +
   1.295 +  [name [& fields] & opts+specs]
   1.296 +  (let [gname name
   1.297 +        [interfaces methods opts] (parse-opts+specs opts+specs)
   1.298 +        classname (symbol (str *ns* "." gname))
   1.299 +        tag (keyword (str *ns*) (str name))
   1.300 +        hinted-fields fields
   1.301 +        fields (vec (map #(with-meta % nil) fields))]
   1.302 +    `(let []
   1.303 +       ~(emit-defrecord name gname (vec hinted-fields) (vec interfaces) methods)
   1.304 +       (defmethod print-method ~classname [o# w#]
   1.305 +           ((var print-defrecord) o# w#))
   1.306 +       (import ~classname)
   1.307 +       #_(defn ~name
   1.308 +         ([~@fields] (new ~classname ~@fields nil nil))
   1.309 +         ([~@fields meta# extmap#] (new ~classname ~@fields meta# extmap#))))))
   1.310 +
   1.311 +(defn- print-defrecord [o ^Writer w]
   1.312 +  (print-meta o w)
   1.313 +  (.write w "#:")
   1.314 +  (.write w (.getName (class o)))
   1.315 +  (print-map
   1.316 +    o
   1.317 +    pr-on w))
   1.318 +
   1.319 +(defn- emit-deftype* 
   1.320 +  "Do not use this directly - use deftype"
   1.321 +  [tagname name fields interfaces methods]
   1.322 +  (let [classname (with-meta (symbol (str *ns* "." name)) (meta name))]
   1.323 +    `(deftype* ~tagname ~classname ~fields 
   1.324 +       :implements ~interfaces 
   1.325 +       ~@methods)))
   1.326 +
   1.327 +(defmacro deftype
   1.328 +  "Alpha - subject to change
   1.329 +  
   1.330 +  (deftype name [fields*]  options* specs*)
   1.331 +  
   1.332 +  Currently there are no options.
   1.333 +
   1.334 +  Each spec consists of a protocol or interface name followed by zero
   1.335 +  or more method bodies:
   1.336 +
   1.337 +  protocol-or-interface-or-Object
   1.338 +  (methodName [args*] body)*
   1.339 +
   1.340 +  Dynamically generates compiled bytecode for class with the given
   1.341 +  name, in a package with the same name as the current namespace, the
   1.342 +  given fields, and, optionally, methods for protocols and/or
   1.343 +  interfaces. 
   1.344 +
   1.345 +  The class will have the (by default, immutable) fields named by
   1.346 +  fields, which can have type hints. Protocols/interfaces and methods
   1.347 +  are optional. The only methods that can be supplied are those
   1.348 +  declared in the protocols/interfaces.  Note that method bodies are
   1.349 +  not closures, the local environment includes only the named fields,
   1.350 +  and those fields can be accessed directy. Fields can be qualified
   1.351 +  with the metadata :volatile-mutable true or :unsynchronized-mutable
   1.352 +  true, at which point (set! afield aval) will be supported in method
   1.353 +  bodies. Note well that mutable fields are extremely difficult to use
   1.354 +  correctly, and are present only to facilitate the building of higher
   1.355 +  level constructs, such as Clojure's reference types, in Clojure
   1.356 +  itself. They are for experts only - if the semantics and
   1.357 +  implications of :volatile-mutable or :unsynchronized-mutable are not
   1.358 +  immediately apparent to you, you should not be using them.
   1.359 +
   1.360 +  Method definitions take the form:
   1.361 +
   1.362 +  (methodname [args*] body)
   1.363 +
   1.364 +  The argument and return types can be hinted on the arg and
   1.365 +  methodname symbols. If not supplied, they will be inferred, so type
   1.366 +  hints should be reserved for disambiguation.
   1.367 +
   1.368 +  Methods should be supplied for all methods of the desired
   1.369 +  protocol(s) and interface(s). You can also define overrides for
   1.370 +  methods of Object. Note that a parameter must be supplied to
   1.371 +  correspond to the target object ('this' in Java parlance). Thus
   1.372 +  methods for interfaces will take one more argument than do the
   1.373 +  interface declarations. Note also that recur calls to the method
   1.374 +  head should *not* pass the target object, it will be supplied
   1.375 +  automatically and can not be substituted.
   1.376 +
   1.377 +  In the method bodies, the (unqualified) name can be used to name the
   1.378 +  class (for calls to new, instance? etc).
   1.379 +
   1.380 +  When AOT compiling, generates compiled bytecode for a class with the
   1.381 +  given name (a symbol), prepends the current ns as the package, and
   1.382 +  writes the .class file to the *compile-path* directory.
   1.383 +
   1.384 +  One constructors will be defined, taking the designated fields."
   1.385 +  {:added "1.2"}
   1.386 +
   1.387 +  [name [& fields] & opts+specs]
   1.388 +  (let [gname name
   1.389 +        [interfaces methods opts] (parse-opts+specs opts+specs)
   1.390 +        classname (symbol (str *ns* "." gname))
   1.391 +        tag (keyword (str *ns*) (str name))
   1.392 +        hinted-fields fields
   1.393 +        fields (vec (map #(with-meta % nil) fields))]
   1.394 +    `(let []
   1.395 +       ~(emit-deftype* name gname (vec hinted-fields) (vec interfaces) methods)
   1.396 +       (import ~classname))))
   1.397 +
   1.398 +
   1.399 +
   1.400 +
   1.401 +;;;;;;;;;;;;;;;;;;;;;;; protocols ;;;;;;;;;;;;;;;;;;;;;;;;
   1.402 +
   1.403 +(defn- expand-method-impl-cache [^clojure.lang.MethodImplCache cache c f]
   1.404 +  (let [cs (into {} (remove (fn [[c e]] (nil? e)) (map vec (partition 2 (.table cache)))))
   1.405 +        cs (assoc cs c (clojure.lang.MethodImplCache$Entry. c f))
   1.406 +        [shift mask] (min-hash (keys cs))
   1.407 +        table (make-array Object (* 2 (inc mask)))
   1.408 +        table (reduce (fn [^objects t [c e]]
   1.409 +                        (let [i (* 2 (int (shift-mask shift mask (hash c))))]
   1.410 +                          (aset t i c)
   1.411 +                          (aset t (inc i) e)
   1.412 +                          t))
   1.413 +                      table cs)]
   1.414 +    (clojure.lang.MethodImplCache. (.protocol cache) (.methodk cache) shift mask table)))
   1.415 +
   1.416 +(defn- super-chain [^Class c]
   1.417 +  (when c
   1.418 +    (cons c (super-chain (.getSuperclass c)))))
   1.419 +
   1.420 +(defn- pref
   1.421 +  ([] nil)
   1.422 +  ([a] a) 
   1.423 +  ([^Class a ^Class b]
   1.424 +     (if (.isAssignableFrom a b) b a)))
   1.425 +
   1.426 +(defn find-protocol-impl [protocol x]
   1.427 +  (if (instance? (:on-interface protocol) x)
   1.428 +    x
   1.429 +    (let [c (class x)
   1.430 +          impl #(get (:impls protocol) %)]
   1.431 +      (or (impl c)
   1.432 +          (and c (or (first (remove nil? (map impl (butlast (super-chain c)))))
   1.433 +                     (when-let [t (reduce pref (filter impl (disj (supers c) Object)))]
   1.434 +                       (impl t))
   1.435 +                     (impl Object)))))))
   1.436 +
   1.437 +(defn find-protocol-method [protocol methodk x]
   1.438 +  (get (find-protocol-impl protocol x) methodk))
   1.439 +
   1.440 +(defn- protocol?
   1.441 +  [maybe-p]
   1.442 +  (boolean (:on-interface maybe-p)))
   1.443 +
   1.444 +(defn- implements? [protocol atype]
   1.445 +  (and atype (.isAssignableFrom ^Class (:on-interface protocol) atype)))
   1.446 +
   1.447 +(defn extends? 
   1.448 +  "Returns true if atype extends protocol"
   1.449 +  {:added "1.2"}
   1.450 +  [protocol atype]
   1.451 +  (boolean (or (implements? protocol atype) 
   1.452 +               (get (:impls protocol) atype))))
   1.453 +
   1.454 +(defn extenders 
   1.455 +  "Returns a collection of the types explicitly extending protocol"
   1.456 +  {:added "1.2"}
   1.457 +  [protocol]
   1.458 +  (keys (:impls protocol)))
   1.459 +
   1.460 +(defn satisfies? 
   1.461 +  "Returns true if x satisfies the protocol"
   1.462 +  {:added "1.2"}
   1.463 +  [protocol x]
   1.464 +  (boolean (find-protocol-impl protocol x)))
   1.465 +
   1.466 +(defn -cache-protocol-fn [^clojure.lang.AFunction pf x ^Class c ^clojure.lang.IFn interf]
   1.467 +  (let [cache  (.__methodImplCache pf)
   1.468 +        f (if (.isInstance c x)
   1.469 +            interf 
   1.470 +            (find-protocol-method (.protocol cache) (.methodk cache) x))]
   1.471 +    (when-not f
   1.472 +      (throw (IllegalArgumentException. (str "No implementation of method: " (.methodk cache) 
   1.473 +                                             " of protocol: " (:var (.protocol cache)) 
   1.474 +                                             " found for class: " (if (nil? x) "nil" (.getName (class x)))))))
   1.475 +    (set! (.__methodImplCache pf) (expand-method-impl-cache cache (class x) f))
   1.476 +    f))
   1.477 +
   1.478 +(defn- emit-method-builder [on-interface method on-method arglists]
   1.479 +  (let [methodk (keyword method)
   1.480 +        gthis (with-meta (gensym) {:tag 'clojure.lang.AFunction})
   1.481 +        ginterf (gensym)]
   1.482 +    `(fn [cache#]
   1.483 +       (let [~ginterf
   1.484 +             (fn
   1.485 +               ~@(map 
   1.486 +                  (fn [args]
   1.487 +                    (let [gargs (map #(gensym (str "gf__" % "__")) args)
   1.488 +                          target (first gargs)]
   1.489 +                      `([~@gargs]
   1.490 +                          (. ~(with-meta target {:tag on-interface})  ~(or on-method method) ~@(rest gargs)))))
   1.491 +                  arglists))
   1.492 +             ^clojure.lang.AFunction f#
   1.493 +             (fn ~gthis
   1.494 +               ~@(map 
   1.495 +                  (fn [args]
   1.496 +                    (let [gargs (map #(gensym (str "gf__" % "__")) args)
   1.497 +                          target (first gargs)]
   1.498 +                      `([~@gargs]
   1.499 +                          (let [cache# (.__methodImplCache ~gthis)
   1.500 +                                f# (.fnFor cache# (clojure.lang.Util/classOf ~target))]
   1.501 +                            (if f# 
   1.502 +                              (f# ~@gargs)
   1.503 +                              ((-cache-protocol-fn ~gthis ~target ~on-interface ~ginterf) ~@gargs))))))
   1.504 +                  arglists))]
   1.505 +         (set! (.__methodImplCache f#) cache#)
   1.506 +         f#))))
   1.507 +
   1.508 +(defn -reset-methods [protocol]
   1.509 +  (doseq [[^clojure.lang.Var v build] (:method-builders protocol)]
   1.510 +    (let [cache (clojure.lang.MethodImplCache. protocol (keyword (.sym v)))]
   1.511 +      (.bindRoot v (build cache)))))
   1.512 +
   1.513 +(defn- assert-same-protocol [protocol-var method-syms]
   1.514 +  (doseq [m method-syms]
   1.515 +    (let [v (resolve m)
   1.516 +          p (:protocol (meta v))]
   1.517 +      (when (and v (bound? v) (not= protocol-var p))
   1.518 +        (binding [*out* *err*]
   1.519 +          (println "Warning: protocol" protocol-var "is overwriting"
   1.520 +                   (if p
   1.521 +                     (str "method " (.sym v) " of protocol " (.sym p))
   1.522 +                     (str "function " (.sym v)))))))))
   1.523 +
   1.524 +(defn- emit-protocol [name opts+sigs]
   1.525 +  (let [iname (symbol (str (munge *ns*) "." (munge name)))
   1.526 +        [opts sigs]
   1.527 +        (loop [opts {:on (list 'quote iname) :on-interface iname} sigs opts+sigs]
   1.528 +          (condp #(%1 %2) (first sigs) 
   1.529 +            string? (recur (assoc opts :doc (first sigs)) (next sigs))
   1.530 +            keyword? (recur (assoc opts (first sigs) (second sigs)) (nnext sigs))
   1.531 +            [opts sigs]))
   1.532 +        sigs (reduce (fn [m s]
   1.533 +                       (let [name-meta (meta (first s))
   1.534 +                             mname (with-meta (first s) nil)
   1.535 +                             [arglists doc]
   1.536 +                               (loop [as [] rs (rest s)]
   1.537 +                                 (if (vector? (first rs))
   1.538 +                                   (recur (conj as (first rs)) (next rs))
   1.539 +                                   [(seq as) (first rs)]))]
   1.540 +                         (when (some #{0} (map count arglists))
   1.541 +                           (throw (IllegalArgumentException. (str "Protocol fn: " mname " must take at least one arg"))))
   1.542 +                         (assoc m (keyword mname)
   1.543 +                                (merge name-meta
   1.544 +                                       {:name (vary-meta mname assoc :doc doc :arglists arglists)
   1.545 +                                        :arglists arglists
   1.546 +                                        :doc doc}))))
   1.547 +                     {} sigs)
   1.548 +        meths (mapcat (fn [sig]
   1.549 +                        (let [m (munge (:name sig))]
   1.550 +                          (map #(vector m (vec (repeat (dec (count %))'Object)) 'Object) 
   1.551 +                               (:arglists sig))))
   1.552 +                      (vals sigs))]
   1.553 +  `(do
   1.554 +     (defonce ~name {})
   1.555 +     (gen-interface :name ~iname :methods ~meths)
   1.556 +     (alter-meta! (var ~name) assoc :doc ~(:doc opts))
   1.557 +     (#'assert-same-protocol (var ~name) '~(map :name (vals sigs)))
   1.558 +     (alter-var-root (var ~name) merge 
   1.559 +                     (assoc ~opts 
   1.560 +                       :sigs '~sigs 
   1.561 +                       :var (var ~name)
   1.562 +                       :method-map 
   1.563 +                         ~(and (:on opts)
   1.564 +                               (apply hash-map 
   1.565 +                                      (mapcat 
   1.566 +                                       (fn [s] 
   1.567 +                                         [(keyword (:name s)) (keyword (or (:on s) (:name s)))])
   1.568 +                                       (vals sigs))))
   1.569 +                       :method-builders 
   1.570 +                        ~(apply hash-map 
   1.571 +                                (mapcat 
   1.572 +                                 (fn [s]
   1.573 +                                   [`(intern *ns* (with-meta '~(:name s) (merge '~s {:protocol (var ~name)})))
   1.574 +                                    (emit-method-builder (:on-interface opts) (:name s) (:on s) (:arglists s))])
   1.575 +                                 (vals sigs)))))
   1.576 +     (-reset-methods ~name)
   1.577 +     '~name)))
   1.578 +
   1.579 +(defmacro defprotocol 
   1.580 +  "A protocol is a named set of named methods and their signatures:
   1.581 +  (defprotocol AProtocolName
   1.582 +
   1.583 +    ;optional doc string
   1.584 +    \"A doc string for AProtocol abstraction\"
   1.585 +
   1.586 +  ;method signatures
   1.587 +    (bar [this a b] \"bar docs\")
   1.588 +    (baz [this a] [this a b] [this a b c] \"baz docs\"))
   1.589 +
   1.590 +  No implementations are provided. Docs can be specified for the
   1.591 +  protocol overall and for each method. The above yields a set of
   1.592 +  polymorphic functions and a protocol object. All are
   1.593 +  namespace-qualified by the ns enclosing the definition The resulting
   1.594 +  functions dispatch on the type of their first argument, which is
   1.595 +  required and corresponds to the implicit target object ('this' in 
   1.596 +  Java parlance). defprotocol is dynamic, has no special compile-time 
   1.597 +  effect, and defines no new types or classes. Implementations of 
   1.598 +  the protocol methods can be provided using extend.
   1.599 +
   1.600 +  defprotocol will automatically generate a corresponding interface,
   1.601 +  with the same name as the protocol, i.e. given a protocol:
   1.602 +  my.ns/Protocol, an interface: my.ns.Protocol. The interface will
   1.603 +  have methods corresponding to the protocol functions, and the
   1.604 +  protocol will automatically work with instances of the interface.
   1.605 +
   1.606 +  Note that you should not use this interface with deftype or
   1.607 +  reify, as they support the protocol directly:
   1.608 +
   1.609 +  (defprotocol P 
   1.610 +    (foo [this]) 
   1.611 +    (bar-me [this] [this y]))
   1.612 +
   1.613 +  (deftype Foo [a b c] 
   1.614 +   P
   1.615 +    (foo [this] a)
   1.616 +    (bar-me [this] b)
   1.617 +    (bar-me [this y] (+ c y)))
   1.618 +  
   1.619 +  (bar-me (Foo. 1 2 3) 42)
   1.620 +  => 45
   1.621 +
   1.622 +  (foo 
   1.623 +    (let [x 42]
   1.624 +      (reify P 
   1.625 +        (foo [this] 17)
   1.626 +        (bar-me [this] x)
   1.627 +        (bar-me [this y] x))))
   1.628 +  => 17"
   1.629 +  {:added "1.2"} 
   1.630 +  [name & opts+sigs]
   1.631 +  (emit-protocol name opts+sigs))
   1.632 +
   1.633 +(defn extend 
   1.634 +  "Implementations of protocol methods can be provided using the extend construct:
   1.635 +
   1.636 +  (extend AType
   1.637 +    AProtocol
   1.638 +     {:foo an-existing-fn
   1.639 +      :bar (fn [a b] ...)
   1.640 +      :baz (fn ([a]...) ([a b] ...)...)}
   1.641 +    BProtocol 
   1.642 +      {...} 
   1.643 +    ...)
   1.644 + 
   1.645 +  extend takes a type/class (or interface, see below), and one or more
   1.646 +  protocol + method map pairs. It will extend the polymorphism of the
   1.647 +  protocol's methods to call the supplied methods when an AType is
   1.648 +  provided as the first argument. 
   1.649 +
   1.650 +  Method maps are maps of the keyword-ized method names to ordinary
   1.651 +  fns. This facilitates easy reuse of existing fns and fn maps, for
   1.652 +  code reuse/mixins without derivation or composition. You can extend
   1.653 +  an interface to a protocol. This is primarily to facilitate interop
   1.654 +  with the host (e.g. Java) but opens the door to incidental multiple
   1.655 +  inheritance of implementation since a class can inherit from more
   1.656 +  than one interface, both of which extend the protocol. It is TBD how
   1.657 +  to specify which impl to use. You can extend a protocol on nil.
   1.658 +
   1.659 +  If you are supplying the definitions explicitly (i.e. not reusing
   1.660 +  exsting functions or mixin maps), you may find it more convenient to
   1.661 +  use the extend-type or extend-protocol macros.
   1.662 +
   1.663 +  Note that multiple independent extend clauses can exist for the same
   1.664 +  type, not all protocols need be defined in a single extend call.
   1.665 +
   1.666 +  See also:
   1.667 +  extends?, satisfies?, extenders"
   1.668 +  {:added "1.2"} 
   1.669 +  [atype & proto+mmaps]
   1.670 +  (doseq [[proto mmap] (partition 2 proto+mmaps)]
   1.671 +    (when-not (protocol? proto)
   1.672 +      (throw (IllegalArgumentException.
   1.673 +              (str proto " is not a protocol"))))
   1.674 +    (when (implements? proto atype)
   1.675 +      (throw (IllegalArgumentException. 
   1.676 +              (str atype " already directly implements " (:on-interface proto) " for protocol:"  
   1.677 +                   (:var proto)))))
   1.678 +    (-reset-methods (alter-var-root (:var proto) assoc-in [:impls atype] mmap))))
   1.679 +
   1.680 +(defn- emit-impl [[p fs]]
   1.681 +  [p (zipmap (map #(-> % first keyword) fs)
   1.682 +             (map #(cons 'fn (drop 1 %)) fs))])
   1.683 +
   1.684 +(defn- emit-hinted-impl [c [p fs]]
   1.685 +  (let [hint (fn [specs]
   1.686 +               (let [specs (if (vector? (first specs)) 
   1.687 +                                        (list specs) 
   1.688 +                                        specs)]
   1.689 +                 (map (fn [[[target & args] & body]]
   1.690 +                        (cons (apply vector (vary-meta target assoc :tag c) args)
   1.691 +                              body))
   1.692 +                      specs)))]
   1.693 +    [p (zipmap (map #(-> % first keyword) fs)
   1.694 +               (map #(cons 'fn (hint (drop 1 %))) fs))]))
   1.695 +
   1.696 +(defn- emit-extend-type [c specs]
   1.697 +  (let [impls (parse-impls specs)]
   1.698 +    `(extend ~c
   1.699 +             ~@(mapcat (partial emit-hinted-impl c) impls))))
   1.700 +
   1.701 +(defmacro extend-type 
   1.702 +  "A macro that expands into an extend call. Useful when you are
   1.703 +  supplying the definitions explicitly inline, extend-type
   1.704 +  automatically creates the maps required by extend.  Propagates the
   1.705 +  class as a type hint on the first argument of all fns.
   1.706 +
   1.707 +  (extend-type MyType 
   1.708 +    Countable
   1.709 +      (cnt [c] ...)
   1.710 +    Foo
   1.711 +      (bar [x y] ...)
   1.712 +      (baz ([x] ...) ([x y & zs] ...)))
   1.713 +
   1.714 +  expands into:
   1.715 +
   1.716 +  (extend MyType
   1.717 +   Countable
   1.718 +     {:cnt (fn [c] ...)}
   1.719 +   Foo
   1.720 +     {:baz (fn ([x] ...) ([x y & zs] ...))
   1.721 +      :bar (fn [x y] ...)})"
   1.722 +  {:added "1.2"} 
   1.723 +  [t & specs]
   1.724 +  (emit-extend-type t specs))
   1.725 +
   1.726 +(defn- emit-extend-protocol [p specs]
   1.727 +  (let [impls (parse-impls specs)]
   1.728 +    `(do
   1.729 +       ~@(map (fn [[t fs]]
   1.730 +                `(extend-type ~t ~p ~@fs))
   1.731 +              impls))))
   1.732 +
   1.733 +(defmacro extend-protocol 
   1.734 +  "Useful when you want to provide several implementations of the same
   1.735 +  protocol all at once. Takes a single protocol and the implementation
   1.736 +  of that protocol for one or more types. Expands into calls to
   1.737 +  extend-type:
   1.738 +
   1.739 +  (extend-protocol Protocol
   1.740 +    AType
   1.741 +      (foo [x] ...)
   1.742 +      (bar [x y] ...)
   1.743 +    BType
   1.744 +      (foo [x] ...)
   1.745 +      (bar [x y] ...)
   1.746 +    AClass
   1.747 +      (foo [x] ...)
   1.748 +      (bar [x y] ...)
   1.749 +    nil
   1.750 +      (foo [x] ...)
   1.751 +      (bar [x y] ...))
   1.752 +
   1.753 +  expands into:
   1.754 +
   1.755 +  (do
   1.756 +   (clojure.core/extend-type AType Protocol 
   1.757 +     (foo [x] ...) 
   1.758 +     (bar [x y] ...))
   1.759 +   (clojure.core/extend-type BType Protocol 
   1.760 +     (foo [x] ...) 
   1.761 +     (bar [x y] ...))
   1.762 +   (clojure.core/extend-type AClass Protocol 
   1.763 +     (foo [x] ...) 
   1.764 +     (bar [x y] ...))
   1.765 +   (clojure.core/extend-type nil Protocol 
   1.766 +     (foo [x] ...) 
   1.767 +     (bar [x y] ...)))"
   1.768 +  {:added "1.2"}
   1.769 +
   1.770 +  [p & specs]
   1.771 +  (emit-extend-protocol p specs))
   1.772 +