Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

The last example in the post doesn't really show off the power of protocols (well, it does, but it's buried a bit deep).

In your example (and in David's) the implementation of the slice function is locked into the definition of that function. The real power of protocols comes when you use them to decouple this.

Here's an example (in Clojure because I don't know the names of the types Clojurescript uses off the top of my head, but the ideas are the same):

    (defprotocol Sliceable
      (slice [this start end]))

    (extend-protocol Sliceable
      clojure.lang.PersistentVector
      (slice [this start end]
        (subvec this start end)))

    (extend-protocol Sliceable
      java.lang.String
      (slice [this start end]
        (.substring this start end)))

    (slice "Hello" 0 2)
    (slice [:a :b :c] 0 2)
Here we've defined a protocol called "Sliceable" and added support for it to two built-in classes. We've done so in a safe way -- the "slice" function isn't visible anywhere outside this namespace unless you import it.

Now say you come along and want to use my little "slicing" library to carve up someone else's data structures. You can add support without touching my files or their files:

    (ns foo
      (:use fancylists [:only (FancyList)])
      (:use stevelosh.slicing [:only (Sliceable)]))

    (extend-protocol Sliceable
        FancyList
        (slice [this start end]
          (... slice up the fancylist here ...)))
And now in another file you want to slice up a FancyList:

    (ns bar
      (:use fancylists [:only (FancyList)])
      (:use stevelosh.slicing [:only (slice)]))

    (def my-fancy-list (FancyList 1 2 "cat" "dog"))

    (slice my-fancy-list 2 4)
    ; => (a FancyList containing "cat" and "dog")
Now you've added support for some random person's FancyList library to my Slicing library, without touching the code in either of them. This is the really cool part about Protocols.

This concept actually does appear in David's post when he adds support for his Slice type to the IFn protocol, but it's a bit obscured by the fact that function calling has some syntactic sugar so you don't have to write -invoke.



Yes, that's a much nicer protocol demo.

When you end up writing switches on types in your protocol implementation ... it fails to demonstrate any particular advantage over switching on types in a simple function.


Glad you think so. Thanks stevelosh. I avoided taking the extra step since I was writing for people of varying levels of familiarity with JavaScript, CoffeeScript, and ClojureScript.

But perhaps I'm a bit old school - I explained something with a minor flaw to leave something for the reader to solve :)


As a busy person who finds your ideas interesting but has no time for riddles, I would really appreciate it if you could at least drop some hints here.


In this context this looks somewhat similar to Haskell's type classes, and maybe to a lesser, or greater(?), extent C#'s extension methods. The latter I guess lacks the concept of the "protocol" as extensions methods live at the namespace level in a way (they're wrapped in a static class, but this is somewhat meaningless). Then again, the protocol concept here seems to be mainly used for the same purpose of having a namespace for storing implementations and also importing them?


What seems really interesting to me here is the ability to extend an existing class by implementing an interface (instead of a set of extension methods), and therefore becoming able to cast any instance of this type as an instance of this interface. I'd love to have that in C#, as it would simplify (if not eliminate) the creation of wrappers around types you don't own.


An important difference between Clojure protocols and C# extension methods is that protocols are one of the tools for polymorphism in Clojure, the (only?) other being multi-methods, whereas extension methods are just syntactic sugar for static methods and therefore aren't polymorphic.


Clojure also supports prototype-based polymorphism via Associative objects, inheritance polymorphism via Java interop/proxy, interface-based polymorphism with deftype/defrecord, polymorphism via abstraction (see the Clojure sequence operations for example, which use Java interfaces under the hood but you'd never know....). And probably a few more I can't remember right now..... there's a reason Rich Hickey calls it "polymorphism a la carte".




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: