Chapter 12. Java.next

This chapter covers

  • Generating objects on the fly with proxy
  • Clojure gen-class and GUI programming
  • Clojure’s relationship to Java arrays
  • Interfaces that all Clojure functions implement
  • Using Clojure data structures in Java APIs
  • Using definterface
  • Exceptions

Regardless of your views on the Java language, it’s difficult to deny that the JVM is a stellar piece of software. The confluence of the just-in-time (JIT) compiler, garbage collection, HotSpot, and the flexible bytecode have created an environment in which many programmers have chosen to grow their alternative programming languages. Additionally, the deluge of library options hosted on the JVM further makes the JVM the language target of choice. From Clojure to Groovy to Scala to Fantom to Frink to Rhino to Jess to JRuby to Jython, there seems to be no lack of options for the enthusiastic polyglot programmer. We may soon see job listings for “JVM programmers.” But where does that leave Java the programming language?

Java the language isn’t dead.

The JVM is optimized for running Java bytecode, and only recently[1] have Java.next languages been a consideration. You may ask yourself whether JVM bytecode is equivalent to Java source code, and the answer is no. Instead, languages such as Clojure and Scala compile directly to bytecode and can access Java compiled libraries as needed. Because of their reliance on the JVM as the runtime environment, Clojure and the other Java.next languages are fundamentally constrained by the limitations of the JVM. The limitations of the JVM as defined by the limitations of the Java language specification set the beat by which the Java.next languages dance. Java isn’t dead; it’s alive and well, and it runs the show.

1 You can find more details in JSR-000292, “Supporting Dynamically Typed Languages on the Java Platform,” http://mng.bz/174x.

The Java.Next Mantra

The apprentice avoids all use of Java classes. The journeyman embraces Java classes. The master knows which classes to embrace and which to avoid.

An expert understanding of the Java Virtual Machine isn’t required for writing powerful applications in Clojure, but it will help when issues stemming from host limitations arise. Fortunately, Clojure does a good job of mitigating many of the limitations inherent in its host, but some are too deeply embedded in the fibers of the JVM to avoid. Clojure provides a specific set of interoperability tools: gen-class, proxy, definterface, its exceptions facility, and a host of array functions. We’ll touch on each of these in turn, but we’ll begin with the creation of anonymous objects using proxy.

12.1. Generating objects on the fly with proxy

There’s a saying in the Clojure community that Clojure does Java better than Java (Halloway 2009). This is a bold statement, but not one without merit, as we’ll show throughout this chapter. Java programmers are accustomed to drawing a severe distinction between development time and runtime. Using Clojure’s proxy feature allows you to blur this distinction.

Clojure Aphorism

Many software projects require a lot of planning because their implementation languages don’t foster change. Clojure makes it a lot easier to plan for change.

Clojure’s proxy mechanism is meant strictly for interoperability purposes. In section 9.3, we discuss how reify is intended to realize a single instance of a type, protocol, or interface—in other words, abstractions. But when dealing with Java libraries, you’re at times required to extend concrete classes, and it’s in this circumstance where proxy shines. Be aware that by using proxy, you bring a lot of Java’s semantics into your Clojure programs. Although extending concrete classes is seen often in Java, doing so in Clojure is considered poor design,[2] leading to fragility, and should therefore be restricted to those instances where interoperability demands it.

2 To be fair, it should be noted that many Java programmers think this also.

12.1.1. A simple dynamic web service

Using Clojure breaks the ponderous code/compile/run development cycle by adding an element of dynamism into the fold. Take for example a scenario where you want to develop a web service using an existing Java 1.5+ API.

Listing 12.1. Importing Java classes to create a simple, dynamic web server

Throughout this book we’ve mostly glossed over namespace declarations, but this one deserves a moment of reflection. First, Clojure provides a suite of I/O functions in the clojure.java.io namespace, of which you’ll use a couple. There are various Java-related namespaces under the higher-level clojure.java heading, including the aforementioned I/O, web-browser launching, Javadoc viewing, and shell execution functions. Of these, the functions in the clojure.java.io namespace are most widely used; and this is where we’ll spend some of our focus. For now, here’s the kernel of the example dynamic web server:

The respond function is the bottom HTTP response mechanism responsible for pushing a bunch of bytes across the wire. For the sake of expediency, in this case you always return the HTTP OK (number 200). If you chose to pursue this code further, you might want a richer return code system. Regardless, let’s continue by implementing a function to create a server instance using the com.sun.net.httpserver.HttpServe class.

Listing 12.2. Simplest possible web server using Java

The new-server function takes an HTTP port, the URL path, and a handler, used to build the response strings fed to the client via respond. The HttpServer instance returned from new-server has a handle method that will, for any given path for a request, delegate down to the given handler instance. As a first example, here is a simple handler to build a static message by using Clojure’s proxy to extend the Http-Handler class.

Listing 12.3. Defining a default web handler using proxy

The proxied HttpHandler instance returned by default-handler returns some closed-over text. Just like a function/closure, a Clojure proxy captures the bindings available during its creation. You take advantage of the closure nature of proxies by capturing the text given and returning a handler that uses the text as its response message:

(def server
  (new-server
    8123
    "/joy/hello"
    (default-handler "Hello Cleveland")))

After entering the code in listing 12.3, you should see the message “Hello Cleveland” in your web browser at address http://localhost:8123/joy/hello. This is only marginally interesting, especially because the source is organized in a way that doesn’t take advantage of Clojure’s flexibility. So, stop your server and try something a little different:

(.stop server 0)

Although you can run this code in a Clojure REPL to gain some level of dynamism, having to start and stop the server after each handler change would quickly become cumbersome. If you instead organize the code to bind the return of default-handler, you can manipulate the handler independently and update its behavior at runtime:

(def p (default-handler
         "There's no problem that can't be solved
          with another level of indirection"))

(def server (new-server 8123 "/" p))

At this point, visiting the URL at http://localhost:8123/ shows the new message. But you can take it one step further by making changes without taking the server instance down in such a clumsy fashion. Clojure provides a function named update-proxy that takes a proxied instance and a map containing method names to functions that implement their new behaviors. The functions supplied in the map must accept as their first argument the this instance that, when called, is the proxied instance. An example will make this clear:

(update-proxy p
  {"handle" (fn [this exchange]
              (respond exchange (str "this is " this)))})

Revisiting the web address http://localhost:8123/ again shows a new message corresponding to the instance representation of the proxy. The following more interesting handler reflects the request headers back to the client in a Clojure map format.

Listing 12.4. Web handler that echoes the request headers

The interesting part is that by holding on to the proxied handler in the p var, you can manipulate it without taking down the server. You do this by again running the update-proxy call:

(update-proxy p {"handle" echo-handler})

If you visit the same web address as before, you’ll see a map-like representation of the given request headers. Pretty cool, no? For example, ours looks like the following:

{"Cache-control" ("max-age=0"),
 "Host" ("localhost:8123"),
 "Connection" ("keep-alive"),
 ...}

If you’re so inclined, you can also inspect the current proxy mappings using the function proxy-mappings. The question remains—how does update-proxy change the behavior of a previously generated proxy class?

It’s called proxy for a reason

Clojure’s proxy function generates the bytecode for an actual class on demand, but it does so in such a way as to provide a more dynamic implementation. Instead of inserting the bytecode for the given method bodies directly into the proxy class, Clojure instead generates a proper proxy in which each method looks up the function implementing the method’s behavior. That is, based on the method name, the corresponding function is retrieved from a map and invoked with the this reference and the remaining argument(s). This trades highly useful dynamic behavior for some runtime cost, but in many cases this is a fair trade.

Proxies for true power dynamism

Working from the abstract model in figure 12.1, observe how Clojure updates the mapped functions within a proxy at runtime. This web service is a humble example, but there’s a point to take away from this exercise: to perform this same task in Java wouldn’t be impossible but would require an enormous amount of scaffolding to implement properly, whereas in Clojure it’s built into the language.

Figure 12.1. The instance returned by proxy is a proper proxy that does method dispatch to functions in a lookup table. These functions can therefore be swapped out with replacements as needed.

Proxies as proper citizens

In the original respond function, you used a hook named around that was intended to take the result of the call to the .getResponseBody method and do something with it. Because the result of this method call is a java.io.OutputStream, you can use that information to your advantage when creating a filtering function. The use of the identity function as the default filter ensures that the usage doesn’t break in the default case; but if you’re going to use your own filtering function, you must ensure that you properly wrap the original, which again is a perfect use case for proxy. A simple implementation of an html-around filter would be implemented as in the following listing.

Listing 12.5. “Around” filter that wraps an output stream with head and body tags

The proxy returned by html-around extends the Java class java.io.FilterOutputStream to the superclass constructor (via the [o] vector). It passes the argument o, which corresponds to the OutputStream obtained from the .getResponseBody method called in the original respond function. The call to the proxy-super function is similar to Java’s super.method() semantics.[3] You could use the HTML filter in the call to echo, but that would be less than satisfying. Instead, you’ll create one more handler that lists the contents of a directory in HTML and that provides links to navigate a file system.[4]

3 Note that in a break from almost every other construct in Clojure, proxy-super is not thread-safe. If some other thread were to call this proxy instance’s write method while proxy-super was still running, the base class’s method would be called directly, incorrectly skipping the proxy implementation. So be careful using proxy-super and multiple threads in close proximity to each other.

4 This new handler operates as a watered-down version of Python’s SimpleHTTPServer module.

At the bottom of the file-system handler named fs-handler is a function that takes a file and attempts to return a sequence of filenames associated with it:

(defn listing [file]
  (-> file .list sort))

Anaphoric proxy

In section 8.5, we discouraged you from writing anaphoric macros (macros with injected forward references), yet you might have noticed that proxy contradicts us. The use of the anaphora this is subject to the same nesting limitations as previously mentioned and is a good candidate for change in later versions of Clojure. You might notice that the reify macro, although similar to proxy, doesn’t use an anaphoric this but instead requires that it be named explicitly—the preferred approach for your own macros, and likely the way forward for all future Clojure core macros.

The listing function expects a File instance for a directory but returns an empty sequence if given a file:

(listing (io/file "."))
;;=> (".gitignore" "README.md" "project.clj" "src" "target" "test")

(listing (io/file "./README.md"))
;;=> ()

Now that listing is in place, you can use its output to generate some HTML links, as shown next.

Listing 12.6. Quick and dirty function to generate HTML file listings

The html-links function is a quick and dirty way to build a string filled with HTML links. It’s fine for the purpose of illustration, but in a production system you’d probably use something like Hiccup instead.[5] In effect, html builds a seq of <a> links, as shown next:

5 Hiccup is a library for representing HTML using Clojure data literals, found at https://github.com/weavejester/hiccup. We’ve had great success with this little library.

(html-links "." (listing (io/file ".")))

;;=> "<a href='./.gitignore'>.gitignore</a><br>
;;       <a href='./README.md'>README.md</a><br>
;;       <a href='./project.clj'>project.clj</a><br>
;;       <a href='./src'>src</a><br>
;;       <a href='./target'>target</a><br>
;;       <a href='./test'>test</a><br>"

The files listed correspond to the files that happen to be in the directory where you launched your REPL. So far, so good. You have a function in html to produce a string for the case of a directory listing, but you’d still like to perform some action in the case where a client requests a file. One thing you could do is to serve the file back to the client, but for simplicity’s sake[6] you’ll build a string listing the file size.

6 The project on which this code is based, named lein-simpleton, does indeed serve the files to the client, as well as some other features not implemented here. The project is located at https://github.com/fogus/lein-simpleton.

Listing 12.7. Function to build a string representation of a file-size listing
(defn details [file]
  (str (.getName file) " is "
       (.length file)  " bytes."))

(details (io/file "./README.md"))

;;=> "README.md is 330 bytes."

To gather the file size, you use the raw java.io.File API. You could write a wrapper around the entire java.io.File API; but in general, using Java (and JavaScript in the case of ClojureScript) calls directly is perfectly acceptable form in Clojure.[7] You can see details in action next.

7 And as we say that, it’s worth noting that Anthony Grimes has create a library named fs that puts a nice Clojure face on the java.io.File API. It’s located at https://github.com/Raynes/fs.

Listing 12.8. Function to convert a relative-path URI into a file

Now that you have your pieces together, including respond, html-around, html, and details, you can assemble them to build your new handler.

Listing 12.9. Web handler to list and navigate a local file system

The fs-handler function uses respond to return a string as the response depending on the type of file garnered from the request URI received. In the case of a directory, the HTML links are built and wrapped with the proper HTML blocks via the html-around filter. In the case of a file, the string from details, without a filter, suffices. Again, to set the new handler you can update the proxy as follows:

(update-proxy p {"handle" fs-handler})

If you visit the root URL at http://localhost:8123/ in a browser, you’ll see the directory listing shown in figure 12.2.

Figure 12.2. A directory listing served by the simple web server

If you click one of the directory links, you’ll see the listing of links for that subdirectory. But if you click on one of the file links, you’ll see something like figure 12.3.

Figure 12.3. File details served by the simple web server

And that, ladies and gentlemen, is how you can use Clojure to build a dynamic web server using Clojure and proxy.

Final points about proxy

Clojure’s proxy capabilities are truly dynamic, allowing you to create fully stubbed proxies using construct-proxy, get-proxy-class, or init-proxy. In all three cases, a partially to fully realized proxy will be constructed, allowing programmatic customization using update-proxy and arbitrary mixin maps.

There’s a universe of difference between the code outlined in this subsection and systems employing true code hot-loading, but it’s a reasonable facsimile. Using proxy is powerful, but doing so creates unnamed instances unavailable for later extension. If you instead want to create named classes, you’ll need to use Clojure’s gen-class mechanism, which we’ll discuss next.

12.2. Clojure gen-class and GUI programming

In section 9.1, we mention that Clojure namespaces can be used as the basis for generating a named class. In this section, we’ll address this topic and others related to Clojure’s gen-class function and :gen-class namespace directive in the context of writing a simple graphical user interface (GUI) library.

12.2.1. Namespaces as class specifications

Similar to the ns example in section 9.1, the explanation of gen-class begs a declarative approach for a namespace defining a class named joy.gui.DynaFrame. Suppose you’d like this class to do two things. First, joy.gui.DynaFrame should extend javax.swing.JFrame to obtain its behavior (and override where necessary). Second, joy.gui.DynaFrame should declare functions that provide its overriding method implementations (to be prefixed[8] by the symbol df-). In addition, you’d like the class to implement the clojure.lang.IMeta interface. You’d also like a place to store information about instances of this class in state and would like the initialization function called on construction to be named df-init. You’d like to define a single constructor, taking a string and passing it on to the superclass constructor, which also takes a string. You then want to declare two public methods: display, which takes a java.awt.Container and returns void; and a static method version that takes no arguments and returns a string. Finally, you’ll declare the required imports needed.

8 If you don’t specify a :prefix, then the default - will be used.

The worded DynaFrame class declaration is complex but has the advantage of having a direct code translation, as shown in the following listing.

Listing 12.10. DynaFrame class ns block showing rich gen-class usage

You can compile the joy.gui.DynaFrame namespace by saving it in a directory joy/gui, located on the classpath, in a file named DynaFrame.clj and executing the function (compile 'joy.gui.DynaFrame) in a fresh REPL. This allows a compiled class to be immediately available. But trying to create an instance in the same REPL will prove fruitless:

(joy.gui.DynaFrame. "First try")

; java.lang.UnsupportedOperationException:
;   joy.gui.DynaFrame/df-init not defined

Clearly you haven’t defined the df-init function, so you’ll do that now by switching to the joy.gui.DynaFrame namespace, defining it outright:

(in-ns 'joy.gui.DynaFrame)

(defn df-init [title]
  [[title] (atom {::title title})])

Now run the following in your REPL:

(joy.gui.DynaFrame. "2nd")

; java.lang.UnsupportedOperationException:
;   meta (joy.gui.DynaFrame/df-meta not defined?)

Because you told the Clojure compiler that the class should implement the IMeta interface, you should have provided a concrete implementation, which you can do at the REPL:

(defn df-meta [this] @(.state this))
(defn version [] "1.0")

As an added bonus, you implement the static method version. To see the effects of these functions, execute the following:

(meta (joy.gui.DynaFrame. "3rd"))
;=> {:joy.gui.DynaFrame/title "3rd"}

(joy.gui.DynaFrame/version)
;=> "1.0"

You’ve filled in most of the implementation of the DynaFrame class except for the display function, which you can implement as follows:

(defn df-display [this pane]
  (doto this
    (-> .getContentPane .removeAll)

    (.setContentPane (doto (JPanel.)
                       (.add pane BorderLayout/CENTER)))
    (.pack)
    (.setVisible true)))

You can see df-display in action in the REPL by running the following:

(def gui (joy.gui.DynaFrame. "4th"))

(.display gui (doto (javax.swing.JPanel.)
                (.add (javax.swing.JLabel. "Charlemagne and Pippin"))))

This displays the GUI frame shown in figure 12.4.

Figure 12.4. Now that you’ve compiled the DynaFrame class, you can start using it to display simple GUIs.

And because it’s a DynaFrame, you should be able to change it on the fly, right? Right:

(.display gui (doto (javax.swing.JPanel.)
              (.add (javax.swing.JLabel. "Mater semper certa est." ))))

This changes the view to that shown in figure 12.5.

Figure 12.5. You can update the DynaFrame on the fly without restarting.

But now that you have this interesting little frame, what can you do with it? Next, you’ll experiment with DynaFrame as the foundation for agile GUI prototyping.

12.2.2. The guts of namespace compilation

What exactly does the :gen-class directive provide in terms of generated class files? With or without :gen-class, Clojure generates a set of classes corresponding to each function in a namespace. For the function joy.gui.DynaFrame/df-display, a class file is generated on the classpath of joy.gui.DynaFrame$df_display containing (at least) a method invoke, at the location CLASSPATH/joy/gui/DynaFrame$df_display .class, as shown:

package joy.gui;
public class DynaFrame$df_display extends AFunction {
    . . .
    public Object invoke(Object that, Object container) {
        . . . display actions . . .
    }
}

Of course, this describes implementation details and shouldn’t be considered fact in future versions of Clojure. As shown earlier, you can add implementations for the parts of the DynaFrame class at the REPL because Clojure generates a stub that looks up concrete implementations through vars. But these details are useful for describing the logical product of :gen-class and compile. The :gen-class directive with the argument :name joy.gui.DynaFrame creates a class vaguely resembling the following Java source:

package joy.gui;

public class DynaFrame extends javax.swing.JFrame {
    public final Object state;
    public DynaFrame(String title) {
      Object r =  clojure.lang.RT.var("joy.gui.DynaFrame", "df-init")
                     .invoke(title);
      Object cargs = clojure.lang.RT.nth(r, 0);
      state = clojure.lang.RT.nth(r, 1);
      super((String) clojure.lang.RT.nth(cargs, 0));
    }

    public static String version() { return "1.0"; }

    // Delegate to the display function var
    public void display(Object the_this, java.awt.Container c) {
        return clojure.lang.RT.var("joy.gui.DynaFrame", "df-display")
                 .invoke(the_this, c);
    }

    . . .
}

The :gen-class directive creates a class that’s a delegate for the vars (prefixed as specified with df-) located in the corresponding namespace, contains the state, and also holds any static methods. This is a lot of detail to contend with, but understanding it is important when arranging your Clojure projects to take advantage of code compilation.

One final important point when using gen-class is the semantics surrounding the :impl-ns directive. This example relies on the fact that the gen-class namespace is the same as the implementation namespace (the :impl-ns), meaning the compilation transitively compiles all the implementation functions. On the other hand, when your implementation and gen-class namespaces are distinct, you no longer suffer transitive compilation. This provides the benefit of allowing a mixture of compiled (class files) and uncompiled (.clj files) Clojure products.

12.2.3. Exploring user interface design and development with Clojure

Before you begin this example, let’s devise a simple model (_why 2007)[9] for exploring user interface design. No need to complicate matters, because the goal is only to get a general idea of how Clojure makes a typically painful task like Java GUI development a joy. To achieve this modest goal, you need the simple containers illustrated in figure 12.6: shelves, stacks, and splitters.

9 The GUI model in this section is based loosely on the Ruby framework Shoes created by _why. Thank you, sir, wherever you are.

Figure 12.6. Using only a handful of rudimentary containers, you can build neato GUI prototypes.

Because DynaFrame requires a java.awt .Container as its displayed element, you’ll make each container a derivative thereof. This allows the containers to nest, helping to build richer GUIs. Finally, their forms should mirror their graphical layout, within reason. These three containers are implemented in the following listing.

Listing 12.11. Simple GUI containers
(ns joy.gui.socks
  (:import
   (joy.gui DynaFrame)
   (javax.swing Box BoxLayout JTextField JPanel
                JSplitPane JLabel JButton
                JOptionPane)
   (java.awt BorderLayout Component GridLayout FlowLayout)
   (java.awt.event ActionListener)))

(defn shelf [& components]
  (let [shelf (JPanel.)]
     (.setLayout shelf (FlowLayout.))
     (doseq [c components] (.add shelf c))
     shelf))

(defn stack [& components]
  (let [stack (Box. BoxLayout/PAGE_AXIS)]
     (doseq [c components]
       (.setAlignmentX c Component/CENTER_ALIGNMENT)
       (.add stack c))
     stack))

  (defn splitter [top bottom]
   (doto (JSplitPane.)
     (.setOrientation JSplitPane/VERTICAL_SPLIT)
     (.setLeftComponent top)
     (.setRightComponent bottom)))

These simple GUI elements are built on top of the Java Swing library, where each sub-widget in the components argument is added to the properly configured Container-derived parent. These are good as a starting point, but there’s nothing to display unless you dive into the Swing API directly. You can do one better than that by providing a simple base set of widgets: buttons, labels, and text boxes.

Listing 12.12. Set of simple widgets
(defn button [text f]
  (doto (JButton. text)
    (.addActionListener
     (proxy [ActionListener] []
       (actionPerformed [_] (f))))))


(defn txt  [cols t]
  (doto (JTextField.)
    (.setColumns cols)
     (.setText t)))

(defn label [txt] (JLabel. txt))

The button element takes a function executed on a mouse click, so you’ll next provide a JavaScript-like alert function as a simple action:

(defn alert
  ([msg] (alert nil msg))
  ([frame msg]
     (javax.swing.JOptionPane/showMessageDialog frame msg)))

Having built all these GUI elements, you can describe the first simple GUI, as shown in figure 12.7.

Figure 12.7. DynaFrame alerts: you can create slightly more complex GUIs and attach actions on the fly.

It seems simple, if not pointless. But you might be pleasantly surprised by the concise code used to describe this GUI:

(.display gui
  (splitter
    (button "Procrastinate" #(alert "Eat Cheetos"))
    (button "Move It" #(alert "Couch to 5k"))))

These widgets are adequate enough to create richer user interfaces, and to illustrate let’s add one more widget builder for grid-like elements:

(defn grid [x y f]
   (let [g (doto (JPanel.)
             (.setLayout (GridLayout. x y)))]
      (dotimes [i x]
        (dotimes [j y]
         (.add g (f))))
      g))

With the small amount of code in the following listing, you can build the richer user interface shown in figure 12.8.

Figure 12.8. A much more elaborate DynaFrame GUI. There’s no limit to the complexity of this simple GUI model. Go ahead and experiment to your heart’s content.

Listing 12.13. A more complex GUI example

Although not perfect, it gives you a good idea how to extend these functions to provide a finer level of control over layout and positioning, as well as ways to provide more functionality to create richer interfaces. How would you go about creating an agile environment for incremental GUI development using plain Java? Clojure allows you to start with a powerful set of primitives and incrementally refine them until they suit your exact needs.

This section started as a description of creating a simple dynamic frame using the gen-class facility, but we felt it was worthwhile to expand into the realm of dynamic, incremental development. Sometimes ahead-of-time (AOT) compilation is absolutely necessary (such as with client requirements), but our advice is to avoid it if at all possible. Instead, use the dynamic nature of Clojure to its fullest, designing your system to fit into that model.

12.3. Clojure’s relationship to Java arrays

In general, the need to delve into arrays should be limited, but such casual dismissal isn’t always apropos. In this section, we’ll cover some of the uses for Java arrays in Clojure, including but not limited to arrays as multimethod dispatch, primitive versus reference arrays, calling variadic functions and constructors, and multidimensional arrays.

12.3.1. Types of arrays: primitive and reference

As mentioned in section 4.1, Clojure numbers are of the boxed variety, but in many cases the Clojure compiler can resolve the correct call for primitive interoperability calls. But it can never resolve the need to pass a primitive array when a reference array is provided instead.

Creating primitive arrays

The Java class java.lang.StringBuilder provides[10] a method .append(char[]) that appends the primitive chars in the passed array to its end. But our first instinct for making this happen in Clojure doesn’t bear fruit:

10 When dealing with and manipulating strings, your best options can almost always be found in the core clojure.string namespace or the clojure.contrib.string namespace in the Clojure contrib library.

(doto (StringBuilder. "abc")
  (.append (into-array [x y z])))

;=> #<StringBuilder abc[Ljava.lang.Character;@65efb4be>

The problem is that Clojure’s into-array function doesn’t return a primitive array of char[], but instead returns a reference array of Character[], forcing the Clojure compiler to resolve the call to the StringBuilder.append(Object) method instead. That the Array class is a subclass of Object is a constant cause for headache in Java and clearly can be a problem[11] for Clojure as well. What you really want to do is ensure that a primitive array is used as the argument to .append, as shown here:

11 In this example, it’s preferred that a java.lang.IllegalArgumentException: No matching method found exception be thrown, because StringBuilder doesn’t have a method matching .append (Character[]) or even .append(Object[]).

(doto (StringBuilder. "abc")
  (.append (char-array [x y z])))

;=> #<StringBuilder abcxyz>

Clojure provides a number of primitive array-building functions that work similarly to char-array, as summarized in the following list:

  • boolean-array
  • byte-array
  • char-array
  • double-array
  • float-array
  • int-array
  • long-array
  • object-array
  • short-array

You can also use the make-array and into-array functions to create primitive arrays:

(let [ary (make-array Long/TYPE 3 3)]
  (dotimes [i 3]
    (dotimes [j 3]
      (aset ary i j (+ i j))))
  (map seq ary))

;=> ((0 1 2) (1 2 3) (2 3 4))

(into-array Integer/TYPE [1 2 3])
;=> #<int[] [I@391be9d4>

Populating arrays can often be an iterative affair, as seen in the previous snippet, but there are often more concise ways to do so when creating reference arrays.

Creating reference arrays

To intentionally create an array of a particular reference type, or of compatible types, use the into-array function, passing in a sequence of objects:

(into-array ["a" "b" "c"])
;=> #<String[] [Ljava.lang.String;@3c3ac93e>

(into-array [(java.util.Date.) (java.sql.Time. 0)])
;=> #<Date[] [Ljava.util.Date;@178aab40>

(into-array ["a" "b" 1M])
; java.lang.IllegalArgumentException: array element type mismatch

(into-array Number [1 2.0 3M 4/5])
;=> #<Number[] [Ljava.lang.Number;@140b6e46>

The function into-array determines the type of the resulting array based on the first element of the sequence, and each subsequent element type must be compatible (a subclass). To create a heterogeneous array of java.lang.Object, use the to-array or to-array-2d function:

to-array-2d function:

(to-array-2d [[1 2 3]
              [4 5 6]])
;=> #<Object[][] [[Ljava.lang.Object;@bdccedd>

(to-array ["a" 1M #(%) (proxy [Object] [])])
;=> #<Object[] [Ljava.lang.Object;@18987a33>

(to-array [1 (int 2)])
;=> #<Object[] [Ljava.lang.Object;@6ad3c65d>

Be wary: primitives are autoboxed when using either to-array or to-array-2d.

12.3.2. Array mutability

Because JVM arrays are mutable, you need to be aware that their contents can change at any point. For example:

(def ary  (into-array [1 2 3]))
(def sary (seq ary))
sary
;=> (1 2 3)

What happens to sary if you change the contents of ary?

(aset ary 0 42)
sary
;=> (42 2 3)

The seq view of an array is that of the live array and therefore subject to concurrent modification. Be cautious when sharing arrays from one function to the next, and especially across threads. This can be especially disastrous if an array changes in the middle of a sequential operation, such as the use of the higher-order array functions amap and areduce, which you might use to define a sum-of-squares function[12] for arrays:

12 This function is fairly clear but slower than it should be. You’ll make it faster in section 15.1.2.

(defn asum-sq [xs]
  (let [dbl (amap xs i ret
              (* (aget xs i)
                 (aget xs i)))]
    (areduce dbl i ret 0
      (+ ret (aget dbl i)))))

(asum-sq (double-array [1 2 3 4 5]))
;=> 55.0

At any point during the processing of asum-sq, the underlying array could change, causing inaccurate results or worse. Take great care when using Java’s mutable arrays, but note that sharing only the seq of an array (typically created with the seq function) is perfectly safe because there’s no way to get at the array when you only have a reference to the seq.

12.3.3. Arrays’ unfortunate naming convention

You might have noticed (how could you miss?) the ugly names printed by the Clojure REPL whenever an array is evaluated. There’s logic to this madness, because part of the jumble is the legal name of the class corresponding to the array—the part formed as [Ljava.lang.String;. For example, the previous name corresponded to a 1D array of strings. The representation for a 2D array of strings is then [[Ljava.lang.String;, and it therefore follows that [[[Ljava.lang.String; is a 3D array of strings. Are you sensing a pattern here? Table 12.1 lays it out.

Table 12.1. Array type class names and dimensions

Representation

Array type

[Ljava.lang.Object; Reference array
[B Primitive byte array
[I Primitive int array
[C Primitive char array
[S Primitive short array
[F Primitive float array
[D Primitive double array
[J Primitive long array
[Z Primitive boolean array
Representation Dimension
[ 1D
[[ 2D
... And so on ...

Using what you know about arrays, you can use the class-representation names to do things such as multimethod dispatch:

(what-is (into-array ["a" "b"]))
;=> "1d String"

(what-is (to-array-2d [[1 2][3 4]]))
;=> "2d Object"

(what-is (make-array Integer/TYPE 2 2 2 2))
;=> "Primitive 4d int"

You can create methods for identifying arrays and returning a descriptive string using the Class/forName method:

(defmulti what-is class)
(defmethod what-is
  (Class/forName "[Ljava.lang.String;")
  [_]
  "1d String")

(defmethod what-is
  (Class/forName "[[Ljava.lang.Object;")
  [_]
  "2d Object")

(defmethod what-is
  (Class/forName "[[[[I")
  [_]
  "Primitive 4d int")

Although this isn’t the most beautiful task to perform in Clojure, it’s easy to understand once you’ve grasped how the array class names are constructed.

12.3.4. Multidimensional arrays

What if you try to construct a 2D array of doubles using into-array? Observe what happens when we try the following call:

(what-is (into-array [[1.0] [2.0]]))
; java.lang.IllegalArgumentException: No method in multimethod
;  'what-is' for dispatch value: class [Lclojure.lang.PersistentVector;

The problem is that the into-array function builds a 1D array of persistent vectors, but we wanted a 2D array of doubles. In order to do this, the array must be built differently:

(defmethod what-is (Class/forName "[[D") [a] "Primitive 2d double")
(defmethod what-is (Class/forName "[Lclojure.lang.PersistentVector;")
[a] "1d Persistent Vector")

(what-is (into-array (map double-array [[1.0] [2.0]])))
;=> "Primitive 2d double"

(what-is (into-array [[1.0] [2.0]]))
;=> "1d Persistent Vector"

You have to use the map function with double-array on the inner arrays in order to build the properly typed outer array. When working with multidimensional arrays, be sure you know what your inner elements should be on creation, and create them accordingly.

12.3.5. Variadic method/constructor calls

There’s no such thing as a variadic constructor or method at the bytecode level, although Java provides syntactic sugar at the language level. Instead, variadic methods expect an array as their final argument, and this is how they should be accessed in Clojure interop scenarios. Take, for example, this call to the String/format function:

(String/format "An int %d and a String %s"
  (to-array [99, "luftballons"]))

;=> "An int 99 and a String luftballons"

That covers most of the high points regarding arrays in Clojure interoperability. We’ll touch on them briefly when we talk about performance considerations in chapter 15, but for now we’ll move on to a more interesting topic: the interoperability underpinnings relating to Clojure’s implementation.

12.4. All Clojure functions implement ...

Clojure functions are highly amenable to interoperability. Their underlying classes implement a number of useful interfaces that you can investigate by running (ancestors (class #())). Most of the resulting classes are only applicable to the internals of Clojure, but a few interfaces are useful in interop scenarios: java.util .concurrent.Callable, java.util.Comparator, and java.lang.Runnable. In this section, we’ll talk briefly about each and also provide simple examples.

12.4.1. The java.util.Comparator interface

The java.util.Comparator interface defines the signature for a single method .compare that takes two objects l and r and returns < 0 if l < r, 0 if l == r, and > 0 if l > r. Every Clojure function already implements the java.util.Comparator interface, so if a function returns a value in accordance with the comparison protocol, it can be used as a comparator.[13]

13 A function can unintentionally adhere to the comparator protocol and not be a comparator per se, thus causing confusion if used as such. Fortunately, this is a rare occurrence.

The static Java method Collections/sort provides an implementation that takes a derivative of java.util.List and a Comparator and destructively sorts the list provided. Using this knowledge, you can provide some basic infrastructure for the remainder of this subsection:

(import '[java.util Comparator Collections ArrayList])

(defn gimme [] (ArrayList. [1 3 4 8 2]))
(doto (gimme)
  (Collections/sort (Collections/reverseOrder)))

;=> [8, 4, 3, 2, 1]

In order to write your own version of a comparator (for illustrative purposes) that provides a reverse-sort Comparator, you might naively do so:

(doto (gimme)
  (Collections/sort
    (reify Comparator
      (compare [this l r]
        (cond
          (> l r) -1
          (= l r) 0
          :else 1)))))
;=> #<ArrayList [8, 4, 3, 2, 1]>

Although this works, Clojure provides a better way by allowing the use of potentially any function as the Comparator directly. You can couple this knowledge with the fact that Clojure already provides numerous functions useful for comparison:

When presented with numerous possible implementation strategies, often the best one in Clojure is the simplest.

12.4.2. The java.lang.Runnable interface

Java threads expect an object implementing the java.lang.Runnable interface, meant for computations returning no value. We won’t get into the specifics of threaded computation here, but the next two examples are simple enough to require little advance knowledge. If you wish to pass a function to another Java thread, it’s as easy as providing it as an argument to the Thread constructor, because every Clojure function implements the java.lang.Runnable interface:

(doto (Thread. #(do (Thread/sleep 5000)
                    (println "haikeeba!")))
  .start)
; => #<Thread Thread[Thread-3,5,main]>
; ... 5 seconds later
; haikeeba!

This scenario is unlikely to occur often, because Clojure’s core concurrency features are sufficient for most needs. But that’s not always the case, and therefore it’s nice to know that raw Clojure functions can be used seamlessly in the JVM’s concurrency API.

12.4.3. The java.util.concurrent.Callable interface

The Java interface java.util.concurrent.Callable is specifically meant to be used in a threaded context for computations that return a value. You can use a Clojure function using Java’s java.util.concurrentFutureTask class, representing a “computation to occur later”:

(import '[java.util.concurrent FutureTask])

(let [f (FutureTask. #(do (Thread/sleep 5000) 42))]
  (.start (Thread. #(.run f)))
  (.get f))
; ... 5 seconds later
;=> 42

The call to FutureTask.get as the last expression stops execution (a behavior known as blocking) until the function passed to the constructor completes. Because the function in question sleeps for five seconds, the call to .get must wait.

Clojure’s interoperability mechanisms are a two-way street. Not only do they allow Java APIs to work seamlessly in Clojure, but they also provide ways for Clojure functions to work in Java APIs. In the next section, we’ll continue on this theme of bidirectional interop with a discussion of the ways you can use Clojure’s collection types in traditional Java APIs.

12.5. Using Clojure data structures in Java APIs

Clojure functions are ready to use in many Java APIs; and, as it turns out, so are its collection types. Just as the Clojure collections are separated along three distinct equality partitions[14] (maps, sequences, and sets), so too are its levels of Java collection interoperability support. The Java Collections Framework has a nice high-level design philosophy centered around working against interfaces. These interfaces are cognizant of immutability, in that the mutable parts are optional and the immutable parts are clearly demarcated. In this section, we’ll give a brief rundown of ways you can use Clojure collections in traditional Java APIs adhering to the immutable collection protocols.

14 You can find a refresher on equality partitions in section 5.1.2 and throughout the remainder of chapter 5.

12.5.1. The java.util.List interface

Clojure sequential collections conform to the immutable parts of the java.util.List interface, which in turn extends the java.util.Collection and java.lang .Iterable interfaces. You can see this conformance in action in the following listing.

Listing 12.14. java.util.List conformance for sequences and seqs

That Clojure sequences and seqs don’t provide the mutable API of typical Java collections is obvious. But the implications are that you can’t use them in all Java APIs, such as you might attempt when requiring that a vector be sorted destructively with a Java API call:

(java.util.Collections/sort [3 4 2 1])
; java.lang.UnsupportedOperationException

A better approach is to either use the method used in the previous section using a Clojure function or, even better, to use the Clojure sort function.

12.5.2. The java.lang.Comparable interface

The interface java.lang.Comparable is the cousin of the Comparator interface. Comparator refers to objects that can compare two other objects, whereas Comparable refers to an object that can compare itself to another object:

(.compareTo [:a] [:a])
;=> 0

(.compareTo [:a :b] [:a])
;=> 1

(.compareTo [:a :b] [:a :b :c])
;=> -1

(sort [[:a :b :c] [:a] [:a :b]])
;=> ([:a] [:a :b] [:a :b :c])

Note that Clojure’s vector implementation is currently the only collection type that implements the java.lang.Comparable interface providing the .compareTo method. As a result, attempting to compare a different collection type to a vector leads to a confusing error message:

(.compareTo [1 2 3] '(1 2 3))

; java.lang.ClassCastException: clojure.lang.PersistentList
;    cannot be cast to clojure.lang.IPersistentVector

Pay no attention to that class-cast exception behind the curtain.

12.5.3. The java.util.RandomAccess interface

In general, the java.util.RandomAccess interface is used to indicate that the data type provides constant-time indexed access to its elements. This allows algorithms to follow optimized paths accordingly. This optimization is generally performed by using the .get method for access rather than an iterator:

(.get '[a b c] 2)
;=> c

Vectors are currently the only Clojure collection type that can make such guarantees.

12.5.4. The java.util.Collection interface

The java.util.Collection interface lies at the heart of the Java Collections Framework, and classes implementing it can play in many of Java’s core collections APIs. A useful idiom that takes advantage of this fact is the use of a Clojure sequence as a model to build a mutable sequence for use in the Java Collections API:

(defn shuffle [coll]
  (seq (doto (java.util.ArrayList. coll)
         java.util.Collections/shuffle)))

(shuffle (range 10))
;=> (3 9 2 5 4 7 8 6 1 0)

It’s difficult to write a proper sequence-shuffling function, so the shuffle function takes full advantage of an existing Java API that has been tested and used extensively for years. As an added bonus, shuffle is mostly[15] functional and fast. Clojure favors immutability but doesn’t trap you into it when practical solutions are available.

15 shuffle isn’t referentially transparent. Can you see why?

The Java.Util.Map interface

Like most of the Clojure collections, its maps are analogous to Java maps in that they can be used in nonmutating contexts. But immutable maps have the added advantage of never requiring defensive copies, and they act exactly the same as unmodifiable Java maps:

(java.util.Collections/unmodifiableMap
  (doto (java.util.HashMap.) (.put :a 1)))
;=> #<UnmodifiableMap {:a=1}>
(into {} (doto (java.util.HashMap.) (.put :a 1)))
;=> {:a 1}

In both cases, any attempt to modify the map-entry classes of the maps will throw an exception.

12.5.5. The java.util.Set interface

In the case of Java and Clojure sets, the use of mutable objects[16] as elements is highly frowned on:

16 Clojure’s mutable reference types used to represent a logical identity are perfectly safe to use in sets. We explore the reference types in exquisite detail in chapter 11.

(def x (java.awt.Point. 0 0))
(def y (java.awt.Point. 0 42))
(def points #{x y})
points
;=> #{#<Point java.awt.Point[x=0,y=0]>
#<Point java.awt.Point[x=0,y=42]>}

Everything looks peachy at this point, but introducing mutability into the equation has devastating costs:

(.setLocation y 0 0)
points
;=> #{#<Point java.awt.Point[x=0,y=0]>
#<Point java.awt.Point[x=0,y=0]>}

Oh boy. Not only have we confused the set points by modifying its entries out from underneath it, but we’ve also circumvented Clojure’s value-based semantics and the nature of set-ness. Dealing with mutable objects is extremely difficult to reason about, especially when dealing with collections of them. The gates of a mutable class are wide open, and at any point during the execution of your programs this fact can be exploited, willingly or not. But you can’t always avoid dealing with mutable nasties in Clojure code because of a strict adherence to fostering interoperability.

We’ve covered the two-way interop for functions and collection types, but we have one final path to traverse: the use and benefits of Clojure’s definterface macro.

12.6. The definterface macro

As we mentioned in section 9.3, Clojure was built on abstractions in the host platform Java. Types and protocols help to provide a foundation for defining your own abstractions in Clojure, for use in a Clojure context. But when you’re interoperating with Java code, protocols and types won’t always suffice. Therefore, you need to be able to generate interfaces in some interop scenarios, and also for performance in cases involving primitive argument and return types. In this section, we’ll talk briefly about generating Java interfaces, because the syntax, use cases, and purposes are likely familiar.

12.6.1. Generating interfaces on the fly

When you AOT-compile a protocol, you generate a public interface by the same name, with the methods defined. The code in the following listing uses definterface to define an interface ISliceable. This interface is used to define an abstract thing that has the ability to be sliced using a method slice, which takes start and end indices of type int. Likewise, the interface defines a method sliceCount that returns an int representing the number of possible slices.

Listing 12.15. Interface defining a sliceable object
(definterface ISliceable
  (slice [^long s ^long e])
  (^long sliceCount []))
;=> user.ISliceable

Notice the inclusion of the type decoration ^long on the arguments to slice and the return type of sliceCount. For now you can assume that they operate the same as a type declaration in most languages. They look similar to type hints discussed in section 12.1, except that only in definterface are primitive hints supported. Now you can create an instance implementing the user.ISliceable interface, as shown next.

Listing 12.16. Dummy reified ISliceable
(def dumb
  (reify user.ISliceable
    (slice [_ s e] [:empty])
    (sliceCount [_] 42)))

(.slice dumb 1 2)
;=> [:empty]

(.sliceCount dumb)
;=> 42

There’s nothing terribly surprising about dumb, but you can instead implement it via deftype, proxy, gen-class, or even a Java class. Note that definterface works even without AOT compilation.

You can take definterface to the next logical step and extend the ISliceable interface to other types using a well-placed protocol.

Listing 12.17. Using a protocol to extend ISliceable
(defprotocol Sliceable
  (slice [this s e])
  (sliceCount [this]))

(extend user.ISliceable
  Sliceable
  {:slice (fn [this s e] (.slice this s e))
   :sliceCount (fn [this] (.sliceCount this))})

(sliceCount dumb)
;=> 42

(slice dumb 0 0)
;=> [:empty]

By extending the ISliceable interface along Sliceable, ISliceable is able to participate in the protocol, meaning you have the possibility of extending other types—even final types such as String.

Listing 12.18. Extending strings along the Sliceable protocol
(defn calc-slice-count [thing]
  "Calculates the number of possible slices using the formula:
     (n + r - 1)!
     ------------
      r!(n - 1)!
   where n is (count thing) and r is 2"
  (let [! #(reduce * (take % (iterate inc 1)))
        n (count thing)]
    (/ (! (- (+ n 2)  1))
       (* (! 2) (! (- n 1))))))

(extend-type String
  Sliceable
  (slice [this s e] (.substring this s (inc e)))
  (sliceCount [this] (calc-slice-count this)))

(slice "abc" 0 1)
;=> "ab"
(sliceCount "abc")
;=> 6

The advantages of using definterface over defprotocol are restricted entirely to the fact that the former allows primitive types for arguments and returns. At some point in the future, the same advantages will likely be extended to the interfaces generated, so use definterface sparingly and prefer protocols unless absolutely necessary.

12.7. Be wary of exceptions

There’s been much debate about the virtues of checked exceptions in Java, so we won’t cover that here. Instead, we’ll stick to the facts regarding the nuances the JVM imposes on Clojure’s error-handling facilities. Before we begin, consider the following view on the use of exceptions in Clojure source:

When writing Clojure code, use errors to mean can’t continue and exceptions to mean can or might continue.

We’ll attempt to constrain ourselves to the generalities of exception handling in this section. If you desire information on deciphering exception messages, we talk about that in section 3.4. If you’re curious about the effects of exceptions on continuation-passing style, then refer to section 7.3.4. We discuss the behavior of Clojure when it attempts to supplant numerical inaccuracies by throwing exceptions in section 4.1.3. If you want to learn about the interplay between exceptions and Clojure’s reference types, such matters can be found throughout chapter 11. Finally, if you have no idea what an exception is, we discuss the basics in section 2.9.

12.7.1. A bit of background regarding exceptions

The behavior of Clojure’s exception features directly spawns from the JVM enforcing the promulgation of checked exceptions. Virtuous or not in the context of Java development, checked exceptions are antithetical to closures and higher-order functions. Checked exceptions require not only that the thrower and the party responsible for handling them should declare interest, but also that every intermediary is forced to participate. These intermediaries don’t have to actively throw or handle exceptions occurring within, but they must declare that they’ll be “passing through.” Therefore, by including the call to a Java method throwing a checked exception within a closure, Clojure has two possible alternatives:

  • Provide a cumbersome exception-declaration mechanism on every single function, including closures.
  • By default, declare that all functions throw the root Exception or Runtime-Exception.

As you can probably guess, Clojure takes the second approach, which leads to a condition of multilevel wrapping of exceptions as they pass back up the call stack. This is why, in almost any (.printStackTrace *e) invocation, an error’s point of origin is offset by some number of layers of java.lang.RuntimeException. Java interfaces and classes get to decide what types of problems potential derivative classes and even callers can have, so Clojure needs to handle the base java.lang.Exception at every level, because it has to preserve dynamism in the face of a closed system. Unless you’re directly calling something that throws typed exceptions, your best bet is to catch Exception and then see what you have in context.

12.7.2. Runtime vs. compile-time exceptions

There are two contexts in Clojure where exceptions can be thrown: runtime and compile time. In this section, we’ll touch on both, explaining how and when to use them.

Runtime exceptions

Runtime exceptions may be the most familiar, because you’re likely to have encountered and used them in your own code. There are two types of runtime exceptions: errors and exceptions. You can see the difference between the two here:

(defn explode [] (explode))
(try (explode) (catch Exception e "Stack is blown"))
; java.lang.StackOverflowError

Why can’t you catch the java.lang.StackOverflowError? The reason lies in Java’s exception class hierarchy and the fact that StackOverflowError isn’t a derivative of the Exception class, but instead of the Error class:

(try (explode) (catch StackOverflowError e "Stack is blown"))
;=> "Stack is blown"
(try (explode) (catch Error e "Stack is blown"))
;=> "Stack is blown"


(try (explode) (catch Throwable e "Stack is blown"))
;=> "Stack is blown"

(try (throw (RuntimeException.))
  (catch Throwable e "Catching Throwable is Bad"))
;=> "Catching Throwable is Bad"

This example starts by catching the most specific exception type StackOverflow-Error and gradually decreases specificity until it’s catching Throwable, which you’ll notice also catches a RuntimeException. In Java, catching exceptions at the level of Throwable is considered bad form, and it should generally be viewed the same way in Clojure. Therefore, we suggest that you follow the advice stated in the opening to this section and reserve exceptions deriving from Errors for conditions that can’t be continued from, and those deriving from Exception for conditions that indicate possible continuation.

Compile-time exceptions

There are a few ways you may come across compile-time exceptions, the most obvious occurring within the body of a macro:

(defmacro do-something [x] `(~x))
(do-something 1)
; java.lang.ClassCastException:
;   java.lang.Integer cannot be cast to clojure.lang.IFn

Although the type of the exception is a java.lang.ClassCastException, it was thrown by the compiler, which you’d see if you were to trace the stack using something like (for [e (.getStackTrace *e)] (.getClassName e)).[17] It’s perfectly acceptable (and even encouraged) to throw exceptions in your own macros, but it’s important to make a distinction between compile-time and runtime exceptions.

17 This is a limited analogy to Groovy’s .? operator. Clojure also provides convenience functions for displaying and handling stack traces in the clojure.stacktrace namespace.

Tip

Why delay until runtime the reporting of an error that at compile time you know exists?

The way to throw a compile-time exception is to make sure your throw doesn’t occur in a syntax-quoted form:

(defmacro pairs [& args]
  (if (even? (count args))
   `(partition 2 '~args)
    (throw (Exception.
             (str "pairs requires an even number of args")))))

(pairs 1 2 3)
; java.lang.Exception: pairs requires an even number of args

(pairs 1 2 3 4)
;=> ((1 2) (3 4))

Nothing is preventing the exception from being thrown at runtime, but because we know pairs requires an even number of arguments, we instead prefer to fail as early as possible—at compilation time. This difference is clearly demonstrated by repeating the preceding test in a function definition:

(fn [] (pairs 1 2 3))
; java.lang.Exception: pairs requires an even number of args

A runtime exception wouldn’t have been thrown until this function was called, but because the pairs macro threw an exception at compile time, users are notified of their error immediately. Although it’s powerful, you should always try to balance the benefits of compile-time error checking with macros and the advantages that implementing as a function provides (the use in higher-order functions, apply, and so on).

12.7.3. Handling exceptions

There are two ways to handle exceptions and errors, each defined by the way in which the error-handling mechanisms flow through the source. Imagine that you want a macro that provides a limited[18] null-safe (Koenig 2007) arrow that catches any occurrence of a NullPointerException in a pipeline:

18 You can find much more comprehensive -?> and .?. macros in the clojure.contrib.core namespace, and we recommend those over the one in this section.

(defmacro -?> [& forms]
 `(try (-> ~@forms)
    (catch NullPointerException _# nil)))

(-?> 25 Math/sqrt (+ 100))
;=> 105.0

(-?> 25 Math/sqrt (and nil) (+ 100))
;=> nil

The flow of any occurrence of NullPointerException happens from the inner functions of the stitched forms. Conceptually, this flow can be viewed as shown in figure 12.9, which describes the way errors can be caught depending on the direction in which data is moving along the stack.

Figure 12.9. There are two ways to handle errors in Clojure. The typical way is to let exceptions flow from the inner forms to the outer. The other way, discussed in section 17.4, uses dynamic bindings to reach into the inner forms to handle errors immediately.

The typical (try ... (catch ...)) form would therefore be used for the case where the handler catches errors bubbling outward from inner functions and forms, as seen in the -?> macro. But if you want to catch errors at their point of origin, you need a way to pass handlers up the stack. Fortunately, Clojure provides a way to do this via its dynamic var feature, which we’ll discuss in section 17.4.1.

12.7.4. Custom exceptions

Prior to Clojure 1.4, to write your own exception and error types, you needed to use the gen-class feature described in section 12.2, thus requiring that your code be compiled. JVM exceptions are a closed system, and it might be a good idea to explore other possibilities [19] for enhanced error reporting and handling. But, should you wish to ignore this advice, bear in mind that it’s rare for Clojure core functions to throw exceptions, and even more rarely are they checked exceptions. The idiom is for Clojure to throw derivatives of RuntimeException or Error, and thus your code should also strive for this when appropriate.

19 One library offering enhanced exception definition and handling is Slingshot, located at http://github.com/scgilardi/slingshot.

At a lower level of operation, Clojure 1.4 introduced two new core functions ex-data and ex-info, used to attach information to load-bearing runtime exceptions. Java exceptions have always been load bearing in that they allow an arbitrary message string accessible via the Throwable#getMessage method. But to encode a finer level of detail than an error message requires extending a class from Java’s exception/error hierarchy or, even worse, encoding some data type as a message string.

Clojure provides a new exception type ExceptionInfo that holds both a message string and a map. The function ex-info is a convenience function to construct instances of the ExceptionInfo type for throwing. Before you do that, consider the following function:

(defn perform-unclean-act [x y]
  (/ x y))

Division is hardly an unclean act; it’s how you abuse it that corrupts:

(try
  (perform-unclean-act 42 0)
  (catch RuntimeException ex
    (println (str "Something went wrong."))))

;; Something went wrong.

Obviously, dividing by zero is an unclean act, but with the use of ex-info you can make it less nasty for potential callers:

(defn perform-cleaner-act [x y]
  (try
    (/ x y)
    (catch ArithmeticException ex
      (throw (ex-info "You attempted an unclean act"
                      {:args [x y]})))))

By catching a runtime exception like ArithmeticException you can rethrow a load-bearing exception with more contextual information. With this additional information, the caller can perhaps recover more appropriately by extracting the error context information using ex-data, as shown next:

(try
  (perform-cleaner-act 108 0)
  (catch RuntimeException ex
    (println (str "Received error: "   (.getMessage ex)))
    (when-let [ctx (ex-data ex)]
      (println (str "More information: " ctx)))))

;; Received error: You attempted an unclean act
;; More information: {:args [108 0]}

The beauty of ex-data is that it can be called on any exception type, but it returns a map only if presented with a Clojure load-bearing kind. This nicely decouples the throw side from the catch side and doesn’t force either to throw or catch any given concrete type.

12.8. Summary

Clojure provides an extensive set of data abstractions via its types and protocols. It also provides an extensive interoperability facility through proxy, gen-class, definterface, exception handling, and the implementation of core Java collection interfaces. Although we stress that types and protocols provide the performant abstractions needed for solving most problems, we realize that not all interop scenarios are solved this way. In these circumstances, you should use the features listed in this chapter to push you the remainder of the way toward your solution. Clojure embraces Java interoperability, but it does so in specific ways and with a specific set of tools.

In the next chapter, we move on to discuss an exciting entry in the Clojure language ecosystem: ClojureScript, a version of Clojure that runs on top of JavaScript..

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.6.249