How to Name Clojure Protocols
This is a blog about the development of Yeller, The Exception Tracker with Answers
Is there any sensible consensus on good naming convention for Clojure Protocols?
Naming things is pretty hard. Naming the abstractions used by your software is often even more difficult. But aside from just the words you use, Clojure programmers face another issue:
Do you name a protocol:
- IDeref
- or Deref
This came up recently on the clojure mailing list, and I thought I’d do some digging into what the different conventions across clojure libraries are:
IBlah
Blah
- riemann
- many of the clojurewerkz libraries (welle is a good example)
- postal
- criterium
- faraday
- rotary
- cheshire
- jet
- the clojurescript compiler does both styles
- pedestal
- instaparse
- nrepl
That’s an incomplete list, partly taken from Yeller’s dependencies, partly from the clojure toolbox, but you can see that nearly all clojure libraries avoid the I
prefix. That seems pretty conclusive.
The Right Approach to Protocol Names
So, given these two options, which style should you use to name your protocols? I strongly recommend the former: plain names without an I
prefix. The I prefix seems to be mostly left over Java legacy - we have tools that let you see what’s a protocol vs a type, and adding that “this is an interface” is just line noise.
Further, if you’re dealing with business oriented software (which many of us are), the business names don’t ever start with I
. They’re just “Auction” or “Store” and so on. Your code should reflect that.
This does go against the convention from Clojure.core for interfaces, but even there this convention isn’t always applied. Here are the defprotocol
calls from clojure.core
:
ag defprotocol src
src/clj/clojure/core/reducers.clj
81:(defprotocol CollFold
src/clj/clojure/core/protocols.clj
13:(defprotocol CollReduce
19:(defprotocol InternalReduce
180:(defprotocol IKVReduce
src/clj/clojure/data.clj
67:(defprotocol ^{:added "1.3"} EqualityPartition
71:(defprotocol ^{:added "1.3"} Diff
src/clj/clojure/java/io.clj
35:(defprotocol ^{:added "1.2"} Coercions
69:(defprotocol ^{:added "1.2"} IOFactory
src/clj/clojure/reflect/java.clj
184:(defprotocol ClassResolver
src/clj/clojure/reflect.clj
44:(defprotocol Reflector
48:(defprotocol TypeReference
However, basically every Java interface that clojure uses adopts this convention. Samples:
ag "public interface" src
src/jvm/clojure/lang/IKeywordLookup.java
15:public interface IKeywordLookup{
src/jvm/clojure/lang/ILookup.java
15:public interface ILookup{
src/jvm/clojure/lang/ILookupSite.java
15:public interface ILookupSite{
src/jvm/clojure/lang/ILookupThunk.java
15:public interface ILookupThunk{
src/jvm/clojure/lang/IMapEntry.java
15:public interface IMapEntry extends Map.Entry{
src/jvm/clojure/lang/IMeta.java
15:public interface IMeta {
But not all of them! So there’s little consistency in Clojure itself. No wonder folk wonder about it on the mailing list.
Here are some sample protocols named in this style from Yeller’s codebase:
ag "defprotocol" src
src/yeller/collector/offset_store.clj
8:(defprotocol OffsetStorage
src/yeller/exception_store.clj
11:(defprotocol ExceptionQuery
14:(defprotocol ExceptionLookup
18:(defprotocol ExceptionStatus
21:(defprotocol GroupsIncidents
24:(defprotocol CreateProject
src/yeller/infrastructure/pool.clj
12:(defprotocol KeyPool
src/yeller/timeseries.clj
9:(defprotocol TimeSeries
Overall, it looks like libraries overwhelmingly avoid the I
prefix style, and you should probably adopt that as well.
References
- Growing Object Oriented Software, Guided by Tests has a great section on this (they avoid the I prefix as well).
- J. B. Rainsberger has a great post/chart on better naming overall.
This is a blog about the development of Yeller, the Exception Tracker with Answers.