What are some useful Clojure macros

Why Clojure?

01/18/2013 Permalink For the last six months I have been using the JVM-based programming language Clojure in my sparse evening free time. If you don't want to look far to get a reasonable outline, you can read the introduction by Mark Volkmann.

I had already dealt with Groovy and Scala for the past few years and looked at the language features based on the books by Dierk König and Martin Odersky, but without trying to try anything other than examples. Then, over the past year, I've had reports of promising features in Clojure from two completely different people. The strangeness of Clojure programs compared to C syntax-based OO languages ​​such as Java, C # or Scala was the decisive factor: I want to learn that properly.

In the meantime I have implemented non-trivial parts of a rich client framework (including a GUI DSL, data binding, validation, unchangeable controller), a domain that seems unsuitable for Clojure at first glance, since a rich client contains a vast amount of changeable status. But it works without any special tricks and with far less brainpower than the comparable Java implementation that I wrote for one of our customers in the last 18 months.

The excellent book Clojure Programming tries to prepare the reader to begin with: 'Clojure demands that you raise your game, and pays you back for doing so.' That hits the bull's eye. It is a challenge like the march to a summit. And then you see the world from 'up there' in a different way. It changes the perspective. And makes me question a lot of what I thought was 'okay' over the past few years.

OOP as an obstacle
I now see OOP critically and no longer consider it part of the solution ... but part of the problem:

We create new data structures with the associated behavior in OO programs. The instances of the resulting classes are generally not uniform in the sense that I can apply general functions to them. To solve problems in Java in general, I have to do the following, for example:

  • Use reflection extensively, thereby undermining the security of the static type system. Interestingly, all advanced Java frameworks like the Spring DI Framework, Tapestry 5, Hibernate, Dozer, etc. only work with reflection.
  • Introduce a certain similarity by means of interfaces, and where I am not master of the classes, create adapters that I have to instantiate at a suitable point.
If I don't want to go to this effort, all I have left is the endless manual programming of always similar but never the same procedures, which in the end result in the technical functionality.

Classes contain extensive sets of changeable, mostly private attributes, which, however, can mostly be rewritten by appropriate setters. There are no guarantees here regarding the condition of an object. In principle, every part of the system can change its state and that, to the regret of concurrent programs, at any time. The concepts of identity and state are mixed up. It is not for nothing good programming style in Java and Scala to use immutable objects when somehow possible.

In the backends of typical enterprise systems, there are predominantly no OO programs in the sense of the inventors, but stateless functions that operate on data structures that were written in an OO language. OOP is essentially useless here.

Today's corporate applications can only be created economically by reusing as much as possible from the ecosystem of a programming language. In other words, nobody would build an enterprise Java system today that didn't use at least a dozen third-party libraries. However, OOP assumes that a class is a useful, self-contained implementation of accountability. If I am not the owner of the class, e.g. because it comes from a third-party library, then today I can use inheritance or the decorator pattern in Java to get an extended variant of the class. Both require that I can control how instances arise, which is not always given. Of course, I can alternatively create static methods or 'service' classes that create a foreign class additional functionality on the publicly accessible parts. In order to eliminate the syntactic break in use, languages ​​such as Xtend or C # offer extensions. Extensions seem to me like an implicit admission that classes are i.a. cannot conclusively be provided with sufficient functionality by the author. And I conclude from this: Classes as the most important linguistic tool for achieving the great asset of 'reuse' are obviously not sufficient, but often force detours, which are then referred to as design patterns.

Is Clojure doing everything right?
I do not know. But many features of the language specifically eliminate the fundamental weaknesses of OOP. This manifests itself in the fact that Clojure programs only have a fraction of the number of lines of functionally identical Java programs. Many problems that we solve in OO languages ​​with the help of dependency injection, AOP, reflection, annotations and anonymous inner classes, almost completely disappear.

Where MDSD processes are used today with growing enthusiasm for the secure implementation of enterprise architectures, the Clojure (and all Lisp languages) own macro system can be used with justifiable compromises in comfort and tooling. Since macros are part of Clojure, no adaptation of the build, no explicit generator construction with strange tools, no configuration of compiler plugins and no binding to an IDE like Eclipse is necessary. This mainly affects the head: mini software generators that help avoid writing boilerplate code are only five to ten lines away. As a developer, I don't think about 'building a generator' with all its consequences, but create the necessary abbreviation immediately.

I could add new protocols to existing types, multi-methods, cycle-free namespaces, REPL and interactive programming, homoiconicity with a dozen of features (destructuring, persistent data structures, preconditions and postconditions, software transactional memory, higher-order functions, existing types , Metadata, keywords), but I also want to list the disadvantages that I see at Clojure today:

  • Everything that is running on the JVM today is relatively easy to access from Clojure. In this respect, the complete Java ecosystem of third-party libraries is available. But: some of these libraries do not conform to the Clojure philosophy. They will only be used where there is no adequate Clojure counterpart. These counterparts are currently being developed, but the number of features is not yet comparable.
  • With the code of statically typed languages ​​such as Java, C # or Scala, IDEs can offer significantly better code completion and make navigation through a code base extremely easier. Due to type inference, it is not even necessary in Scala or Xtend to always write down all types. Clojure does, of course, not write types here (except for performance reasons). The tools (plugins for Emacs, Eclipse or IntelliJ) do not offer the same convenience as is available in Java. Clojure makes up for a lot with its strong standardization in code, the REPL process and the powerful functions. You gain time that you lose here and there due to the weak tooling.
  • Figuratively speaking, I can travel a kilometer in Java without unit tests before I feel any problems. In a dynamically typed language, it hurts after just 150 meters. In order to develop robust, changeable software, I have to write unit tests in both worlds, so Clojure is not really worse off here, even if programming without static types feels like cycling without training wheels: at first you are unsure, but in the end you become safe and much faster.
  • Clojure is a young programming language, and functional programming is nowhere near as widespread as imperative programming (with or without OO). Of course, that's a chicken-and-egg problem that Java also faced. But the cognitive path from OOP to FP seems further to me than from C ++ to Java. I believe that many developers would be more productive with Clojure than with Java if they recognized the eternal detours in Java as such and knew the alternatives in Clojure. So I have great hope. But trends in software engineering are more like fashions; they rarely arise from the pursuit of better methods and technologies.
  • There are still 'experts' today who complain about the slowness of Java. You are right to a certain extent, but nobody is interested in enterprise IT anymore. Performance is wasted 1000 times in other places. The Clojure compiler produces bytecode, so the same conditions apply. However, persistent data structures, multi-methods and the generous use of maps certainly cost a few clock cycles more. I assume that the same 'experts' are groaning again, and that this fact is just as uninteresting after a year.

I consider Clojure to be a productivity booster in project teams of experienced, quality-conscious developers once the first hurdles have been overcome.

Developers who are unable to really understand what they are working on will not have much luck with Clojure. Frameworks like Hibernate or JSF that try to hide the actual world (DB relations or HTTP / HTML / CSS) do not really fit into the Clojure worldview. So it is not possible without profound know-how about the area of ​​application. Clojure will probably never be a programming language that somehow plays into the hands of Klickibunti programming.

Clojure is definitely a safe bet if you want to improve your skills and gain a much broader perspective on programming.