Matthew Boston

Clojure Protocol Namespaces

Importing and requiring Clojure protocols has its tricks. There are some edge cases you must be aware of when using them from separate namespaces.

First off, if you’re not familiar with Protocols, I suggest you go check out the documentation.

I’ll continue to use the examples from my recent blog post on Implementing Clojure Protocols. Here we have a protocol for defining how something Drives.

(ns company.drives)

(defprotocol Drives
  "a protocol for driving"
  (drive [this throttle]))

Using a Protocol from a Different Namespace

In the concrete implementation of Car we need to require the Drives protocol like so.

(ns company.car
  (:require [company.drives :refer [Drives]]))

(defrecord Car [top-speed]
  Drives
  (drive [_ throttle] (str "Spin wheels " (* top-speed throttle))))

Then in the namespace in which we are instantiating and using Car, we’ll need to require the namespaces of the protocol and record. As well as import the record.

(ns company.core
  (:require [company.car]
            [company.drives :refer [drive]])
  (:import company.car.Car))

(defn -main [args]
  (println (drive (Car. 60.0) 0.4)))

What About Dashes?

There’s something tricky about using dashes in namespaces that I couldn’t find in any documentation.

Given these two facts about Java and Clojure:

  1. Java cannot have dashes in class names
  2. Clojure converts all dashes to underscores when compiling to Java

I was able to trial-and-error my way into a solution for dealing with dashes in protocols.

(ns company.school-bus
  (:require [company.drives :refer [Drives]]))

(defrecord SchoolBus [top-speed]
  Drives
  (drive [_ throttle] (str "The wheels on the bus go " (* top-speed throttle))))
(ns company.core
  (:require [company.car]
            [company.drives :refer [drive]]
            [company.school-bus])
  (:import company.car.Car
           company.school_bus.SchoolBus))

(defn -main []
  (println (drive (Car. 60.0) 0.4))
  (println (drive (SchoolBus. 45.0) 0.4)))

Notice the use of dash in the :require but the use of underscore in the :import. This is because the :import is a Java import used for Java classes, and we must refer to our SchoolBus record as a Java class.

Check out my article on Implementing Clojure Protocols for more information.


Discuss on Hacker News