Circular dependencies (Clojure and Java)

60 views
Skip to first unread message

jng27

unread,
Oct 14, 2009, 11:48:01 PM10/14/09
to Clojure
The following seems like it could be a common scenario when attempting
to re-write parts of an existing Java application in Clojure.

Let's say there exists Clojure code and Java code in the same
'project'.
The Clojure code depends on the Java code in one direction and then
the same is true in the opposite direction.
Given that compiling Java and Clojure require separate and different
compilation steps, how would circular dependencies be resolved ?
e.g. A class is defined in the Clojure code that references Java
classes(yet to be compiled) and the same is true for the Java code
referencing classes defined in Clojure(yet to be compiled). It doesn't
seem like compiling one before the other would solve this issue.

Laurent PETIT

unread,
Oct 15, 2009, 7:56:52 AM10/15/09
to clo...@googlegroups.com
Hello,

One solution I've applied in the past:

if the clojure classes depend on the java classes in the implementation and not in their interfaces ( extends, implements, methods signatures ), then you can write your gen-class with a separate namespace for the implementation of the class ( using :impl-ns ). Then you compile clojure code in 2 pass : 1/ compile the namespaces that use call gen-class and gen-interface for generating the class stubs 2/ compile java 3/ compile all the remaining clojure code.

This one is not tested but should work: if the clojure classes depend on the java classes even in the signature, then maybe it could be easier to define these classes squeletons in java, and extend them for implementation in clojure.
So 1/ create class ASqueleton java source code 2/ compile java , 3/ create gen-class that extends ASqueleton 4/ compile clojure

HTHal (*),

--
Laurent

(Hope This Helps a little)

2009/10/15 jng27 <jgra...@gmail.com>

Stuart Sierra

unread,
Oct 15, 2009, 2:48:59 PM10/15/09
to Clojure
On Oct 15, 7:56 am, Laurent PETIT <laurent.pe...@gmail.com> wrote:
> if the clojure classes depend on the java classes in the implementation and
> not in their interfaces ( extends, implements, methods signatures ), then
> you can write your gen-class with a separate namespace for the
> implementation of the class ( using :impl-ns ). Then you compile clojure
> code in 2 pass : 1/ compile the namespaces that use call gen-class and
> gen-interface for generating the class stubs 2/ compile java 3/ compile all
> the remaining clojure code.

I used this approach in the past, now I try to avoid circular
dependencies like this.

-SS

Laurent PETIT

unread,
Oct 15, 2009, 3:04:18 PM10/15/09
to clo...@googlegroups.com
2009/10/15 Stuart Sierra <the.stua...@gmail.com>


Indeed, tackling the problem at the source :)

Manuel Woelker

unread,
Oct 15, 2009, 3:17:15 PM10/15/09
to clo...@googlegroups.com
One possible solution that could be feasible is a two pass compilation
for clojure. The first compilation pass would basically just generate
the skeletons for the classes, i.e. just the method "heads" with each
method body being ignored and replaced with 'throw new
IllegalStateExccpetion("You are using first pass compilation
results")' or something along those lines. After this compilation pass
the Java classes can be compiled just fine, since all method calls can
be resolved. Finally the second pass of the clojure compilation fills
in the real method bodies, now that Java classes have been generated.
This should in theory work for most scenarios.

Laurent PETIT

unread,
Oct 15, 2009, 3:43:28 PM10/15/09
to clo...@googlegroups.com
2009/10/15 Manuel Woelker <manuel....@gmail.com>

But there is still the case of true circular dependencies between definitions, even of interfaces (let's not talk about implementation code) :

A java interface JavaInterface with a method of signature "ClojureInterface foo()"
A clojure generated interface (via gen-interface) ClojureInterface with a method of signature "JavaInterface bar()"

Compiling JavaInterface requires ClojureInterface
Compiling ClojureInterface requires JavaInterface

chess mat if not done by the same ubiquitous compiler :-(

Manuel Woelker

unread,
Oct 15, 2009, 4:05:49 PM10/15/09
to clo...@googlegroups.com
I don't think so. The clojure compiler sees the method "JavaInterface
bar()". In the first pass, it just assumes the JavaInterface to exist
(i.e. that there is at least an "interface JavaInterface {}"
somewhere). In this pass we do not care what the interface actually
looks like, so we don't need it's definiton. We can thus produce
stubbed "ClojureInterface.class". On the second pass, we expect the
Java compiler to have compile the JavaInterface (which it can because
it finds the ClojureInterface). Now we perform the usual checks,
resolve JavaInterface see that it exists and do the normal compilation
yielding the actual "ClojureInterface.class" with meaningful method
bodies, and all is well.

It seems that internally the java compilers (which are faced with the
same problem in similar situation) follow a similar strategy. Of
course they can forego the luxury of generating stubbed class files,
and keep the state in their symbol table.
(c.f. http://openjdk.java.net/groups/compiler/doc/compilation-overview/index.html
though a little light on details)

Cheers
- Manuel

Laurent PETIT

unread,
Oct 15, 2009, 4:40:32 PM10/15/09
to clo...@googlegroups.com

If you're right, that's pretty cool ! (and I'm not at all versed into this kind of stuff, so I really hope you're right and my feelings were wrong ! :)

jng27

unread,
Oct 15, 2009, 5:08:17 PM10/15/09
to Clojure


On Oct 15, 11:48 am, Stuart Sierra <the.stuart.sie...@gmail.com>
wrote:
Sure, this sometimes works for new and smaller projects.
Even then there are enough cases where circular dependencies could
easily be a justified part of a design.

However, back to how this relates to the adoption of Clojure by
industry.
Many companies trying to embrace Clojure would replace parts of their
existing Java code bases piecemeal as they come up to speed with
Clojure.
Many of them will be forced to deal with this issue. I'm wondering if
there's an elegant
solution for them to help with Clojure being a practical option in
those places ?

It probably goes without saying that the ability for Clojure to deal
with circular dependencies
and bi-directional relationships between Java and Clojure code is
quite important for it's successful adoption in the mainstream.

jng27

unread,
Oct 15, 2009, 5:14:51 PM10/15/09
to Clojure
To deal with the problem the ideal solution would be supporting
something along these lines.
It's probably inevitable that at some point the Clojure compiler would
have be modified.

On Oct 15, 12:17 pm, Manuel Woelker <manuel.woel...@gmail.com> wrote:
Reply all
Reply to author
Forward
0 new messages