On Fri Aug 3 16:50 2012, dgrnbrg wrote: > I ended up digging deep through gen-class, and I learned about an > interesting, undocumented feature that solves this problem: > > You can, in fact, overload methods of the same arity on type, and here's > how: > > Each method you define in gen-class tries to lookup a corresponding var in > the impl-ns of the form {impl-ns}/{prefix}{method-name} > > However, if the method is overloaded on type, gen-class first looks up a > var of the form {impl-ns}/{prefix}{method-name}{typesig}, and only if that > fails does it use the default var. > > typesig is constructed in the following way: > > (str (interleave (repeat \-) (map typesig-name types)) > > where types is the vector of types passed to the method declaration. > > Finally, here's a way to define typesig-name (and I'm assuming all > arguments are Classes) > > (defn typesig-name [c] > (cond (.isArray c) (str (typesig-name (.getComponentType c)) "<>") > (.isPrimitive c) (comment this should give "int", "float", > "double", "long", etc) > (.getSimpleName c))) > > If you provide vars with those names, you can overload by arity. > > To recap, these are the quirks: > 1) If you don't overload a method, you must provide the implementation in > the var of the same name. > 2) If you do overload the arity, you can optionally provide the > implementation in the specially named vars, but if they don't exist, > they'll fall back to vars of the same name. > 3) The overload vars have dash-separated type signatures included in their > name, where primitives are written like in java, arrays end in "<>", and > you only include the simple name of the classes. > > Whew... > > p.s. unfortunately, clojure still boxes the arguments into these function > no matter what. So this is a dispatch optimization, not a boxing > optimization (or, in my case, allows me to generate the correct interop > forms).
You're right. I wasn't aware of this functionality. So, my previous example can be implemented using: (defn -foo-boolean<>-boolean [_ _] "booleans") (defn -foo-char<>-char [_ _] "chars") (defn -foo-short<>-short [_ _] "shorts") (defn -foo-int<>-int [_ _] "ints") (defn -foo-long<>-long [_ _] "longs") (defn -foo-float<>-float [_ _] "floats") (defn -foo-double<>-double [_ _] "doubles") (defn -foo-String<>-String [_ _] "Strings") (defn -foo-Object<>-Object [_ _] "Objects") (defn -foo-String<>-int [_ _] "Strings + int") (defn -foo-boolean [_] "boolean") (defn -foo-char [_] "char") (defn -foo-short [_] "short") (defn -foo-int [_] "int") (defn -foo-long [_] "long") (defn -foo-float [_] "float") (defn -foo-double [_] "double") (defn -foo-String [_] "String") (defn -foo-Object [_] "Object") One interesting difference between this approach and the multimethod approach is compile-time vs runtime-time dispatching. With a multimethod, doing something like (StaticTest/foo (Long. 2)) returns "long" instead of "Object", which may or may not be desirable behaviour. On the other hand, it hides some quirks in Clojure's compile-time resolution. For example, calling (StaticTest/foo \a) actually invokes StaticTest.foo(Object) instead of StaticTest.foo(char). Thanks for the extra insight! Sincerely, Daniel
signature.asc
Description: Digital signature