Earlier quoted context omitted.
IIUC Rich Hickey probably did just that too, writing ad-hoc version of clojure semantics in cpp before making his own language enforcing this.
I recall he was big into SBCL, but most IT organizations wanted all code to run on the JVM or CLR. So he had to make a Lisp to run on the JVM and Armed Bear Common Lisp apparently wasn't exactly what he wanted. I want to learn Clojure, but there are definitely some road blocks. I don't have the time for Emacs, it'd be a pain to get a Cursive license (although the cost is extremely reasonable I'd have to do paperwork…
Clojure’s Approach to Identity and State (2008)
31–40 of 69 posts
Re: Clojure’s Approach to Identity and State (2008)
#32This is interesting. I've tried Clojure, and heard about the idea of avoiding mutable data and using pure functions plenty of times, but imperative/OOP have still always made the most sense to me. When reading this though, something clicked because I've encountered the problem of getting a stable state to read/write without blocking other operations, and dealt with it in C++ in a similar way to Clojure without realiz…
Re: Clojure’s Approach to Identity and State (2008)
#33Immutability is very helpful, and the connection between values and identity is illuminating. But there have been very important developments in programming languages since this post/page was written: notably, the introduction of "borrow checking" (exemplified by Rust's implementation). Borrow checking has a very significant positive effect on the sustainability of imperative code, which makes the claim that "imperat…
But in Clojure it's not a problem. I find lots of applications where working with stale, consistent data is just fine.
Also, if you ever need to rewind your state Clojure's immutability makes it trivial. I've used this in e.g. latency compensation for multiplayer games.
Re: Clojure’s Approach to Identity and State (2008)
#34This is interesting. I've tried Clojure, and heard about the idea of avoiding mutable data and using pure functions plenty of times, but imperative/OOP have still always made the most sense to me. When reading this though, something clicked because I've encountered the problem of getting a stable state to read/write without blocking other operations, and dealt with it in C++ in a similar way to Clojure without realiz…
This is pretty much how clojure atoms [0] work. It's basically a Clojure wrapper around a Java AtomicReference, but Clojure's immutable data structures make an atomic reference type really useful because it is very cheap to read a "snapshot". It doesn't do upfront allocation, because like you mentioned, that requires you to have some knowledge about how the accessing code works. Additionally, whatever you are doing i…
I was focused on situations with just one writer (and originally also one reader), with the main thing being avoiding allocations. The situation where future values actually depend on past values, and specifically the current past value with other writers in the mix, is definitely trickier.
Re: Clojure’s Approach to Identity and State (2008)
#35Immutability is very helpful, and the connection between values and identity is illuminating. But there have been very important developments in programming languages since this post/page was written: notably, the introduction of "borrow checking" (exemplified by Rust's implementation). Borrow checking has a very significant positive effect on the sustainability of imperative code, which makes the claim that "imperat…
Rust doesn't let you have a mutable reference while you have immutable ones. It's only a problem in Rust because mutating the underlying data would cause problems for chunks of code using the immutable data. But in Clojure it's not a problem. I find lots of applications where working with stale, consistent data is just fine. Also, if you ever need to rewind your state Clojure's immutability makes it trivial. I've use…
Re: Clojure’s Approach to Identity and State (2008)
#36This has been extremely useful to me while writing a (somewhat optimizing) compiler for spreadsheets. I can do subtree deduplication just by `assoc`ing into a map.
I want to hear more about your somewhat optimizing compiler for spreadsheets!
We are working with LibreOffice Calc ODS sheets, which are pretty terrible as a format (since the references are not normalized in the formulas, they can't repeat them even when they behave identically, and they duplicate most of the XML namespaces in the attributes).
We parse and normalize the references from A1 to R1C1 form, and then deduplicate the formulas (by text) and extract all of the immediates (and mark some of them as input, so that they can be varied at runtime).
Then we pass the deduplicated formulas through instaparse (which is spectacular) with a relatively simple grammar, and propagate some of the constants.
I then extract the references from the AST, while at the same time replacing SUMIF/MINIFS/MAXIFS/AVERAGEIF and similar with simple addition/min/max of known cells, where the tests are known at compile time. Then those ASTs are complied to functions (ignoring our cross-function optimizations).
Then it's just down to generating a complete DAG of dependencies, and using that to sort the assignments (cells) topologically. The sheet can be evaluated naiively at that point by injecting the references into each subsequent assignment/cell and storing the result in a map (ranges injected as a seq over a range).
There's a lot more to it, and it's getting better all the time, but that's the gist of it. Many real spreadsheets are not well-behaved, and they have dependency patterns which are more difficult to handle (i.e. ranges that refer to the current cell, or future cells, dynamically). The compiled output is getting more and more static, and will probably be reduced to some form of ssa, possibly even well-formed enough to be popped casually into LLVM.
It would be some help if the ODS format were improved, it takes several seconds just to parse the hundreds of megabytes of XML in our amazing spreadsheet, and a lot of it is redundant.
Re: Clojure’s Approach to Identity and State (2008)
#37Earlier quoted context omitted.
Regarding values: you can construct the same datastructure in any place, and compare it meaningfully with a datastructure from a completely different source (and you can do so efficiently). This is accomplished, as far as I know, by representing almost everything as persistent hash trees (with some implementation voodoo and shortcuts). Beyond that, you can actually just read the Clojure runtime code. It's a bit messy…
Persistent hash tries [1] I have an efficient C# implementation here [2] [1] https://michael.steindorfer.name/publications/phd-thesis-eff... [2] https://github.com/louthy/language-ext/blob/master/LanguageE...
> Compressed Hash Array Map Trie
Q: “What datastructure would you like?”
A: “Yes.”
Re: Clojure’s Approach to Identity and State (2008)
#38Immutability is very helpful, and the connection between values and identity is illuminating. But there have been very important developments in programming languages since this post/page was written: notably, the introduction of "borrow checking" (exemplified by Rust's implementation). Borrow checking has a very significant positive effect on the sustainability of imperative code, which makes the claim that "imperat…
Borrow checking and immutability solve two different problems. Immutability is about the absence of ownership and state, while borrow checking is a way to manage ownership. One does not replace the other, they coexist solving different problems.
Re: Clojure’s Approach to Identity and State (2008)
#39Earlier quoted context omitted.
Borrow checking allows even structures that support mutation to be safely (checked by the compiler) treated as immutable, and thus as values. Clojure also recognizes the connection between ownership and mutability in its "transients": https://clojure.org/reference/transients ... compile time borrow checking extends that idea to an entire language.
What do transients have to do with ownership? They are simply a way to gain new performance characteristics from an existing data structure.
Re: Clojure’s Approach to Identity and State (2008)
#40Immutability is very helpful, and the connection between values and identity is illuminating. But there have been very important developments in programming languages since this post/page was written: notably, the introduction of "borrow checking" (exemplified by Rust's implementation). Borrow checking has a very significant positive effect on the sustainability of imperative code, which makes the claim that "imperat…
Borrow checking and immutability solve two different problems. Immutability is about the absence of ownership and state, while borrow checking is a way to manage ownership. One does not replace the other, they coexist solving different problems.