
Monkey Patching & Gorilla Engineering: Protocols In Clojure - fogus
http://kirindave.tumblr.com/post/658770511/monkey-patching-gorilla-engineering-protocols-in
======
KirinDave
I forgot to mention it (and I guess I'll need a part 2 to fully explain some
of the subtler points), but it's important to note that ANY class can be
extended by these extension methods. Even java.lang.String and its ilk.

That wasn't exactly clear from the post, but it was already getting too long
as it is.

~~~
kemiller
One of the more legitimate uses I've found for monkeypatching is fixing bugs
in libraries you don't control when the maintainer is for whatever reason
unresponsive to your pleas. I.e. replacing someone's broken method despite
lack of a pre-existing contract. Is that supported?

~~~
prospero
Every function in a protocol is namespaced. There's nothing preventing you
from defining a new function with the same name in a different namespace. This
doesn't actually replace anything, though, and any references to the old
function will have to be changed to point to the new one.

It is also possible to extend a protocol a second time for a given
type/record, which will overwrite the previous implementation. This can be
occasionally useful (mostly for incremental development), but for the most
part runs counter to the philosophy of Clojure. Seeing it used in production
Clojure code would raise a lot of alarm bells.

~~~
kemiller
So anything that requires changing all the references to the new one is a non-
starter. I want something that lets me fix it in-place without going and
hacking their source directly.

If you could do something like "monkey patch this, but only for calling code
inside my private namespace" that would probably get you 90% of the way there.

------
samstokes
`defprotocol` and `extend-protocol` remind me of type classes and type class
instances in Haskell. The latter provide the same kind of ad-hoc polymorphism
- i.e. you can tell the compiler that your custom datatype implements a
predefined protocol, and then code that talks to that protocol automatically
works with your new datatype, in a statically type-safe and (I believe)
efficient way.

For example, this (built-in) type class is similar to Java's `toString()`:

    
    
        class Show a where
          show :: a -> String
    
        -- works with any instance of Show
        showTwice :: (Show a) => a -> String
        showTwice s = show s ++ show s
    
        data Fruit = Orange | Apple
    
        instance Show Fruit where
          show Orange = "orange"
          show Apple = "apple"
    
        x = Orange
        show x       -- => "orangeorange"
    

Can anyone more familiar with Clojure (or Haskell for that matter) compare and
contrast?

~~~
fogus
A popular meme that's going around the Clojure community right now is that
Clojure's types and protocols are "dynamic type classes". Clojure's offering
does not provide all of the same things that Haskell's type system gives --
like say, return type based method selection.

------
bretthoerner
So would you use this like so?

    
    
      (make-hash (MyIntHolder. 5))
    

And the benefit here is that you aren't doing "my_int_instance.make_hash()" so
everyone can have their own definition of make-hash, right?

What's the difference between this and defining a function in Python/Ruby
(rather than monkeypatching)?

    
    
      # Python
      def make_hash(val):
        if not isinstance(val, MyIntHolder): raise TypeError
        return val.data
    

That's a namespaced function that checks it was passed a MyIntHolder and
returns the data[1]. Each library/module can define its own version.

I mean this as an honest (and stupid) question. I'm fascinated with Clojure
and am working on learning more, but none of the Protocol examples I've seen
have given me that "Ah ha!" moment I've had with so many other parts of
Clojure. Can you help me understand?

[1] Side question, I didn't understand "(.x this)" in your example, what is
.x?

~~~
KirinDave
> 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.

~~~
bretthoerner
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?

~~~
prospero
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.

------
mahmud
A programming article with no code? I hope this doesn't become a trend.

