diff options
author | Rich Hickey <richhickey@gmail.com> | 2008-07-28 15:52:38 +0000 |
---|---|---|
committer | Rich Hickey <richhickey@gmail.com> | 2008-07-28 15:52:38 +0000 |
commit | 4b47829f59992f7de22a859fd32306bb512747de (patch) | |
tree | dc206ce19926a8fb519a67b0436e460c99d4eef8 /src | |
parent | a63f62ee1ffcab5c042d42a1dbc01e59e5ec9960 (diff) |
added prefer-method
Diffstat (limited to 'src')
-rw-r--r-- | src/clojure/boot.clj | 11 | ||||
-rw-r--r-- | src/jvm/clojure/lang/MultiFn.java | 39 |
2 files changed, 38 insertions, 12 deletions
diff --git a/src/clojure/boot.clj b/src/clojure/boot.clj index 7b515309..69cdaf21 100644 --- a/src/clojure/boot.clj +++ b/src/clojure/boot.clj @@ -927,18 +927,15 @@ [multifn dispatch-val & fn-tail] `(. ~multifn addMethod ~dispatch-val (fn ~@fn-tail))) -; `(let [pvar# (var ~multifn)] -; (. pvar# (commuteRoot (fn [#^clojure.lang.MultiFn mf#] -; (. mf# (assoc ~dispatch-val (fn ~@fn-tail)))))))) - (defmacro remove-method "Removes the method of multimethod associated with dispatch-value." [multifn dispatch-val] `(. ~multifn removeMethod ~dispatch-val)) -; `(let [pvar# (var ~multifn)] -; (. pvar# (commuteRoot (fn [#^clojure.lang.MultiFn mf#] -; (. mf# (dissoc ~dispatch-val))))))) +(defmacro prefer-method + "Causes the multimethod to prefer matches of dispatch-val-x over dispatch-val-y when there is a conflict" + [multifn dispatch-val-x dispatch-val-y] + `(. ~multifn preferMethod ~dispatch-val-x ~dispatch-val-y)) ;;;;;;;;; var stuff diff --git a/src/jvm/clojure/lang/MultiFn.java b/src/jvm/clojure/lang/MultiFn.java index e956b761..de398429 100644 --- a/src/jvm/clojure/lang/MultiFn.java +++ b/src/jvm/clojure/lang/MultiFn.java @@ -13,11 +13,13 @@ package clojure.lang; import java.util.Map; +import java.util.Collection; public class MultiFn extends AFn{ final public IFn dispatchFn; final public Object defaultDispatchVal; IPersistentMap methodTable; +IPersistentMap preferTable; IPersistentMap methodCache; Object cachedHierarchy; @@ -31,6 +33,7 @@ public MultiFn(IFn dispatchFn, Object defaultDispatchVal) throws Exception{ this.defaultDispatchVal = defaultDispatchVal; this.methodTable = PersistentHashMap.EMPTY; this.methodCache = methodTable; + this.preferTable = PersistentHashMap.EMPTY; cachedHierarchy = null; } @@ -46,6 +49,31 @@ synchronized public MultiFn removeMethod(Object dispatchVal) throws Exception{ return this; } +synchronized public MultiFn preferMethod(Object dispatchValX, Object dispatchValY){ + if(prefers(dispatchValY, dispatchValX)) + throw new IllegalStateException( + String.format("Preference conflict: %s is already preferred to %s", dispatchValY, dispatchValX)); + preferTable = preferTable.assoc(dispatchValX, RT.conj((IPersistentCollection) RT.get(preferTable, + dispatchValX, + PersistentHashSet.EMPTY), + dispatchValY)); + resetCache(); + return this; +} + +private boolean prefers(Object x, Object y){ + IPersistentSet xprefs = (IPersistentSet) preferTable.valAt(x); + return xprefs != null && xprefs.contains(y); +} + +private boolean isA(Object x, Object y) throws Exception{ + return RT.booleanCast(isa.invoke(x, y)); +} + +private boolean dominates(Object x, Object y) throws Exception{ + return prefers(x, y) || isA(x, y); +} + private IPersistentMap resetCache(){ methodCache = methodTable; cachedHierarchy = hierarchy.get(); @@ -72,14 +100,15 @@ private IFn findAndCacheBestMethod(Object dispatchVal) throws Exception{ for(Object o : methodTable) { Map.Entry e = (Map.Entry) o; - if(RT.booleanCast(isa.invoke(dispatchVal, e.getKey()))) + if(isA(dispatchVal, e.getKey())) { - if(bestEntry == null || RT.booleanCast(isa.invoke(e.getKey(), bestEntry.getKey()))) + if(bestEntry == null || dominates(e.getKey(), bestEntry.getKey())) bestEntry = e; - if(!RT.booleanCast(isa.invoke(bestEntry.getKey(), e.getKey()))) + if(!dominates(bestEntry.getKey(), e.getKey())) throw new IllegalArgumentException( - String.format("Multiple methods match dispatch value: %s -> %s and %s", - dispatchVal, e.getKey(), bestEntry.getKey())); + String.format( + "Multiple methods match dispatch value: %s -> %s and %s, and neither is preferred", + dispatchVal, e.getKey(), bestEntry.getKey())); } } if(bestEntry == null) |