view src/clojure/lang/MultiFn.java @ 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 source
1 /**
2 * Copyright (c) Rich Hickey. All rights reserved.
3 * The use and distribution terms for this software are covered by the
4 * Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
5 * which can be found in the file epl-v10.html at the root of this distribution.
6 * By using this software in any fashion, you are agreeing to be bound by
7 * the terms of this license.
8 * You must not remove this notice, or any other, from this software.
9 **/
11 /* rich Sep 13, 2007 */
13 package clojure.lang;
15 import java.util.Map;
17 public class MultiFn extends AFn{
18 final public IFn dispatchFn;
19 final public Object defaultDispatchVal;
20 final public IRef hierarchy;
21 final String name;
22 IPersistentMap methodTable;
23 IPersistentMap preferTable;
24 IPersistentMap methodCache;
25 Object cachedHierarchy;
27 static final Var assoc = RT.var("clojure.core", "assoc");
28 static final Var dissoc = RT.var("clojure.core", "dissoc");
29 static final Var isa = RT.var("clojure.core", "isa?");
30 static final Var parents = RT.var("clojure.core", "parents");
32 public MultiFn(String name, IFn dispatchFn, Object defaultDispatchVal, IRef hierarchy) throws Exception{
33 this.name = name;
34 this.dispatchFn = dispatchFn;
35 this.defaultDispatchVal = defaultDispatchVal;
36 this.methodTable = PersistentHashMap.EMPTY;
37 this.methodCache = getMethodTable();
38 this.preferTable = PersistentHashMap.EMPTY;
39 this.hierarchy = hierarchy;
40 cachedHierarchy = null;
41 }
43 synchronized public MultiFn reset(){
44 methodTable = methodCache = preferTable = PersistentHashMap.EMPTY;
45 cachedHierarchy = null;
46 return this;
47 }
49 synchronized public MultiFn addMethod(Object dispatchVal, IFn method) throws Exception{
50 methodTable = getMethodTable().assoc(dispatchVal, method);
51 resetCache();
52 return this;
53 }
55 synchronized public MultiFn removeMethod(Object dispatchVal) throws Exception{
56 methodTable = getMethodTable().without(dispatchVal);
57 resetCache();
58 return this;
59 }
61 synchronized public MultiFn preferMethod(Object dispatchValX, Object dispatchValY) throws Exception{
62 if(prefers(dispatchValY, dispatchValX))
63 throw new IllegalStateException(
64 String.format("Preference conflict in multimethod '%s': %s is already preferred to %s",
65 name, dispatchValY, dispatchValX));
66 preferTable = getPreferTable().assoc(dispatchValX, RT.conj((IPersistentCollection) RT.get(getPreferTable(),
67 dispatchValX,
68 PersistentHashSet.EMPTY),
69 dispatchValY));
70 resetCache();
71 return this;
72 }
74 private boolean prefers(Object x, Object y) throws Exception{
75 IPersistentSet xprefs = (IPersistentSet) getPreferTable().valAt(x);
76 if(xprefs != null && xprefs.contains(y))
77 return true;
78 for(ISeq ps = RT.seq(parents.invoke(y)); ps != null; ps = ps.next())
79 {
80 if(prefers(x, ps.first()))
81 return true;
82 }
83 for(ISeq ps = RT.seq(parents.invoke(x)); ps != null; ps = ps.next())
84 {
85 if(prefers(ps.first(), y))
86 return true;
87 }
88 return false;
89 }
91 private boolean isA(Object x, Object y) throws Exception{
92 return RT.booleanCast(isa.invoke(hierarchy.deref(), x, y));
93 }
95 private boolean dominates(Object x, Object y) throws Exception{
96 return prefers(x, y) || isA(x, y);
97 }
99 private IPersistentMap resetCache() throws Exception{
100 methodCache = getMethodTable();
101 cachedHierarchy = hierarchy.deref();
102 return methodCache;
103 }
105 synchronized public IFn getMethod(Object dispatchVal) throws Exception{
106 if(cachedHierarchy != hierarchy.deref())
107 resetCache();
108 IFn targetFn = (IFn) methodCache.valAt(dispatchVal);
109 if(targetFn != null)
110 return targetFn;
111 targetFn = findAndCacheBestMethod(dispatchVal);
112 if(targetFn != null)
113 return targetFn;
114 targetFn = (IFn) getMethodTable().valAt(defaultDispatchVal);
115 return targetFn;
116 }
118 private IFn getFn(Object dispatchVal) throws Exception{
119 IFn targetFn = getMethod(dispatchVal);
120 if(targetFn == null)
121 throw new IllegalArgumentException(String.format("No method in multimethod '%s' for dispatch value: %s",
122 name, dispatchVal));
123 return targetFn;
124 }
126 private IFn findAndCacheBestMethod(Object dispatchVal) throws Exception{
127 Map.Entry bestEntry = null;
128 for(Object o : getMethodTable())
129 {
130 Map.Entry e = (Map.Entry) o;
131 if(isA(dispatchVal, e.getKey()))
132 {
133 if(bestEntry == null || dominates(e.getKey(), bestEntry.getKey()))
134 bestEntry = e;
135 if(!dominates(bestEntry.getKey(), e.getKey()))
136 throw new IllegalArgumentException(
137 String.format(
138 "Multiple methods in multimethod '%s' match dispatch value: %s -> %s and %s, and neither is preferred",
139 name, dispatchVal, e.getKey(), bestEntry.getKey()));
140 }
141 }
142 if(bestEntry == null)
143 return null;
144 //ensure basis has stayed stable throughout, else redo
145 if(cachedHierarchy == hierarchy.deref())
146 {
147 //place in cache
148 methodCache = methodCache.assoc(dispatchVal, bestEntry.getValue());
149 return (IFn) bestEntry.getValue();
150 }
151 else
152 {
153 resetCache();
154 return findAndCacheBestMethod(dispatchVal);
155 }
156 }
158 public Object invoke() throws Exception{
159 return getFn(dispatchFn.invoke()).invoke();
160 }
162 public Object invoke(Object arg1) throws Exception{
163 return getFn(dispatchFn.invoke(arg1)).invoke(arg1);
164 }
166 public Object invoke(Object arg1, Object arg2) throws Exception{
167 return getFn(dispatchFn.invoke(arg1, arg2)).invoke(arg1, arg2);
168 }
170 public Object invoke(Object arg1, Object arg2, Object arg3) throws Exception{
171 return getFn(dispatchFn.invoke(arg1, arg2, arg3)).invoke(arg1, arg2, arg3);
172 }
174 public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4) throws Exception{
175 return getFn(dispatchFn.invoke(arg1, arg2, arg3, arg4)).invoke(arg1, arg2, arg3, arg4);
176 }
178 public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5) throws Exception{
179 return getFn(dispatchFn.invoke(arg1, arg2, arg3, arg4, arg5)).invoke(arg1, arg2, arg3, arg4, arg5);
180 }
182 public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6) throws Exception{
183 return getFn(dispatchFn.invoke(arg1, arg2, arg3, arg4, arg5, arg6)).invoke(arg1, arg2, arg3, arg4, arg5, arg6);
184 }
186 public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7)
187 throws Exception{
188 return getFn(dispatchFn.invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7))
189 .invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7);
190 }
192 public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
193 Object arg8) throws Exception{
194 return getFn(dispatchFn.invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8)).
195 invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8);
196 }
198 public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
199 Object arg8, Object arg9) throws Exception{
200 return getFn(dispatchFn.invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9)).
201 invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9);
202 }
204 public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
205 Object arg8, Object arg9, Object arg10) throws Exception{
206 return getFn(dispatchFn.invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10)).
207 invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10);
208 }
210 public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
211 Object arg8, Object arg9, Object arg10, Object arg11) throws Exception{
212 return getFn(dispatchFn.invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11)).
213 invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11);
214 }
216 public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
217 Object arg8, Object arg9, Object arg10, Object arg11, Object arg12) throws Exception{
218 return getFn(dispatchFn.invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12)).
219 invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12);
220 }
222 public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
223 Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13) throws Exception{
224 return getFn(dispatchFn.invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13)).
225 invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13);
226 }
228 public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
229 Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14)
230 throws Exception{
231 return getFn(
232 dispatchFn.invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14)).
233 invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14);
234 }
236 public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
237 Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
238 Object arg15) throws Exception{
239 return getFn(
240 dispatchFn.invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14,
241 arg15))
242 .invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15);
243 }
245 public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
246 Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
247 Object arg15, Object arg16) throws Exception{
248 return getFn(
249 dispatchFn.invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14,
250 arg15, arg16))
251 .invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14,
252 arg15, arg16);
253 }
255 public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
256 Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
257 Object arg15, Object arg16, Object arg17) throws Exception{
258 return getFn(
259 dispatchFn.invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14,
260 arg15, arg16, arg17))
261 .invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14,
262 arg15, arg16, arg17);
263 }
265 public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
266 Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
267 Object arg15, Object arg16, Object arg17, Object arg18) throws Exception{
268 return getFn(
269 dispatchFn.invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14,
270 arg15, arg16, arg17, arg18)).
271 invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14,
272 arg15, arg16, arg17, arg18);
273 }
275 public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
276 Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
277 Object arg15, Object arg16, Object arg17, Object arg18, Object arg19) throws Exception{
278 return getFn(
279 dispatchFn.invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14,
280 arg15, arg16, arg17, arg18, arg19)).
281 invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14,
282 arg15, arg16, arg17, arg18, arg19);
283 }
285 public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
286 Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
287 Object arg15, Object arg16, Object arg17, Object arg18, Object arg19, Object arg20)
288 throws Exception{
289 return getFn(
290 dispatchFn.invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14,
291 arg15, arg16, arg17, arg18, arg19, arg20)).
292 invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14,
293 arg15, arg16, arg17, arg18, arg19, arg20);
294 }
296 public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
297 Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
298 Object arg15, Object arg16, Object arg17, Object arg18, Object arg19, Object arg20, Object... args)
299 throws Exception{
300 return getFn(
301 dispatchFn.invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14,
302 arg15, arg16, arg17, arg18, arg19, arg20, args)).
303 invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14,
304 arg15, arg16, arg17, arg18, arg19, arg20, args);
305 }
307 public IPersistentMap getMethodTable() {
308 return methodTable;
309 }
311 public IPersistentMap getPreferTable() {
312 return preferTable;
313 }
314 }