Clojure's missing piece (2017)
nathanmarz.com
nathanmarz.com
It would be more idiomatic to have a hiccup-like API, where data is passed - not code, not syntax. And the keywords should be namespaced, so meaning is self-documented (and one gets superior IDE support).
- paths as data
- navigators as transducers
First I started using plain clojure code, then played with using xml-in [1], then created my own transducers and channels to build a dataflow-like pipeline manually, and finally started using specter because I wanted paths to be a data structure I can use to build the pipeline programmatically instead of manually. For that I needed a way for paths to become tranducers (which specter traverse-all does [2]), and I also needed paths to be a sequence of literals or "interned" elements [3] such as keywords or symbols, but not functions created on the fly. That way I can create a tree from a sequence of paths, using something similar to this SO answer [4], and build the pipeline. I'm almost there, I should end up in a situation where I can specify a list of paths to extract from an XML document, and the dataflow pipeline needed will be built automatically, with an input channel for the parsed XML, and output channels for each given path. If you use clojure.data.xml instead of clojure.xml, my hope is to have a one pass XML parsing of larger-than-RAM XML docs.
[1] https://github.com/tolitius/xml-in
[2] https://github.com/nathanmarz/specter/wiki/List-of-Macros#tr...
[3] http://www.yourdictionary.com/interning
[4] https://stackoverflow.com/questions/49515858/clojure-convert...
I take them apart to something flatter as early as possible, but the complexity is nevertheless unavoidable.
(changing the upstream representation would involve changing an entire industry as well as a standard)
In essence yes, the maps I see being bandied about in clj tend to have tree structures.
Haven't seen much/any use of graphs in clj code. Deep nesting has become almost idiomatic at this point from my POV.
Could you provide example/pointer to clj code using a graph in lieu or in addition to a map?
user ^. id
txt ^? _JSON . key "foo"
txt ^?! _JSON . key "foo"
First one gives exactly one result. The second one is the equivalent of returning null on failure. The third one throws an exception on failure.And because of the somewhat peculiar implementation of van laarhoven lenses we get subtyping so it doesn't get in the way.
The reason I came to like Haskell is that they tend to ground things in (type/category) theory, which gives you extremely strong foundation (the downside is that sometimes things get so abstract that it's really tough to understand them).
I don't disapprove the Clojure (and Lisp) approach of experimentation with new features, rather than trying to figure out the theory, but it can lead to unforeseen problems where you make design mistakes that are not obvious at the first sight.
A good example is a recent discussion https://news.ycombinator.com/item?id=18565826 about Maybe. There are several good arguments in the thread against the union types, which are very easy to miss at first glance.
So, I would recommend you dive into the theory of Lenses and Traversals, to see if you can avoid some mistakes you're perhaps doing (AFAIK there are several different definitions of lens in Haskell). As they say, a month in the lab can save you an afternoon in the library. Although I agree that lab is much more fun!
A lens just happens to guarantee you can always get the element you're looking for and so more restrictive functions that cannot tolerate failure to find an element must use lenses.
The more fundamental divide is between lenses and prisms (which are also a kind of Traversal).
http://hackage.haskell.org/package/lens-4.17/docs/Control-Le...
Which I still maintain is the most hilarious API I have ever seen in my life.
https://groups.google.com/d/topic/clojure/qN1UPMVQmaM/discus...
https://news.ycombinator.com/item?id=18077612
My comments:
https://news.ycombinator.com/item?id=18080968
https://news.ycombinator.com/item?id=18086937
I'm not familiar with several of the functional programming terms brought up so far, but I think the gist of them is that all data structures need deterministic iteration.
So for example, I can understand how returning a copy of an immutable map might result in a new map whose values are in a different order than the original. But that isn't acceptable. Either the map's order is determined by aspects of its values (it's deterministic), or it's not. This might even need to be true for structures like sets that aren't thought of as having order.
This is a pretty serious situation for not just Clojure but a whole host of FP languages. Maybe this determinism is as important as immutability or process isolation. It certainly should rank higher than the need to minimize bloat. My vote is for Clojure to address it, even if it doesn't incorporate Specter.
https://stackoverflow.com/questions/36851766/histomorphisms-...
Oh, and what is a zipper as a data structure?
Zippers can be seen as (or at least isomorphic to) the derivative of a type with respect to one of its type parameters. This is described in Conor McBride's paper "The Derivative of a Regular Type is its Type of One-Hole Contexts."
http://strictlypositive.org/diff.pdf
If a video is more your speed, Kenneth Foner gave a great talk titled "`choose` your own derivative" that takes the concept of derivatives of types and takes it a step further. You can skip to the 11 minute mark to watch his explanation of zippers as derivatives here:
https://www.youtube.com/watch?v=79zzgL75K8Q&t=11m
With that said, the standard go-to paper for Zippers would probably be "The Zipper" by Gerard Huet: