In the book "The Joy of Clojure", defprotocol
is offered as a solution to the expression problem -- "the desire to implement an existing set of abstract methods for an existing concrete class without having to change the code that defines either."
The example given is as follows:
(defprotocol Concatenatable
(cat [this other]))
(extend-type String
Concatenatable
(cat [this other]
(.concat this other)))
(cat "House" " of Leaves")
;=> "House of Leaves"
(extend-type java.util.List
Concatenatable
(cat [this other]
(concat this other)))
(cat [1 2 3] [4 5 6])
;=> (1 2 3 4 5 6)
It is suggested that this is not possible in a language like Java, but how is it different than the following?
public class Util {
public static String cat(final String first,
final String second) {
return first.concat(second);
}
public static <T> List<T> cat(final List<T> first,
final List<T> second) {
final List<T> list = new List<T>(first);
list.addAll(second);
return list;
}
}
After all, both are used similarly:
(cat "House" " of Leaves")
Util.cat("House", " of Leaves");
The Clojure function cat
is not a method on the String
and List
classes, but rather an independent function that is overloaded to accept either String
or List
arguments.
Although I really like Clojure, I don't understand the claims of superiority for this construct.
Okay. You release this cat
Java library to much fanfare, and everyone downloads it. It's so great I want to make my own TVCommercial
type be concatenable so that I can send it to bits of your library that operate on concatenable objects.
But I can't, because you call Util.cat(obj1, obj2)
, which has no overload for TVCommercial
. I can't extend your code to handle my types, because I don't own your code.
You can define Concatenable as an interface to address this problem:
interface Concatenable {
Concatenable cat(Concatenable other);
}
But now I can't write a class which is both Concatenable and... I don't know, an AnimalHandler, that handles cat
s. Clojure's protocols solve both problems by decentralizing the dispatch functions and implementations: they live all over the place, rather than in some single location. In Java, you choose between:
Clojure basically does the latter of these, but because it uses namespaced names, there's no danger of conflict with other protocols that think cat
is a good function name.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With