4 ms·
This 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 sti
by etbebl 7y ago
This 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 realizing it at the time.
I have this little lightly-tested library: https://github.com/tne-lab/rw-synchronizer https://github.com/tne-lab/rw-synchronizer. I'm not using it much currently but have played with it a lot while building extensions to Open Ephys. The idea being that as a reader, you get a "snapshot" of the last thing that was written, but it's really just one of several copies, and subsequent writes can happen on the other copies. So you never really modify the current data, just push newer versions of it. The cool thing is, if you know how many simultaneous readers you'll need ahead of time, all the allocation can be done upfront, so then if you have a real-time loop or something, all it needs to do is exchange pointers.
If I ever get around to it, the next thing I would do is allow any writer to also read the latest value, so it can use a transformation to create a new one. Maybe even do it automatically with copy-on-write semantics? On the other hand, I'm probably reinventing the wheel here...
- agumonkey 7y agoIIUC Rich Hickey probably did just that too, writing ad-hoc version of clojure semantics in cpp before making his own language enforcing this.
- 6thaccount2 7y agoI 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 at work), and I don't know the JVM or Java well.
- tosh 7y agoVisual Studio Code, Atom and Vim also have great Clojure support. (edit: might be worth to add editors and IDEs to the Clojure landing page to illustrate that there are many solid options by now) With ClojureScript you can leverage JavaScript runtimes like browsers. The ClojureScript testsuite also passes using the recently released hermes runtime (https://twitter.com/mfikes/status/1149360258994847745 https://twitter.com/mfikes/status/1149360258994847745) edit: Just wanted to add that being familiar with Java or the JVM isn't necessary to get into Clojure. It definitely is not a prerequisite :)
- 6thaccount2 7y agoI'll have to check out the Atom and VSCode options. Thanks for the heads up. Basically all I need is paredit, some basic intellisense, and be able to see the project heirachy. I like the idea of Clojurescript, but don't currently have any reason to do web development.
- didibus 7y agoIf that's all you need, I think Nightcode will do: https://sekao.net/nightcode/ https://sekao.net/nightcode/ It's a simple Clojure IDE written in Clojure.
- slifin 7y agoIf you're learning Clojure then you can use cursive for free until you go commercial with it?
- 6thaccount2 7y agoI believe there is a limited trial yes, but corporate IT always has hoops to go through
- vnorilo 7y agoIn addition to the editors others have suggested, I've been quite happy writing Clojure in Sublime Text. In ST, you might want the lispindent and paredit plugins on top of the main Clojure plugin.
- vnorilo 7y agoI was at the exact same spot, apart from my (not purely rational) dislike of OOP. I was envisioning a STM-based concurrency mechanism along with collections with value semantics. Spoiler: today I write quite a bit of Clojure.
- 6thaccount2 7y agoCan you give me the ELI5 on STM & the Actor model? The article points out why Actor has issues, but I don't fully understand STM.
- vnorilo 7y agoIdentities change their associated values (state) by transactions. That means that from the viewpoint of concurrent observers (threads), partial or interrupted updates are never seen - the update is either 0% or 100% completed. In Software Transactional Memory, that can be accomplished with atomic swaps. In Clojure, a new value, no matter how large, is constructed and the transaction is completed by repointing a mutable reference (ref or atom) to the new value atomically. Clojure has plenty of tools for constructing such transactions, such as `update-in` [1]. In order to make this work well, we need to be able to make collections behave like values. So, when you associate a value to a key in a Clojure collection, the original collection is unmodified and a new version is returned. This plays well into updating collections with STM - you just swap the root reference to a new collection. Any transactions must be indempotent, that is, not touch the program state in any way, just produce a new value - because the STM system might need to retry the transaction. Retries happen when multiple threads try to modify a bit of shared state. In Clojure, `swap!` [2] is the actual mutation bit. You provide the transaction function to `swap!`, which produces a new value from the current state of a mutable reference. If, during the computation, another thread has swapped in a new value, the transaction is retried based on that updated value. On some architectures, this system can be implemented without locks, using the atomic compare-swaps of the hardware. The happy path of no conflicts is very efficient, while a heavily contested updates will result in redundant discarded (due to retry) transactions. Please let me know if I can better explain anything! [1] https://clojuredocs.org/clojure.core/update-in https://clojuredocs.org/clojure.core/update-in [2] https://clojuredocs.org/clojure.core/swap https://clojuredocs.org/clojure.core/swap!
- fazzone 7y agoThis 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 in Clojure is pretty likely to allocate memory anyway, so it probably wouldn't be that beneficial. [0] https://clojure.org/reference/atoms https://clojure.org/reference/atoms
- etbebl 7y agoOh neat, thanks! Yup, that sounds like a more general/flexible version of what I was trying to do. 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.