In part I, I talked about Clojure and the need for a convenient “REPL”: a piece of software that lets you type input at the language engine and see its responses. And I found one in an usual place: a build tool for Clojure applications and libraries.
Bear with me; this is all relevant.
For a working software engineer, some kind of build tool is essential. There are lots of repetitive actions involved in making a piece of software ready to run, too many to do by hand every time. A build tool is a piece of software that knows how to do things with minimal effort. If I want to run my software, I tell the build tool, it recompiles everything that’s been changed, and then I can run it. If I want to run a test suite, I tell the build tool, it recompiles everything that’s changed, and runs the test suite. It’s an enormous time saver.
Now me, I’m an old school software engineer. I’ve usually used a build tool called “make“, which has only been around for decades. And make knows what to do only because I spent a great deal of time and effort writing “Makefiles” that tell it what needs to be done and how to do it.
The first part of setting up a project using make is defining your project tree: a set of folders that contain all of the files for your project, each kind of file in its place: source code over here, document files over there, and so on and so forth. Project trees are often quite complicated, with many distinct levels. Then, once you’ve got your code set up in your project tree, you can begin to think how to automate all of the tasks.
It’s a lot of work to set up, but once you’ve got it all set up, it just works—and it works well enough that when the time comes that you need to change something you’ve generally forgotten how the trick was done, and have to go read the manual again.
It seems, however, that things are simpler in the Clojure world. Many Clojure programs use a build tool called “Leiningen” which boasts that it is for “automating Clojure projects without setting your hair on fire”. And I have to say, Leiningen is something of a revelation to me.
First, the name. There is a short story called “Leiningen versus the Ants”, about a plantation owner in the Brazilian rain forest who refuses to give way before a column of army ants. I vaguely remember reading it as a kid. Of course, there’s a joke here. The make tool not being very well suited to Java development, a fellow wrote a competing tool specifically for Java called “Ant”, which I used on one project about ten or so years ago. It was much better suited for Java development, I’ll give it that, but it had many of make‘s weaknesses, including that you had to tell it everything. And the “ant file” you had to write was even uglier (to my eye) than a Makefile. I have to assume that Leiningen is named in contra-distinction to Ant.
So, joke. Ba-dum-bum.
So why is Leiningen so cool? You download one file, a command-line program called “lein“, and you do this:
% lein %
and it downloads the latest Leiningen system and makes it all ready to work. Then you do this:
% lein new app my-project ... %
where “my-project” is the name of the new application I want to write. And Leiningen creates a complete project tree, with skeleton files for the main module, the documentation, and the test suite. There’s one basic way to do it, and it does it.
The skeleton main module is a simple “Hello, world” program. If I want to compile it and run it, I just say,
% lein run ... Hello, World! %
Do you see? I didn’t need to tell it what to do. It already knows. I just drop my code files into the right spots, and everything works the way it should. I can build, run tests, build a stand-alone executable (as a “.jar” file), and all manner of things, without having to think much about how it works.
Better yet, Leiningen manages external dependencies. Most languages come with a good standard library these days, but on any large project you’re going to need to access libraries written by others. Leiningen has hooks into a variety of code repositories for Clojure and Java code around the web; I simply identify my external dependencies in the project’s Leiningen “project file”, and it will download them and do everything necessary involving them. All I need to do is write code using them.
But wait, I hear you cry—weren’t you trying to find a REPL?
Yup. Like I said, this is all relevant. If I go to my project directory in my Terminal window, I can do this:
% lein repl nREPL server started on port 58734 on host 127.0.0.1 REPL-y 0.3.0 Clojure 1.6.0 Docs: (doc function-name-here) (find-doc "part-of-name-here") Source: (source function-name-here) Javadoc: (javadoc java-object-or-class-here) Exit: Control+D or (exit) or (quit) Results: Stored in vars *1, *2, *3, an exception in *e my-project.core=> (+ 1 1) 2 my-project.core=>
I have here a full-fledged REPL that is hooked into my project. All of my code is available, and it has complete “readline” support of the kind I discussed last week, making it a pleasant environment in which to work.
In fact, there are only two rough spots. The first is that I’d rather have something more tightly integrated into my editor (though that’s a minor point); and the second I’ll talk about next time.