How to Name Clojure Protocols

This is a blog about the development of Yeller, The Exception Tracker with Answers Read more about Yeller here

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

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. Read more about Yeller here