top | item 10295060

Functional-navigational programming in Clojure with Specter

105 points| dantiberian | 10 years ago |nathanmarz.com | reply

33 comments

order
[+] rtpg|10 years ago|reply
Wow, I love this. My impression is that Specter is (at least in main use cases) Haskell's lens package. But while lens is very inside-baseball (at least in the tutorials I read), this explanation describes an extremely useful principle.

Every language needs a lens/Specter. If you're spending a lot of time messing with data, this article will go directly to your heart.

[+] dj-wonk|10 years ago|reply
I think https://www.fpcomplete.com/school/to-infinity-and-beyond/pic... does a nice job of explaining a lens:

> At its simplest, a lens is a value representing maps between a complex type and one of its constituents. This map works both ways—we can get or "access" the constituent and set or "mutate" it. For this reason, you can think of lenses as Haskell's "getters" and "setters", but we shall see that they are far more powerful.

[+] skratlo|10 years ago|reply
Exactly what I thought, either lens or zippers.
[+] escherize|10 years ago|reply
I've read about specter before on its github[1], and I must admit I didn't see the bigger picture that is explained in the opening example of this article.

I thought I could get by with just using:

    (-> huge-thing :key1 :key2 first ...)
But now I actually grok the value behind Specter! Great stuff, and I will use it next time I begin up a re-frame[2] app!

[1] https://github.com/nathanmarz/specter

[2] https://github.com/Day8/re-frame/

[+] tel|10 years ago|reply
> Any connection to transducers?

Yep!

On the fancy Haskell lens hierarchy (hackage.haskell.org/package/lens) we have the notion of a fold or a "getter which touches multiple items". The existence of a fold for a type like `Fold s a` indicates that we can extract from the type `s` some number (0 to many) `a` values in sequence. This is the idea of "Foldable" in Haskell.

Given a foldable type `s` and a transducer we execute the transducer by passing the "build" reducer in and then "visiting" each value `a` inside of `s` with the reducer that the transducer returns (modulo the early stopping bit which is just sort of a Clojure-specific optimization). Essentially, the transducer is a notion of "visitation" which is invariant to how the final summary is constructed—essentially the same thing that's captured in the "getter which touches multiple items" of a Fold.

So there really ought to be a way to treat any specter optic as a possibly very limited transducer. Essentially, the "read" component of a lens will correspond pretty directly.

We can also see this by remembering that any pure transducer is semantically equivalent to a function `a -> [b]` which you can read as a way of finding 0-to-many `b` values "inside" of `a`.

[+] OliverM|10 years ago|reply
I like Specter, but it doesn't quite give me the holy grail it promises. My main pain-point is that it assumes I know the route to the datapoint I want to change. What if all I know is the general shape of a subset of data, and also that that shape might appear none or more times in my client dataset?

I'd love to be able to describe features of a data structure and have all qualifying elements be transformed, without my having to tell the select function how to get to those data structures in the first place; just hand it a data structure, and have it grovel through looking for matching subsets, then transforming them as desired.

Sort of like a grammar/DSL for Clojure data structures, without having to be specified from the basic character level up.

[+] nathanmarz|10 years ago|reply
It sounds like you're looking for the "walk" selector, which comes with Specter:

(select (walker number?) {2 [1 2 [6 7]] :a 4 :c {:a 1 :d [2 nil]}})

=> [2 1 2 1 2 6 7 4]

I've personally found this to be a use case that comes up infrequently.

[+] norswap|10 years ago|reply
I like the idea.

Here's another that seems similar to it: an immutable language wherein you could create a mutable, write-only copy of an object. After writing all that was required, the copy could be finalized, at which point it becomes read-only (hence immutable). This is a bit like the builder pattern, but I've never seen a language with built-in support for it.

Thoughts? Is it much different than what the OP proposes?

[+] programnature|10 years ago|reply
Clojure/script already has this. Its called transients
[+] hellofunk|10 years ago|reply
This is pretty nifty. What is so exciting about Clojure is that the language does fully realize the original lisp intention of creating building blocks on which any paradigm or style of programming can be built.
[+] Guthur|10 years ago|reply
How exactly was that not realisable before?

Common Lisp is a multi-paradigm programming language, arguably more so than Clojure because it has a fully featured multi dispatch object system as well.

[+] tel|10 years ago|reply
Oh! I'm glad to hear about this. I was getting painfully close to having to implement it myself...
[+] dj-wonk|10 years ago|reply
I find the use of all-caps to be non-idiomatic. Take `LAST`, for example.
[+] nathanmarz|10 years ago|reply
Regular functions are treated as filters, so that's done so that things like ALL, LAST, etc. are clearly delineated as something different than filters.
[+] porker|10 years ago|reply
I don't understand how it's doing it, but it looks seriously cool - like a paradigm I want to start programming with.
[+] porker|10 years ago|reply
To whoever downvoted this - instead of doign so, why not point me to a beginners-guide? I have read whatever I've found so far...
[+] th0ma5|10 years ago|reply
Reminds me of SPARQL.
[+] billrobertson42|10 years ago|reply
Reminds me of XSLT.
[+] dj-wonk|10 years ago|reply
Lenses (as a construct) can encompass traversal, getting, setting, and transformation. With this in mind, I'd suggest that lenses are closer to a combination of XPath and XSLT. A key difference is that no schema is imposed.

That said, I'm a little sad to hear that lenses remind you of an XML technology. :) I suppose I'd rather that XSLT be perceived as a very particular way of doing data transformation under particular conditions with a particular syntax.

All of the above said, I think good lessons could (in theory) be learned from XML, but I feel like XML's notational particulars get over-accentuated and the underlying thinking about data structures gets muddled and even lost.