3 Things Java Programmers Can Steal from Clojure

March 09, 2013

The other day I wrote about some principles that programming in Clojure makes very clear. Those principles could be applied just as well in Java, and often are. However, there are some things that make Clojure distinct.

Three of those distinctions are the way it deals with state change (using an STM), the Persistent Data Structures, and the literal syntax for data with a reader (now called edn). Diving into the source code for Clojure, I realized that these three bits were written in Java. And that means that they can be used from Java. It is certainly not as easy as using them in Clojure, but they are all three powerful enough to warrant using them if you are using Java. You simply need to add one more JAR to your project (or add a maven dependency). I have constructed a few minimal examples of their use.

1. Persistent Data Structures

Clojure comes with several powerful and fast collection classes. The interesting thing about them is that they are immutable. If you want to add an object to a list, you actually create a new list containing the old elements and the new element. Instead of using copy-on-write, it reuses most of the internal structure of the original list, so only a small number of objects need to be allocated. It turns out that this can be done very quickly, comparable to using an ArrayList.

The following example illustrates three of the more useful data structures: Vector, HashMap, and HashSet.

package persistent;

import clojure.lang.IPersistentMap;
import clojure.lang.IPersistentSet;
import clojure.lang.IPersistentVector;
import clojure.lang.PersistentHashMap;
import clojure.lang.PersistentHashSet;
import clojure.lang.PersistentVector;

public class PersistentTest {
  public static void main(String[] args) {
    IPersistentMap m = PersistentHashMap.create("abc", "xyz");
    m = m.assoc(1, 4); // add a new key/value pair
    m = m.assoc("key", "value");
    m = m.without("abc"); // remove key "abc" 
    System.out.println(m);
        
    IPersistentVector v = PersistentVector.create(1, 2, 3);
    v = v.assocN(0, "a string"); // change index 0
    v = v.cons("should be last"); // add a string at the end
    System.out.println(v);
            
    IPersistentSet s = PersistentHashSet.create("a", "b", "c");
    s = (IPersistentSet) s.cons("d"); // add d to the set
    s = (IPersistentSet) ((IPersistentMap) s).without("a"); // remove an element
    s.contains("g"); // should return false
    System.out.println(s);
  }
}

Now, it ain't pretty. But it's actually no worse than quite a few native Java libraries I've seen. There may be a better way to do this, but this one works.

2. Software Transactional Memory

Clojure uses Multiversion concurrency control to provide a safe way to manage concurrent access to state shared between threads. In Clojure, they are called refs. I won't go very deep into how it works. Suffice it to say that Clojure refs gives you non-blocking reads and transactional updates without having to do locking yourself. There are two caveats: 1 is that the value you give to the ref has to be immutable. 2 is that you should not perform IO (or perform any mutation) inside of the transaction.

package stm;

import java.util.concurrent.Callable;

import clojure.lang.LockingTransaction;
import clojure.lang.Ref;

public class STMTest {
  public static void main(String[] args) {
    // final needed to be used in anonymous class
    final Ref r = new Ref(1);
    final Ref s = new Ref(5);

    try {
      // run this in a transaction
      // don't do IO inside
      LockingTransaction.runInTransaction(
        new Callable<Object>() {
          public Object call(){
            s.set((Integer)r.deref() + 10);
            r.set(2);
            return null;
          }
        }
      );
    } catch (Exception e) {
      e.printStackTrace();
    }
        
    System.out.println(r.deref());
    System.out.println(s.deref());
  }
}

3. Extensible Data Notation

With Clojure 1.5, edn has become a standard part of the language. Edn is like an extensible JSON where the keys of objects can be any value (not just strings). It is based on the Clojure literal syntax, much in the same way that JSON is based on Javascript literal syntax. It is a nice way to serialize data. And since you already have the JAR in your project, it's a no brainer to use it.

package edn;

import java.io.PushbackReader;
import java.io.StringReader;

import clojure.lang.EdnReader;
import clojure.lang.PersistentHashMap;

public class EDNTest {
  public static void main(String[] args) {
    // reading from a string
    System.out.println(
      EdnReader.readString("{\"x\" 1 \"y\" 2}", PersistentHashMap.EMPTY));

    // reading from a Reader
    // really, you can use any Reader wrapped in a PushbackReader
    System.out.println(
      EdnReader.read(new PushbackReader(new StringReader("#{10 2 3}")),
                     PersistentHashMap.EMPTY));
  }
}
Learn Functional Programming using Clojure with screencasts, visual aids, and interactive exercises
Learn more

You might also like

Pre-conj Prep: Brian Goetz

September 28, 2014

Talk: Keynote

Background

Brian Goetz's talk at the conj is the keynote, which doesn't have a description (which is common with keynotes). I would only be guessing if I said what the talk was about. But here are the things that I wouldn't be surprised to hear in a keynote by the Java Language Architect at a Clojure conference:

Whatever he talks about, it will be important.

Why it matters

Rich Hickey made the decision to design Clojure as a hosted language. Clojure is tied to the JVM. Also, JVM languages are gaining popularity, and they obviously must play a part in Oracle's strategy. This talk has the potential to set the tone for Java's interaction with Clojure in the future.

About Brian Goetz

Twitter

Brian Goetz is quite an important figure in the Java world. He's the Java Language Architect at Oracle (the company that produces Java). He was instrumental in bringing lambdas to Java 8. The book he wrote, Java Concurrency in Practice is one of the books on Rich Hickey's reading list.

He's been giving a talk recently called Lambda: A Peek Under the Hood about the design decisions and how they are finally implemented in Java Bytecode.


This post is one of a series called Pre-conj Prep, which originally was published by email. It's all about getting ready for the upcoming Clojure/conj, organized by Cognitect. Conferences are ongoing conversations and explorations. Speakers discuss trends, best practices, and the future by drawing on the rich context built up in past conferences and other media.

That rich context is what Pre-conj Prep is about. I want to enhance everyone's experience at the conj by surfacing that context. With just a little homework, we can be better prepared to understand and enjoy the talks and the hallway conversations, as well as the beautiful venue and city of Washington, DC.

Clojure/conj is a conference organized and hosted by Cognitect. This information is in no way official. It is not sponsored by nor affiliated with Clojure/conj or Cognitect. It is simply me curating and organizing public information about the conference.

You might also like