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
