summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorRich Hickey <richhickey@gmail.com>2008-07-28 15:52:38 +0000
committerRich Hickey <richhickey@gmail.com>2008-07-28 15:52:38 +0000
commit4b47829f59992f7de22a859fd32306bb512747de (patch)
treedc206ce19926a8fb519a67b0436e460c99d4eef8 /src
parenta63f62ee1ffcab5c042d42a1dbc01e59e5ec9960 (diff)
added prefer-method
Diffstat (limited to 'src')
-rw-r--r--src/clojure/boot.clj11
-rw-r--r--src/jvm/clojure/lang/MultiFn.java39
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)