Protocols are a feature in Clojure and ClojureScript that provide high-performance, dynamic, typed-based polymorphism. That’s a lot of computer words, but what it really means is that Clojure(Script) will help us in creating functions that process in different ways arguments of different types. They are similar to Java Interfaces, but with a few key differences.
This post will focus on using protocols in ClojureScript, so if you would like to play around with the examples, you can replicate them in a ClojureScript REPL, like clojurescript.io
Let’s create a basic protocol:
1 2 3
As shown, a protocol is given a name (
Invertible) and a function (invert) the is connected to that protocol. Only one function is defined for our example, but protocols can be associated with multiple functions.
1 2 3 4 5 6 7 8 9 10
It’s also possible to create custom types for our programs and have those types extend protocols, but that is outside the scope of this post.
Working with the DOM
Let’s say in our ClojureScript app we need to query some DOM elements that have the class
todo-item to change them.
When we execute this function we get back a
NodeList object from the browser.
NodeList is sequential like a ClojureScript vector, but unfortunately we can’t use all the functions we know and love from the ClojureScript core library:
map function above as defined in
cljs.core calls attempts to translate our
NodeList object into an instance of the
seq abstraction in order to process it. Many of the built-in ClojureScript types such as vectors implement the
ISeqable protocol, and thus are able to convert themselves into
cljs.core doesn’t implement anything for
NodeLists, so we have to do that ourselves, which allows us to pass the
1 2 3 4 5
ClojureScript’s provided protocols offer a painless way to extend the core language giving us a flexible nature missing in most languages. For instance, in ClojureScript the concept of invoking a function is itself a protocol, named
IFn. For any given type, we can extend the
RegExp types act like functions:
1 2 3 4 5 6 7 8
- filter that array down to the strings that are longer than three characters
- downcase each remaining string
filter, each one producing a new ClojureScript collection. Then when the transformation is finished we could call
We can create a better solution with Transducers, a feature in ClojureScript which provide efficient data transformations without creating intermediate collections. We can create a transducer that represents our desired transformation by composing individual transducer functions like
map which represent a specific transformation but are not tied to any specific concrete input our output collections. Here’s a transducer that will work for our problem:
1 2 3 4
ICollection protocol which defines the
conj for arrays is to use the
push method to add an item to the array an then return it.
1 2 3 4 5 6
ClojureScript also offers a shortcut version of the above call via the
Agree? Disagree? Let us know! Email us your comments at firstname.lastname@example.org.