Realizing What Protocols Are For

I enjoyed Douglas Crockfords talk on "Javascript, the Better Parts", particularly the middle section where Douglas describes all the things he has stopped doing in Javascript, and gives an insight into the style he uses day to day.

This-less programming

In particular, I found his avoidance of the this keyword interesting. Crockford advocates for using constructor functions which return frozen objects, forming a closure over all the methods in the object. In the spirit of the classic contrived OOP examples, lets consider a construcor function for a Dog object:

var Dog = function(spec) {

    var sound = spec['sound'];

    var bark = function() {
        return sound + "!!";
    };

    return Object.freeze({
        bark: bark
    });
};

Notably, this style of OOP completely avoids using the this keyword to refer to any other part of the object, everything is closed over by the functions on the resulting frozen object.

One nice side-effect of this is that an instance of an object built by this constructor cannot be interfered with:

var pup = Dog({sound: "woof"});

pup.bark()  
// => "woof!!"

pup.bark = function() { return "meow"; };

pup.bark()  
// => "woof!!"

In Clojure

Being much more fond of Clojure than Javascript, I opened a lein repl and figured out the equivalent Clojure code:

(defn Dog [spec]
  (let [sound (:sound spec)
        bark (fn [] (str sound "!!"))]
    {:bark bark})

(def pup (Dog {:sound "woof"}))

(:bark pup)
;; => "woof!""

At this point I realized that this pattern seemed kind of like what I had previously read about Clojure Protocols, a topic which I had not fully understood before. I lookup up clojuredocs.org to read up on Protocols, and came up with the following solution:

(defprotocol Barking
  (bark [self]))

(defrecord Dog [sound]
  Barking
  (bark [self] (str (:sound self) "!!"))

Once the Barking protocol and the Dog record are defined, they can be used like so:

(def pup (->Dog "woof"))

(bark pup)
;; => "woof!!"

Which I found to be most pleasing.

Conclusion

Lets compare the two solutions, in Javascript:

var Dog = function(spec) {

    var sound = spec['sound'];

    var bark = function() {
        return sound + "!!!";
    };

    return Object.freeze({
        bark: bark
    });
};

And in Clojure:

(defprotocol Barking
  (bark [self]))

(defrecord Dog [sound]
  Barking
  (bark [self] (str (:sound self) "!!"))

The Clojure solution is obviously quite a bit shorter than the Javascript solution. It also more clearly indicates what is happening, there is a Barking protocol, consisting of a single function/method, called bark, and the Dog type implements this function, using its own sound property.

So Clojure solutions can be more concise and readable than the equivalent Javascript? Sure, we knew that aleady, but that's not what I found interesting. For me personally this was the light-bulb moment which demonstrated why I would ever want to think about using a Protocol over a plain old data-generating function in Clojure.