How to organize namespaces in Clojure?

(written by lawrence krubner, however indented passages are often quotes). You can contact lawrence at: lawrence@krubner.com, or follow me on Twitter.

This is good:

You can think of namespaces as a tool to express something about your application. Here are some ideas to get you started:

Group functions into namespaces based on type of data they manipulate. For example, functions to manipulate customer data go in the “customer” namespace. This technique is familiar from object-oriented languages, but it has the same limitations: where do you put functions concerning relationships among two or more types? The OO answer would be to make a new name for the relationship. This style leads to a proliferation of small namespaces, which can become a burden.

Divide a library into a public API namespace and a internal implementation namespace. Or define a high-level API for common cases and a low-level API for more advanced usage.

Divide an application into namespaces representing architectural layers. You can examine “ns” declarations to prove that each layer calls functions only from the layer below it.

Divide an application into namespaces representing functional modules, with well-defined contracts for communication between modules.

Try to separate decision-making code from the code that carries out those decisions. That is, keep your business logic purely functional and free of side-effects, so it is easy to test. You don’t necessarily have to put side-effect code in a separate namespace, but doing so may help keep it cleanly separated.

So far, in my own work, I have gone with “Divide an application into namespaces representing architectural layers”.

Also interesting here is the style of the extremely smart Zach Tellman :

import-vars

Clojure namespaces conflate the layout of your code and your API. For larger libraries, this generally means that you either have large namespaces (e.g. clojure.core) or a large number of namespaces that have to be used in concert to accomplish non-trivial tasks (e.g. Ring).

The former approach places an onus on the creator of the library; the various orthogonal pieces of his library all coexist, which can make it difficult to keep everything straight. The latter approach places an onus on the consumers of the library, forcing them to remember exactly what functionality resides where before they can actually use it.

import-vars allows functions, macros, and values to be defined in one namespace, and exposed in another. This means that the structure of your code and the structure of your API can be decoupled.

Check out what Zach does with import-fn:

(ns aleph.netty
(:use
[potemkin])
(:require
[aleph.netty.udp :as udp]
[aleph.netty.core :as core]
[aleph.netty.server :as server]
[aleph.netty.client :as client]))

(import-fn core/channel-remote-host-address)
(import-fn core/channel-local-host-name)
(import-fn core/channel-local-port)
(import-fn core/wrap-netty-channel-future)
(import-fn core/event-message)

(import-fn core/wrap-network-channel)
(import-fn core/set-channel-readable)
(import-fn core/network-channel->netty-channel)

(import-macro core/create-netty-pipeline)

(import-fn core/current-options)
(import-fn core/current-channel)

(import-fn server/start-server)
(import-fn server/server-message-handler)

(import-fn client/create-client)

(import-fn udp/create-udp-socket)

He creates a new namespace that has no code but has all the functions he wants, pulled from different namespaces and libraries: he plays mix-n-match and ends up with the interface he wants.

Post external references

  1. 1
    http://stuartsierra.com/2011/08/08/clojure-namespaces
  2. 2
    https://github.com/ztellman/potemkin
  3. 3
    https://github.com/ztellman/aleph/blob/perf/src/aleph/netty.clj
Source