Clojure embedded in a Java Application

172 views
Skip to first unread message

Eamonn

unread,
Sep 15, 2011, 5:39:10 PM9/15/11
to Clojure
Hi

I'm new to Clojure so forgive me if this is a dumb question. I want
to incorporate some Clojure into a Java application.

String rule="(str key val label)";
String str = "(ns test) " + "(defn foo [key val label] " +
rule +
")";
System.out.println(str);

Compiler.load(new StringReader(str));

// Get a reference to the foo function.
Var foo = RT.var("test", "foo");


// Call it!

Object result = foo.invoke( "hello","world","this is a test");

The code works BUT if I try to pass in a map for example like so
Object result = foo.invoke( "hello","world","#{:a 1 :b 2}");

I always get a ClassCastException. Am I doing something ridiculous
here? Is it possible to pass in a Map from the Java world into the
Clojure code?

Also any comments on using Clojure within a Java app appreciated. Is
it a good idea. I was thinking of allowing rules to be dynamically
added to fields within a web application.

Bronsa

unread,
Sep 16, 2011, 9:33:46 AM9/16/11
to clo...@googlegroups.com

The problem could be that #{} in clojure is a set literal, try using clojure.lang.PersistentHashSet/create

> --
> You received this message because you are subscribed to the Google
> Groups "Clojure" group.
> To post to this group, send email to clo...@googlegroups.com
> Note that posts from new members are moderated - please be patient with your first post.
> To unsubscribe from this group, send email to
> clojure+u...@googlegroups.com
> For more options, visit this group at
> http://groups.google.com/group/clojure?hl=en

Meikel Brandmeyer (kotarak)

unread,
Sep 16, 2011, 9:56:36 AM9/16/11
to clo...@googlegroups.com
Hi,


Am Donnerstag, 15. September 2011 23:39:10 UTC+2 schrieb Eamonn:

The code  works BUT if I try to pass in a map for example like so
Object result = foo.invoke( "hello","world","#{:a 1 :b 2}");


This does not pass a map to the function, but the string "#{:a 1 :b 2}". And from your example I don't see where the class cast exception should come from. To create a clojure map use the normal hash-map function.

Var keyword = RT.var("clojure.core", "keyword");
Var hashMap = RT.var("clojure.core", "hash-map");

foo.invoke("hello", "world", hashMap.invoke(keyword.invoke("a"), 1, keyword.invoke("b"), 2));

Hope this helps.

Sincerely
Meikel

Eamonn

unread,
Sep 17, 2011, 10:58:28 PM9/17/11
to Clojure
Hi Meikel
Thank you for your reply. Is there a way to populate the HashMap
before passing it to the invoke method

I tried the following
Var keyword = RT.var("clojure.core", "keyword");
Var hashMap = RT.var("clojure.core", "hash-map");
hashMap.invoke(keyword.invoke("a"), 1);

then I created the following function
(defn foo[key paramMap](key paramMap))

Object result=foo.invoke(keyword.invoke("a"), hashMap);

But I got null returned but when I do as you suggest
Object result=foo.invoke(keyword.invoke("a"),
hashMap.invoke(keyword.invoke("a"), 1));
I get 1 returned

Any suggestions welcomed

On Sep 16, 9:56 am, "Meikel Brandmeyer (kotarak)" <m...@kotka.de>
wrote:

Ken Wesson

unread,
Sep 17, 2011, 11:55:00 PM9/17/11
to clo...@googlegroups.com
On Sat, Sep 17, 2011 at 10:58 PM, Eamonn <odo...@gmail.com> wrote:
> Hi Meikel
> Thank you for your reply.  Is there a way to populate the HashMap
> before passing it to the invoke method

The easiest might be to just pass a map literal (in String form)
through the Clojure reader. Variable integers or other simple objects
can just be incorporated using the Java String + operator; the
concatenation will always start with a string literal such as "{" so
this will work in general. (It's likely keywords etc. will be constant
while numbers might be variable.)

--
Protege: What is this seething mass of parentheses?!
Master: Your father's Lisp REPL. This is the language of a true
hacker. Not as clumsy or random as C++; a language for a more
civilized age.

Meikel Brandmeyer

unread,
Sep 18, 2011, 3:55:19 AM9/18/11
to clo...@googlegroups.com
Hi,

Am 18.09.2011 um 04:58 schrieb Eamonn:

> I tried the following
> Var keyword = RT.var("clojure.core", "keyword");
> Var hashMap = RT.var("clojure.core", "hash-map");
> hashMap.invoke(keyword.invoke("a"), 1);
>
> then I created the following function
> (defn foo[key paramMap](key paramMap))
>
> Object result=foo.invoke(keyword.invoke("a"), hashMap);

You pass the hash-map function, not a map. Just as in clojure you have to capture the return value.

Object theMap = hashMap.invoke(keyword.invoke("a"), 1);
Object result = foo.invoke(keyword.invoke("a"), theMap);

You get caught be the keyword lookup, because keywords return nil on non-map/set objects. If you change your foo as follows, you will get 5.

(defn foo [k m] (k m 5))

> But I got null returned but when I do as you suggest
> Object result=foo.invoke(keyword.invoke("a"),
> hashMap.invoke(keyword.invoke("a"), 1));
> I get 1 returned

Here you pass the return value of the hash-map call directly. Hence it works.

Sincerely
Meikel

Meikel Brandmeyer

unread,
Sep 18, 2011, 4:06:58 AM9/18/11
to clo...@googlegroups.com

Am 18.09.2011 um 05:55 schrieb Ken Wesson:
>
> The easiest might be to just pass a map literal (in String form)
> through the Clojure reader. Variable integers or other simple objects
> can just be incorporated using the Java String + operator; the
> concatenation will always start with a string literal such as "{" so
> this will work in general. (It's likely keywords etc. will be constant
> while numbers might be variable.)

It's a complete mystery to me why people in the 21st century still promote messing around with strings as FFI when an easy and simple programmatic interface exists, which will *always* work – not only with literals the reader understands. Not to speak about quoting hell and other gotchas.

Calling this “easiest” and “will work in general” is really a joke.

Ken Wesson

unread,
Sep 18, 2011, 4:11:17 AM9/18/11
to clo...@googlegroups.com

Tell me which is simpler:

Reader.read("{:foo 1 :bar " + x + "}");

or

Var keyword = RT.var("clojure.core", "keyword");
Var hashMap = RT.var("clojure.core", "hash-map");

hashMap.invoke(keyword.invoke("foo"), 1, keyword.invoke("bar"), x);

Nobody suggested using the reader in the cases where it is actually
more compact to express using stuff like the above, by the way.

Meikel Brandmeyer

unread,
Sep 18, 2011, 4:40:52 AM9/18/11
to clo...@googlegroups.com
Hi,

Am 18.09.2011 um 10:11 schrieb Ken Wesson:

> Tell me which is simpler:
>
> Reader.read("{:foo 1 :bar " + x + "}");

String x = "13rabc";

Have fun.

> Var keyword = RT.var("clojure.core", "keyword");
> Var hashMap = RT.var("clojure.core", "hash-map");
> hashMap.invoke(keyword.invoke("foo"), 1, keyword.invoke("bar"), x);

This one is more simple. This is clojure code. And the example shows what unnecessary boilerplate is required in Java which Clojure hides from you. Everyone knowing how to call a clojure function understands this code after learning how to get a Var from a namespace and how to to invoke it.

And most importantly: it works. Also for the x above. Just like that. Nothing more involved.

For yours you have to know what x is. You have to quote it if you want to pass it as a string. You have to escape various quotes in strings. You have to know features of the reader.

> Nobody suggested using the reader in the cases where it is actually
> more compact to express using stuff like the above, by the way.

Compactness says close to nothing about simplicity.

Sincerely
Meikel

Ken Wesson

unread,
Sep 18, 2011, 6:36:35 AM9/18/11
to clo...@googlegroups.com
On Sun, Sep 18, 2011 at 4:40 AM, Meikel Brandmeyer <m...@kotka.de> wrote:
> Hi,
>
> Am 18.09.2011 um 10:11 schrieb Ken Wesson:
>
>> Tell me which is simpler:
>>
>> Reader.read("{:foo 1 :bar " + x + "}");
>
> String x = "13rabc";
>
> Have fun.

Syntactically invalid Clojure code would fail just as much in Clojure
source as it would in Java source.

If x is expected to be a string, it needs to be quoted. If it's not,
then you have a type error that will blow up at runtime, same as
usual.

>> Var keyword = RT.var("clojure.core", "keyword");
>> Var hashMap = RT.var("clojure.core", "hash-map");
>> hashMap.invoke(keyword.invoke("foo"), 1, keyword.invoke("bar"), x);
>
> This one is more simple. This is clojure code.

No, it's Java code, and butt-ugly Java code at that.

> And the example shows what unnecessary boilerplate is required in Java which Clojure hides from you. Everyone knowing how to call
> a clojure function understands this code after learning how to get a Var from a namespace and how to to invoke it.

Capability of understanding isn't the issue. Speed of understanding
is. It takes a lot longer to parse that. And it's harder to be sure it
doesn't have errors.

> For yours you have to know what x is. You have to quote it if you want to pass it as a string. You have to escape various quotes in strings. You have to know features of the reader.

Your point being? You have to know this stuff to write functioning
Clojure source code, too -- including what types you expect in what
parts of your business logic.

>> Nobody suggested using the reader in the cases where it is actually
>> more compact to express using stuff like the above, by the way.
>
> Compactness says close to nothing about simplicity.

In this case, it does.

Meikel Brandmeyer

unread,
Sep 18, 2011, 6:28:16 PM9/18/11
to clo...@googlegroups.com
Hi Eamonn,

if you find this too tedious to write and use it quite often, then there is always the possibility to hide things a little bit behind a facade.

class Clj {
static final Var seqVar = RT.var("clojure.core", "seq");
static final Var keywordVar = RT.var("clojure.core", "keyword");
static final Var hashMapVar = RT.var("clojure.core", "hash-map");

static Object seq(Object x) { return seqVar.invoke(x); }
static Object kw(String n) { return keywordVar.invoke(n); }
static Object kw(String n1, String n2) { return keywordVar.invoke(n1, n2); }
static Object hashMap(Object... args) { return hashMapVar.applyTo((clojure.lang.ISeq)seq(args)); }
}

This would make names a little bit more meaningful (less “invoke”s) for the functions you use most. And you might use your own function names. Of course this won't reach the sugar you get from Clojure itself.

theMap = Clj.hashMap(Clj.kw("a"), 1, Clj.kw("b"), 2);
foo.invoke(Clj.kw("a"), theMap);

This translates directly into:

(let [the-map (hash-map :a 1 :b 2)]
(foo :a the-map))

It should be easy to understand what's going on despite the noise. I believe Java devs are used to noise.

And FWIW: Not quoting "13rabc" by accident in the other scenario will not result in the Reader complaining but puzzled looks where this magical value came from. I personally don't particular like these moments.

My 0,02€. YMMV.

Sincerely
Meikel

Eamonn

unread,
Sep 18, 2011, 6:57:54 PM9/18/11
to Clojure
Hi Meikel,Ken
Thank you so much for taking the time to reply to my question. Meikel
Thanks for the code. I will implement as described above.

Kevin Downey

unread,
Sep 18, 2011, 7:46:29 PM9/18/11
to clo...@googlegroups.com

Just skimming this on the phone, has no one mentioned RT.map?

Luc Prefontaine

unread,
Sep 18, 2011, 11:07:14 PM9/18/11
to clo...@googlegroups.com
Hi,

You can also do it the other way around, use gen-class
and write yourself a static entry point callable from Java.

You get all the Clojure runtime init stuff done and you can define your dependencies in
the clojure file (require or use). Then you can dive into the Clojure world for as long as you need it
and return some Javaish value directly usable by your Java code.

You can write a single routine to pass clojure expressions as strings or several ones depending how your
code is structured.

Of course you need to AOT the clojure files you want to call. If you are in
a Java centric world this is not an alien concept at all.

If you need an example, look at the top of this file (gen-class) and the bottom for fns starting with -.

https://github.com/lprefontaine/Boing/blob/master/src/boing/bean.clj

The wiki describes also the Java API so you can link the Java API code versus its use.
https://github.com/lprefontaine/Boing/wiki/Using-Boing-from-java

Luc P.

--
Luc P.

================
The rabid Muppet

Sean Corfield

unread,
Sep 19, 2011, 12:44:48 AM9/19/11
to clo...@googlegroups.com
On Sun, Sep 18, 2011 at 3:40 AM, Meikel Brandmeyer <m...@kotka.de> wrote:
>> Var keyword = RT.var("clojure.core", "keyword");
>> Var hashMap = RT.var("clojure.core", "hash-map");
>> hashMap.invoke(keyword.invoke("foo"), 1, keyword.invoke("bar"), x);
>
> This one is more simple. This is clojure code.

Since I've ended up discussing it with a few people here at The
Strange Loop, I thought it opportune to provide an example of interop
into CFML (another dynamic scripting language on the JVM), using a
little bridge library I created:

// assumes x is declared with some value...
// variables.clj contains the root Clojure "binding":
var core = variables.clj.clojure.core;
var result = variables.clj.myns.foo( core.hash_map( core.keyword( "a"
), 1, core.keyword( "bar" ), x ) );

The bridge injects a specified set of namespaces into the root binding
so they can be accessed as a nested structure with a "method missing"
style mechanism used to sugar-coat the calls to invoke(). _ is
automatically translated to - in symbols.

We use a convention that namespace._sym() returns a reference to
namespace/sym whereas namespace.sym() calls (namespace/sym) - with
namespace._("sym") for symbols that aren't valid CFML identifiers.

That allows us to do stuff like:

var data = [ 1, 2, 3, 4, 5 ]; // native CFML array
var result = core.map( core.partial( core._("*"), 3 ), data );
for (var elem in result) {
// realizes result of map as each elem value is obtained
writeOutput( elem & "<br />" );
}

Whilst not as clean as pure Clojure, it allows for great interop as
well as exposing a lot of the power of Clojure directly into our CFML
code (and, yes, for anyone who thought CFML was all <tags> there is a
full "JavaScript-like" scripting language in there too!). We run all
our CFML code on the JBoss community project, Railo.

Behind the scenes it uses clojure.lang.RT and RT.var() and .invoke() /
.deref() for everything.
--
Sean A Corfield -- (904) 302-SEAN
An Architect's View -- http://corfield.org/
World Singles, LLC. -- http://worldsingles.com/
Railo Technologies, Inc. -- http://www.getrailo.com/

"Perfection is the enemy of the good."
-- Gustave Flaubert, French realist novelist (1821-1880)

Meikel Brandmeyer (kotarak)

unread,
Sep 19, 2011, 4:07:37 AM9/19/11
to clo...@googlegroups.com
Hi,

It was recommended in the past to go through the clojure side of things. That means for me to call hash-map rather than RT.map. By creating a small helper class one can get one's own Clj.map while still going through the official API as recommended. Hence I didn't mention RT.map.

Sincerely
Meikel

Laurent PETIT

unread,
Sep 19, 2011, 4:15:47 PM9/19/11
to clo...@googlegroups.com

2011/9/19 Meikel Brandmeyer (kotarak) <m...@kotka.de>

Hi,

It was recommended in the past to go through the clojure side of things. That means for me to call hash-map rather than RT.map. By creating a small helper class one can get one's own Clj.map while still going through the official API as recommended. Hence I didn't mention RT.map.

+1
 

Armando Blancas

unread,
Sep 19, 2011, 5:05:55 PM9/19/11
to Clojure
> You can also do it the other way around, use gen-class
> and write yourself a static entry point callable from Java.

That's the right way to do it, IMO. Better yet, write a Java wrapper
to offer Javadocs and to hide any interop code. That means extra work
on both ends, but in Java shops Clojure-based APIs shouldn't be subpar
or more difficult, which is the case with raw Clojure calls from Java.
Reply all
Reply to author
Forward
0 new messages