Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Difference between Clojure Protocols and Groovy Categories

Having recently seen a presentation of Clojure Protocols, I was quite impressed by the clean way extensions to existing types can be done this way. However, I was pretty sure to have already seen a similar way of doing this in some other language and after some time I found out it was Groovy Categories.

Compare this:

 @Category(String) ​class StringCategory {
   String lower() {
     return this.toLowerCase()
   }
 }

 use (StringCategory) {
   println("TeSt".lower())
   assert "test" == "TeSt".lower()
 }

to the Clojure Protocol equivalent (taken from mikera's answer below and tested in ideone.com)

 (defprotocol Lowerable
   (lower [x]))

 (extend-protocol Lowerable
   String
     (lower [s] 
       (.toLowerCase s)))

 (println (lower "HELLO"))

my question is:

  1. besides from performance differences (it is said that Clojure is highly optimized in this regard) - is there a semantic difference between the two approaches?
  2. besides the clumsy syntax, is there anything logically wrong with the Groovy approach?

Disclaimer: I am a complete Clojure newbie!

like image 825
Ice09 Avatar asked Mar 29 '12 23:03

Ice09


2 Answers

Here's the rough equivalent Clojure code using protocols:

(defprotocol Lowerable
  (lower [x]))

(extend-protocol Lowerable
  String
    (lower [s] 
      (.toLowerCase s)))

(lower "HELLO")
=> "hello"

The key distinctions to note about Clojure protocols (which I believe make it distinctive from the Groovy categories version)

  • The Clojure protocol definition does not contain any implementation (it's more like an interface in this respect). The implementation is provided separately: you can extend the Lowerable protocol to as many different classes as you like without needing to make any alterations to either the classes themselves or the protocol definition. For example, you could define lower to work on a Rope.
  • Your Groovy category above is specialised for Strings - this is not the case with Clojure protocols. In this example, the Clojure protocol "Lowerable" is defined without saying anything about the type of arguments.
  • lower is a proper first class function. So you can use it to build higher order abstractions (via higher order functions) that in turn will accept any arguments to which the Lowerable protocol has been extended.
  • Clojure protocols are heavily optimised since they are designed to exploit the fast method dispatch of the JVM. They therefore get compiled down to very efficient code (there is no dynamic object examination or reflection required)

Clojure protocols are actually a fairly unique solution to the Expression Problem (linked video is pretty interesting). I think the closest equivalent to Clojure protocols in another language is actually Haskell type classes. Even then it's a bit of a stretch since Haskell is statically typed and Clojure is dynamically typed....

like image 149
mikera Avatar answered Oct 22 '22 05:10

mikera


The Clojure feature he's referring to looks like:

(defprotocol StringMunging
  (lower [this]))

(extend-protocol StringMunging
  String
  (lower [this]
    (.toLowerCase this))

  clojure.lang.Keyword
  (lower [this]
    (keyword (lower (name this)))))

user> (lower "TeSt")
"test"
user> (lower :TeSt)
:test

Implementations can be added for any type at any time - there's no need for the two implementations I wrote to cooperate in any way.

However, I don't understand the Groovy well enough to make any substantive comments on the question itself; I can only help describe the question's Clojure side.

like image 35
amalloy Avatar answered Oct 22 '22 06:10

amalloy