12

I'm thinking about start using (not playing with) Clojure. Are there any useful guides? I'm not asking about lein, javac or any other 'small' manual tools. I need to know how to have Java and Clojure sources in eclipse in the same project. How to make them calling each other without compilation errors? How to configure maven? How to set up fully productive development environment? Is it even possible at the moment? What plugins may be useful? Where to start?

piotrek
  • 13,982
  • 13
  • 79
  • 165

3 Answers3

15

I have a fully working production setup with Eclipse, Maven and Clojure that is working very nicely at the moment. Hopefully it is helpful as an example of a good polyglot setup within a Java IDE.

I don't use leiningen - Nothing against lein at all - it's very nice and ideal in a pure Clojure/CLI world. But I've found that pure Maven is nicer to work with in a polyglot Java+Clojure environment with an IDE since the tool integration is so much better. Also from an ecosystem / audience / community perspective if you want people from the Java world to be able to build your source you are going to cause a lot less confusion if you just use Maven directly.

Here is my setup:

  • Eclipse 4.2 as main IDE
  • Counterclockwise Eclipse plugin - very good, takes care of REPL, Clojure editing etc.
  • Maven used to manage all projects (I use the built in Eclipse Maven integration mostly, but occasionally use the CLI version as well)
  • cljunit used to enable JUnit tests to run on Clojure parts of the project
  • Github / Travis CI used for SCM and Continuous integration, accessed using the built-in EGit team provider in Eclipse

In terms of actually how I manage / set up the project itself:

  • I configure everything with Maven, using standard Maven directory layout. Polyglot Java+Clojure Projects typically have both src/main/java and src/main/clojure
  • Clojure is just a Maven dependency, like any other Java library.
  • I make the Clojure source directories into resource directories in the Maven setup. This means that the .clj files get bundled in any jars and can be loaded / run dynamically at runtime.
  • I usually make the entry point on the Java side with a public static void main(...) as usual, but call quite quickly into the Clojure code. See this blog post on calling Clojure from Java.

Finally some coding tips for polyglot Java+Clojure

  • I find that Java is better for low level data structures, libraries and algorithms, while Clojure is better for integrating things together and "glue" code.
  • Clojure calling Java is usually easier / more elegant than the other way round. Also it makes sense since you generally want the dependencies to flow that way (higher level code calling lower level code)
  • If you make all your Java classes immutable, they play very nicely in a Clojure world with minimal effort.
  • Sometimes it is worth making one or more of your Java classes implement some of the Clojure interfaces, particularly clojure.lang.IFn for example. This way your Java objects can act as first class functions in Clojure code.

Here's an example project that mixes Java and Clojure source:

I also wrote a small library (clojure-utils) that includes some example code for calling Clojure from Java, which you may find useful.

mikera
  • 105,238
  • 25
  • 256
  • 415
  • correct me if i'm wrong. in 'enlight' project java is compiled first, then clojure (using maven-clojure-plugin). clojure calls java functions but java never calls clojure. is that true? but that's not what i need. i want clojure calls java **and** java calls clojure. is there any way to compile java and clojure together? – piotrek Jan 01 '13 at 12:13
  • well you don't actually pre-compile Clojure code- it gets compiled when it is loaded at runtime. So what you really have is a Java application, which includes the `clojure.jar` runtime library and is therefore capable of compiling and running the Clojure parts of the code base. Once this is running, Clojure can call Java and vice versa just fine. – mikera Jan 01 '13 at 12:18
  • true, 'once this is running'. but the problem is compilation. when you have references from java to non-existing classes (which will be generated at runtime) then maven can't compile the project – piotrek Jan 01 '13 at 12:24
  • what do you mean by "non-existing classes"? usually Java doesn't need to know what classes Clojure generates, since they can easily be looked up at runtime. see the technique in my blog post: http://clojurefun.wordpress.com/2012/12/24/invoking-clojure-code-from-java/ – mikera Jan 01 '13 at 12:27
  • by 'non existing' i mean all those created by `:gen-class` (they don't exists before clojure:compile). i would like to use them as a regular java classes (as it would be the most convenient) instead of doing static initializations and RT... Is it even possible? i admit the way you described works but i try to make entry barrier even lower – piotrek Jan 01 '13 at 12:35
  • 1
    I must admit I've generally avoided gen-class. It means you need an extra compile step, complicates the build and also I find writing Java classes in Clojure is rather unidiomatic. If you can live without gen-class (e.g. by writing the Java objects you need in Java itself) then your life will be simpler. I think it is possible to make gen-class work with Java IDEs, but probably worth a separate question in its own right. – mikera Jan 01 '13 at 12:42
12

Despite your tone about leiningen, I recommend looking at it. Leiningen supports Java compilation, so combining java and clojure sources in one project isn't a problem.

The Counterclockwise plugin, the clojure plugin for Eclipse, can work with leiningen project files (project.clj). So within Eclipse you have dependency management and java compilation all handled for you by defining the right things in project.clj, without the need to install leiningen separately or execute commands from the command line.

In the project.clj set :java-source-paths, like for example:

:java-source-paths ["src/main/java"] 

In package src/main/java put a class Foo:

package main.java;

public class Foo {
    public static final String value = "Michiel";
}

Somewhere in a clojure source file define this function and "Michiel" will be printed when it is called:

(defn foo
  "I don't do a whole lot."
  []
  (println (main.Foo/value)))

Further reading:

Michiel Borkent
  • 34,228
  • 15
  • 86
  • 149
  • oh no, i have nothing against lein. it just looks like a manual CLI command without proper integration with IDE. are you saying that i can just install the plugin, show the path to leiningen and eclipse will not show compilation errors? and what about maven? – piotrek Dec 23 '12 at 21:35
  • @piotrek you don't have to install anything other than the counterclockwise plugin to make this work. You will have to create a project.clj yourself and then choose "Configure -> convert to Leiningen project". – Michiel Borkent Dec 23 '12 at 21:40
  • @JustinThomas What do you mean with 'the reverse'? – Michiel Borkent May 03 '16 at 22:14
  • I mean Java including clojure, but I did it successfully. – Justin Thomas May 04 '16 at 17:47
0

You could also try the framework "Funky". It will completly seperate you Clojure and Java code . Just have a look at https://github.com/windler/Funky

windler
  • 57
  • 4