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

Attachment: signature.asc
Description: Digital signature

Reply via email to