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.
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.
But perhaps I'm a bit old school - I explained something with a minor flaw to leave something for the reader to solve :)
Also my example is one step from being open, extensible to any type instead of hard coding string/array which you copied. You can't do this in CoffeeScript in a safe way:
(deftype Slice [start end]
ISlice
(-shift [_] (Slice. (inc start) (inc end)))
IFn
(-invoke [_ x]
(-slice x start end)))it also comes off as a bit dickish to try and refute a point with (what amounts to) "I have more knowledge than you, and there's no point explaining because you wouldn't understand anyway"
In fact, you might argue that OP and PG are saying the same thing. Programming languages are habits of mind, and until you have real understanding of an abstraction, it's a lot easier to think "that looks weird, why don't I just use this other thing I already know".
For example, in David's final gist, we have:
When I think there's a good case to be made for the clarity of this alternative: ... but with careful compile-to-JS languages, it's pretty great that each can do it their own way, and the resulting code can play nicely together.