On Mar 31, 3:49 pm, Rich Hickey <
richhic...@gmail.com> wrote:
> On Mar 30, 11:17 pm, Arto Bendiken <
arto.bendi...@gmail.com> wrote:
>
> > This library defines the procedures 'java/new' and 'java/.', allowing
> > me to shorten the aforementioned 'locale' function down to:
>
> > (defn locale [& args]
> > (apply java/new 'java.util.Locale (map str args)))
>
> I don't recommend this. None of your examples use import, but it will
> get rid of the package prefixes. If it is still that important to say
> (locale lang country) rather than (new Locale lang country), your
> first (overloaded) formulation is far better. In both cases the result
> of your function won't have a type known to the compiler unless you
> add type hints, whereas it will be known if you call new, and all
> subsequent use of the resulting object will be non-reflective.
OK, good point.
> More generally, I recommend to everyone to avoid the tendency to try
> to hide Java. There are umpteen thousand Java methods - I'd hate to
> see umpteen thousand Clojure functions written just to eliminate dot/
> new or uppercase letters. There's no significant benefit and some
> tangible costs - in the ability of dev environments to provide type-
> based assistance/Javadoc help/completion, and in the ability of the
> compiler to optimize.
>
> Never say never, of course, but the emphasis should be on leveraging
> Java libs, not hiding/wrapping them, and on making their direct use
> succinct and easy. I'm not saying I've got that optimal yet, but it's
> a better place to focus IMO than wrapper generation.
Hmm :-) The UUIDs and Locales happened to be very straightforward
examples, which is why I asked about them. Arguably they don't indeed
further benefit from wrapping, especially using 'import' and the nifty
lambda syntax you demonstrated.
However, for my other printf/sprintf example, there are several lines
of code involved in getting this functionality going - I certainly
don't want to be writing those cumbersome lines by hand every time I
need formatted output, hence the wrappers.
In general, there are sane APIs and then there are not-so-sane APIs. I
spent most of yesterday figuring out and playing with the Java
libraries that I'd need in order to utilize Clojure for my work. From
the looks of it, all the libraries I need are available (no surprise),
but their APIs tend to fall into the latter category (not so sane,
from a Lisper's point of view).
For instance, I work with RDF a lot, so let's take the Jena [1]
library as an example. Jena is, I'm told, the most widely-used and
comprehensive RDF/SPARQL library for Java. But being used to elegant,
sane APIs from the likes of Ruby, Scheme or Python, I really can't
describe Jena's API with any term more charitable than a clusterfuck.
There is no way that I want to use Jena directly on a daily basis; it
would take the fun out of Clojure, plus simply make for a whole lot of
verbose coding. Yet the library provides all the _functionality_, per
se, that I need. Hence, if I will use it, I will first want to wrap
it.
While Clojure could perhaps still improve with e.g. handling varargs
and autoboxing number types, I don't think there's anything further
Clojure-the-language can provide to make up for the design
deficiencies of third-party libraries; it's just a matter of manually
figuring out sane high-level Lispy/Clojuresque wrappers to make them
usable.
To take another example perhaps closer to your heart, I was reading up
on the ASM bytecode generation library [2] you use. Again, while
there's no doubt all the functionality one could desire is in there, I
have to say (with the caveat that this is based on reading just the
asm-guide.pdf) that it looks like a somewhat painful API to use from a
Lispy point of view, what with the explicit visitor pattern and all.
To sum it up, I'm interested in using Lisp, not Java. If I'm to be
using Clojure, I want to eventually reach a point where I don't need
to keep the JDK javadoc open in order to be able to do stuff, and the
fewer times I need to see any 'import java...' in my programs, the
better. Hence my first order of business now being creating interfaces
and wrappers in order to assemble something resembling a Lispy
standard library catering to my requirements.
> > While at it, I implemented some convenience stuff that became possible
> > with these forms. First, 'java/method' is a higher-order function that
> > is nifty for quickly making wrappers of standard library methods in a
> > fully declarative style:
>
> > ...
>
> If you must have wrappers, it's better to use the existing facilities:
>
> (def uuid #(. java.util.UUID (randomUUID)))
>
> (def url-encode #(. java.net.URLEncoder (encode %)))
>
> These versions will be much faster, and are as succinct. Although I
> must say, while experimenting with your stuff I found the eval calls
> to be surprisingly fast, but I can't promise they will always be so.
Excellent, thanks - this is certainly succinct enough, at least for
the non-variadic function cases.
> Again though, if you were to say:
>
> (let [id (uuid)]
>
> the compiler knows nothing about the type of id, vs:
>
> (let [id (. UUID (randomUUID))]
>
> where it knows that id is a UUID.
OK - but just to make sure: presumably the type information _is_
preserved if I wrap it using your succinct 'uuid' example from above?
> > Also, 'java/method*' is a variant of the previous that also handles
> > calling variadic Java functions based on what I learned today in this
> > thread:
>
> > (def format-xy
> > (java/method* (new java.util.Formatter) 'format "x=%d y=%d\n"))
>
> > (format-xy (int 123) (int 456))
> > => x=123 y=456
>
> Support for auto-creating the array for Java 'variadic' calls is on my
> todo list. apply ./new are interesting concepts I'll have to think
> about.
Sounds good!
I actually ran into quite a bit further trouble yesterday with
invoking variadic functions, even after I got 'printf' working; most
often, I'm getting ClassCastExceptions and 'IllegalArgumentException:
No matching method found' for (seemingly) no discernible reason.
These problems were with non-JDK libraries, so I'll try and reproduce
them with some variadic function in the JDK for easier
troubleshooting, and then post back to the group.
I'm sure it's just my ineptitude, but certainly, some built-in varargs
support would be superb; or perhaps even more importantly, a script to
take a Java class and produce the Clojure code which would
successfully invoke its methods. Could be useful for unit testing
purposes, too - hmm, guess I better look into the Reflection API.
Arto
[1]
http://jena.sourceforge.net/
[2]
http://asm.objectweb.org/