4 ms·
One man's "reusable abstraction" is another man's "why the hell is this thing being abstracted?" ... perhaps with Lisps more than anywhere else ;) For example,
by jashkenas 15y ago
One man's "reusable abstraction" is another man's "why the hell is this thing being abstracted?" ... perhaps with Lisps more than anywhere else ;)
For example, in David's final gist, we have:
(defprotocol ISlice
(-shift [this]))
(deftype Slice [start end]
ISlice
(-shift [_] (Slice. (inc start) (inc end)))
IFn
(-invoke [_ x]
(cond
(string? x) (.substring x start end)
(vector? x) (subvec x start end))))
(def s (Slice. 0 5))
(def v ["List Processing" [0 1 2 3 4 5 6]])
(map s v)
;; >> ("List " [0 1 2 3 4])
(map (-shift s) v)
;; >> ("ist P" [1 2 3 4 5])
When I think there's a good case to be made for the clarity of this alternative:
slice = (start, end) ->
(list) ->
if typeof list is 'string'
list.substring start, end
else
list.slice start, end
vector = ["List Processing", [0, 1, 2, 3, 4, 5, 6]]
vector.map slice 0, 5
# ["List ",[0,1,2,3,4]]
vector.map slice 1, 6
# ["ist P",[1,2,3,4,5]]
... 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.
- programnature 15y ago"But when our hypothetical Blub programmer looks in the other direction, up the power continuum, he doesn't realize he's looking up. What he sees are merely weird languages. He probably considers them about equivalent in power to Blub, but with all this other hairy stuff thrown in as well"
- cubicle67 15y agoyeah, I see what you're trying to do here, except you're calling Jeremy a blub programmer when he's anything but. 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"
- programnature 15y agoNot trying to refute anything. Just a passage that comes to mind when reading that opening sentence. Especially with the specific reference how that seems to be common occurrence with Lisp. 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".
- stevelosh 15y agoThe 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.
- jashkenas 15y agoYes, 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.
- swannodette 15y ago
- swannodette 15y agoExcept isn't that missing the actual point? The example was contrived to drive home that we can use objects as functions in ClojureScript - I respect that the engaged reader understands that these tools must be applied tastefully. I could have implemented it just like you've shown but would we have learned anything? :) 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)))
- moomin 15y agoIsn't this just the "the example code that shows off a feature can be implemented more simply" gambit? The point is always that the code that really needs the feature is too long to fit in an example.