Jean Niklas L'orange writes technical posts on Clojure internals, persistent data structures, Postgres, and Haskell.
HN commenters recommend his multi-part series on Clojure's persistent vectors as the go-to explanation of how persistent data structures work.
Yes, mutating something at one place in memory is more efficient, because you don't need to allocate new memories.
I don't know all to much about other functional languages, as I learned perl -> ruby -> javascript -> little bit C & Java & Go -> now doing Clojure. But I find Clojures collection data structures interesting. The vector (collection like lists or array) type look immutable, but under the hood are trees. When you append to a vector, you seem get a new vector returned.
In reality, you added the new element as a node in a tree. Then just modified pointers to that the new and old version share almost all of the pointers & allocations. With simple arrays or lists, you would allocate every element anew.
Idk if I can properly explain. Found this blog post very interesting and easier to follow: http://hypirion.com/musings/understanding-persistent-vector-pt-1
Personally, most things in business I find easily parallelisable. You mostly decouple the I/O parts of something with the logic parts of it. But yeah I still have much to learn. Thanks for the link! Interesting :)
The difference (like the author states) is that JS & family has mutable data by default, while Clojurescript defaults to immutable such. You have to specify that you want your vectors and maps etc to be transient (fancy pants word for mutable).
You want immutability - think about it: how large % of your bugs are due to more than one procedure changing the value of one variable, and state getting out of sync?
For me in my JS day job, it is definitely a majority of the bugs we create. We also code very defensively to protect us against this.
In CLJS on the other hand, you'd have immutable data, and in tight loops where allocating objects would be expensive, you make your data transient, and then mutate it as much as you wish.
Living your life with immutable values is a serious improvement. Not only to code, but honestly quality of life.
Edit: Clojure doesn't return whole new data structures under the hood, but you never notice that. Here is a great explanation, prepare for (= (mind article) :blown) => http://hypirion.com/musings/understanding-persistent-vector-pt-1
I can't downvote, but might have as the first sentence is an over-simplication and misunderstanding -- particularly as laziness for collections has always been available in clojure.core. Clojure transducers offer an optimization orthogonal to collections best summed above with: "transducers allow you to define steps in collection processing _per item_ rather than having collection processing as a series of transformations of collections". Yes, transducers can be viewed as somewhat of an analog to the Haskell conduit library (as discussed here several years ago: https://hypirion.com/musings/haskell-transducers). However, I think the distractors coming from strongly typed languages are decidedly missing much of the generalization of the transducer model, particularly those conflating transducers exclusively with streams.
> cloning the entire structure for every applied function sounds like it will be slow? is that not what you will have to do in practice?
In short, no - as an example, when an element is added to an immutable map, a big part of the newly produced map is structurally shared with the old one (check out [0].)
As for the ordering, you could have a list of geometry -> geometry functions. You could then take the initial geometry and apply the list of functions in order, or a permutation of it, or drop some functions, or apply some others multiple times, and so on.
But anyway, it's just an idea - thinking in terms of atomic geometry transformations instead of alternations may or may not be easier in more complex scenarios, I don't know :)
[0] http://hypirion.com/musings/understanding-persistent-vector-pt-1
I'm not sure how it's implemented in Ferret but Clojure's data structures are implemented as trees so there's structural sharing that happens when a vector or hash map is updated or when a new version is produced.
So the whole data structure isn't copied but only the relevant parts are added / updated in the tree and the rest is shared with the "new" data structure.
Mutable data structures are still more memory effective but I'm assuming its fine since the author has a blog post on using ferret on an Arduino Uno which has 32k of ram.
https://nakkaya.com/2017/02/15/bare-metal-lisp-rc-control-using-ferret/
Clojure data Sturucture links:
Persistent Vectors
http://hypirion.com/musings/understanding-persistent-vector-pt-1
Video with Rick hick on them
https://youtu.be/wASCH_gPnDw?t=1742
> Conceptually, it does this by constructing a new copy of the result for every addition, since conj is not destructive
That's not how persistent data structures work
http://hypirion.com/musings/understanding-persistent-vector-pt-1
> why does efficient set union of two collections require one of the two collections to support fast membership tests
I haven't thought much about this but it seems intuitively true to me. You want to take the smaller set, and add each item to the larger set if it doesn't already exist. That last part is why you need a fast membership test.
If you're still unconvinced, try loading up two similarly structured database tables with a million rows and get the union of them (with and without indices). The indexed one will be orders of magnitude quicker.