> What's the difference between this and defining a function in Python/Ruby (rather than monkeypatching)?
Your function can't handle new types in the future that you're not aware of. How, exactly, would I as a BloomFilter writer know about MyIntHolder in order to put it in that conditional? How could a library writer extend it? Your response might be to make a hash table, in which case you have re-implemented an slow, expensive, error prone version of what Clojure is doing under the covers.
> [1] Side question, I didn't understand "(.x this)" in your example, what is .x?
It's like saying "this.x", which is to say "access the public x field." Clojure is in the camp that enforced visibility on data members is generally a bad idea, and it expresses that opinion with deftype.
I understand that Clojure does this under the covers with great speed, but is that the only benefit? I want to reiterate that I'm merely confused here and not trying to argue my silly function with type check is good code. I still feel like something about types/protocols isn't clicking in my brain.
What's the difference between extending a protocol including make-hash and human-readable-hash for MyIntHolder and doing two defmethods that define those and are dispatched on an instance of MyIntHolder? (Sorry if my Clojure lingo is off there, I hope this makes sense)
> How, exactly, would I as a BloomFilter writer know about MyIntHolder in order to put it in that conditional?
Didn't the person writing this need to know about MyIntHolder?
(extend-protocol BloomFilterable
MyIntHolder
...
> It's like saying "this.x"
But what is "x" there? I'm probably reading it too literally, but I see that MyIntHolder takes 1 value (of type int). I don't see any name associated with it, is the "x" just an unrelated example?
Protocols are just a faster, less general version of multimethods. They dispatch only on the type of the first parameter, and they do it quickly.
This speed is important because it allows the java interfaces that define the core constructs of Clojure (such as clojure.lang.IFn, clojure.lang.Seqable, etc.) to be defined purely in terms of protocols, thus bypassing the need for Java altogether. Using multimethods would have been untenably slow.
Protocols can also be useful in that they recognize that certain behavior is defined by a set of functions, rather than just one. Consider the Cantor library (http://github.com/ztellman/cantor), which does simple floating point math. Using only multimethods, it's possible that you could define addition for a type, but not subtraction. With protocols, it's much more difficult to make that mistake.
> Didn't the person writing this need to know about MyIntHolder?
Yes, but they wrote it much later. The library writer only wrote the protocol and coded to the protocol. Your function method is not extensible unless you totally re-implement this mechanism (effectively re-implementing single dispatch). If your question is, "Why can't I get this effect in Python?" then of course you can, but you're basically implementing this feature, and all that entails.
The reason I mentioned monkey patching is to point out how powerful and seductive mechanisms that allow for unforeseen modifications to existing behaviors can be. Clojure provides a fast, well-designed system right out of the box for handling these situations succinctly. Unlike existing methods which are error prone or tedious (monkey patching and retooling dispatch every time, as in your approach), this is elegant and trivial.
> I understand that Clojure does this under the covers with great speed, but is that the only benefit?
Clojure provides one mechanism that can work two ways. Firstly it can provide flexible specifications where code can interface in a functional way, even across library and time boundaries. Secondly it can induct pre-existing types into a behavior pattern without causing a (potentially destructive) ripple effect in existing code.
> But what is "x" there? I'm probably reading it too literally, but I see that MyIntHolder takes 1 value (of type int). I don't see any name associated with it, is the "x" just an unrelated example?
Ahh, it was meant to be ".data". Sorry, there was a version mismatch between two iterations of the code when I was posting. Thank you for catching it, it has been corrected.
Your function can't handle new types in the future that you're not aware of. How, exactly, would I as a BloomFilter writer know about MyIntHolder in order to put it in that conditional? How could a library writer extend it? Your response might be to make a hash table, in which case you have re-implemented an slow, expensive, error prone version of what Clojure is doing under the covers.
> [1] Side question, I didn't understand "(.x this)" in your example, what is .x?
It's like saying "this.x", which is to say "access the public x field." Clojure is in the camp that enforced visibility on data members is generally a bad idea, and it expresses that opinion with deftype.