On Wed, Dec 22, 2010 at 11:36 PM, Sunil S Nandihalli <sunil.nandiha...@gmail.com> wrote: > the caveat is that the order in which you specify the methods would matter > .. since that is the order in which it is going to check for the appropriate > method to call.. Just like condp again.
That seems sucky. What about adding a priority parameter to your defmethod-analogue? The predicates are kept sorted by priority. So (my-defmulti foo) (my-defmethod foo 4 (constantly true) [x y] (str "x = " x "\ny = " y "\n")) (my-defmethod foo 3 #(= %2 1) [x y] (str "y is one!\nx = " x)) (my-defmethod foo 2 #(= %1 %2) [x y] (str "x = y = " x)) (my-defmethod foo 1 #(or (> %1 5) (> %2 5) (> 1 %1) (> 1 %2)) [x y] (str "One is out of range!\n x = " x "\ny = " y "\n")) would check for out of range first, then for equality, then for the default case, despite these being defined in the reverse of that order. Of course this could be nicened up further. For example, extend the scope of the bindings to cover the predicate as well as what follows; and if the object after the bindings is not a list, if it's the value true treat it as (constantly true) and if it's a vector check it for equality against the arguments, treating any vector element equal to the clojure.core multiplication function as matching anything (so, under normal circumstances, a literal * in the vector is a wild-card): (my-defmethod foo 4 [x y] true (str "x = " x "\ny = " y "\n")) (my-defmethod foo 3 [x y] [* 1] (str "y is one!\nx = " x)) (my-defmethod foo 2 [x y] (= x y) (str "x = y = " x)) (my-defmethod foo 1 [x y] (or (> x 5) (> y 5) (> 1 x) (> 1 y)) (str "One is out of range!\n x = " x "\ny = " y "\n")) An untested implementation: (def qlzqqlzuup (Object.)) (defmacro my-defmulti [name] `(def name (let [dtable (atom (sorted-map))] (fn [& args] (if (= (first args) qlzqqlzuup) (let [[_ pri predmeth] args] (swap! dtable assoc pri predmeth)) (loop [d (seq @dtable)] (if d (let [[_ [pred meth]] (first d)] (if (apply pred args) (apply meth args) (recur (next d)))) (throw IllegalArgumentException (str "no matching method in " name " for " args))))))))) (defn wildmatch [matcher & args] (loop [m (seq matcher) a (seq args)] (if (and m a) (let [fm (first m)] (if (or (= fm *) (= fm (first a))) (recur (next m) (next a)))) true))) (defmacro my-defmethod [multifn priority bindings matcher & body] (let [pred (cond (= true matcher) `(constantly true) (vector? matcher) `(fn ~bindings (wildmatch ~matcher ~...@bindings)) true `(fn ~bindings ~matcher))] `(~multifn qlzqqlzuup ~priority [~pred (fn ~bindings ~...@body)]))) WARNING: Untested! IF it works, this implementation has three particular properties not mentioned above: 1. Defining two methods of the same multifn with the same priority isn't possible -- the later one will replace the earlier method of the same priority. 2. Vector matching matches if there are fewer arguments than the vector length, and the n arguments match against the first n elements of the vector, and if there are more arguments than the vector length, and the n vector elements match against the first n arguments, as well as if there are equal numbers of each and they match in sequence. 3. If no method matches it throws an IAE. Note also that if a method is given a vector as matcher, and that vector contains expressions, those expressions get run every time the matcher does (including in some cases where it doesn't match!); e.g. (my-defmethod foo 3 [x y] [(* bar quux) 42] (str "x is bar times quux and y is 42")) will generate a predicate test defined as (fn [x y] (wildmatch [(* bar quux) 42] x y)) and the lookup and multiplication of bar and quux will be done every time this is called. Obviously it's best if no such computation is expensive (unless it needs to be and can't be hoisted out of the defmethod because it must be run every time) or has side effects (other than temporary debugging printlns, anyway). As you can see also, the bindings are in effect where the (* bar quux) appeared, so the above equality test method could also have been written as: (my-defmethod foo 2 [x y] [y] (str "x = y = " x)) which yields a predicate defined by (fn [x y] (wildmatch [y] x y)) which in turn will simply see if y is equal to x. That makes some cases like that one nicer to write, but also means that the bindings will shadow globals of the same name in the vector, which might be a surprise some of the time; if we had a global y and expected the above to check x against that and not against the argument y we'd get a nasty, and perhaps subtle, bug. -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en