Live data from Hacker News

Clojure, the Good Parts

rasterize.io

31–40 of 96 posts

Re: Clojure, the Good Parts

#31
post #15

Earlier quoted context omitted.

Haskell noob here. How is STM used in Haskell? What is type-driven STM? In Clojure, I don't recommend it because it's Just Another Place To Stick State. It's cool, but provides little semantic value over compare-and-set! on an atom.

Aren't atoms a part of STM in clojure?

Nope, atoms are based on simple CAS (Compare And Swap) operation that replaces the reference value. They are pretty much a sugar around AtomicReference in Java (in case of JVM implementation of course).

It is pretty much as like this

  1. Dereference a variable and keep the old reference.
  2. Create a new modified variable.
  3. Use CAS on old reference and new variable 
          to update the reference to point to the new vairable.
    In case of failure (some other thread made the update before) go to 1. 
But the version of SMT implemented in Closure is very similar to the CAS, just now you can apply it to the multiple variables i.e. it is multi variable CAS.

It has the same drawbacks and advantages as atomic references: your code does not depend on lock taking order (as they are managed by the transaction manager) and you avoid dead locks as the locks are taken in the same order before applying the update, but now two different threads can start the transaction to update the same variable, but only one of them can finish as the first one and the other one should be cancelled and repeated.

In principle you can get away with a simple atomic reference (that is much faster) when the set of the updated variables is always the same - you make a pair of them and update them together.

Re: Clojure, the Good Parts

#32
post #15
post #10

It's sad to see that STM is not recommended. It is hugely important in Haskell. I jump to believe that this is an artifact of type-driven STM, but it may be something else instead. I've recently moved from Component to Mount. It is less powerful but just a vast syntactic overhead reduction. Absolutely avoid metadata. The worst thing ever is using it like how Reagent does in ClojureScript where it actually provides im…

Haskell noob here. How is STM used in Haskell? What is type-driven STM? In Clojure, I don't recommend it because it's Just Another Place To Stick State. It's cool, but provides little semantic value over compare-and-set! on an atom.

> but provides little semantic value over compare-and-set! on an atom

Can you compare and set on multiple atoms at the same time? If not, how do you make updates atomic without using either STM, a global lock, or re-implementing STM yourself with an incremental locking and rollback system.

Re: Clojure, the Good Parts

#33
I agree with most of the recommendation in this article (thanks for publishing it!), but the advice about futures should be taken with a grain of salt. You need to be careful with futures because they swallow exceptions. However, the solution given (setting a global exceptions handler), while sound advice in general, doesn't actually solve the problem of hidden errors, as futures catch all exceptions and return them (so the caller can handle the failure in whatever way it prefers).

If your goal is "fire and forget" behavior (e.g. sending off a confirmation email), then it's better to use java.util.concurrent.Executors directly. It's actually easier than it sounds: https://gist.github.com/pesterhazy/c41786690ee0f8a3e5a21f88f.... Also see https://stuartsierra.com/2015/05/27/clojure-uncaught-excepti...

Re: Clojure, the Good Parts

#34
post #29
post #15

Earlier quoted context omitted.

Haskell noob here. How is STM used in Haskell? What is type-driven STM? In Clojure, I don't recommend it because it's Just Another Place To Stick State. It's cool, but provides little semantic value over compare-and-set! on an atom.

If you're using an atom, you're already sticking state in a place; the problem with atoms is choosing granularity. If one atom has everything in it, it will become a point of contention. If your compare-and-set returns false, what do you do? STM resolves these issues by letting you coordinate updates to more granular, finer-scoped refs

The drawback here is that the STM (at least the Clojure implementation few years ago when I tested it) is orders of magnitude slower than simple atomic reference.

It actually does not have to be, but this is how it is (was) implemented. I built a simple STM (in Java) as part of my bachelor thesis and its performance was comparable to the atomic reference.

Re: Clojure, the Good Parts

#35
post #10

It's sad to see that STM is not recommended. It is hugely important in Haskell. I jump to believe that this is an artifact of type-driven STM, but it may be something else instead. I've recently moved from Component to Mount. It is less powerful but just a vast syntactic overhead reduction. Absolutely avoid metadata. The worst thing ever is using it like how Reagent does in ClojureScript where it actually provides im…

It's sad to see that STM is not recommended.

Actually, this is not recommendation for a specific use case - a (web based) database application.

For applications that require to manage state internally it might be still way to go (sorry, I am not a Clojure user).

Re: Clojure, the Good Parts

#36
The article has some fairly reasonable rules of thumb to follow. For whatever reason I rarely find refs or agents to be useful, and lexical scoping is more limited and predictable than dynamic scoping.

That said, I feel it's a little too harsh in places. Metadata is fine so long as you don't expect it to persist to derived data structures. If you think of metadata as being about a particular piece of data and nothing else, then you won't run into problems. One place I find metadata particularly useful is annotating event data, which could allow, for example, treating events from different sources with different priorities.

I find that there are a lot of specialised tools in Clojure. Most of the time it's good advice to avoid them, but in the rare cases where they are needed, you're glad to have them. Keyword inheritance, for instance, is something I've been using recently, yet I've rarely had need of it before.

Re: Clojure, the Good Parts

#37

Earlier quoted context omitted.

The problem with that is that if `put!` or some other function is used inside `map` or some other lazy context, it may get realized outside the context of `with-read-txn`. Making `binding` part of your public API breaks referential transparency.

So what would you recommend that function look like? I think it is bad taste to say (with-read-txn db-name (put! db-name)) - no reason you should specify again what db you are dealing with.

There's plenty of reason: you can put any code you like inside of `with-read-txn`, including closures that are evaluated lazily. If you dislike the duplication, create a syntactic form that allows less, like this:

    (transaction-> db-name
      (put! ...)
      ...)
This doesn't lend itself to every kind of action, but it is narrower. Alternately, create a variadic version of `put!` which guarantees eager evaluation inside of a transaction.

The nightmare scenario here is not that the sequence will lazily evaluate outside of `with-read-txn`, because that at least will throw an error. Rather, it's that it will be evaluated inside a different transaction, without anyone ever realizing it. By leaving that possibility open, you're doing a huge disservice to the users of your library.

Re: Clojure, the Good Parts

#38
post #29

Earlier quoted context omitted.

If you're using an atom, you're already sticking state in a place; the problem with atoms is choosing granularity. If one atom has everything in it, it will become a point of contention. If your compare-and-set returns false, what do you do? STM resolves these issues by letting you coordinate updates to more granular, finer-scoped refs

The drawback here is that the STM (at least the Clojure implementation few years ago when I tested it) is orders of magnitude slower than simple atomic reference . It actually does not have to be, but this is how it is (was) implemented. I built a simple STM (in Java) as part of my bachelor thesis and its performance was comparable to the atomic reference.

If all you need is a simple atomic reference, then great.

I'm not convinced a single atomic reference sufficiently covers enough use cases, however.

Re: Clojure, the Good Parts

#39
I am not sure about this article. Using Clojure for ~6 years I know one thing for sure: use whatever gets the job done for you. Using X, don't use Y is just another way of saying, you do not need to get to know the tool you are using. Offering very little insight into why that decision has made is just not really appealing to me as an engineer who likes to know the internals of the tools he uses, does not matter if it is the linux kernel, a firmware of a high level programming language.

Re: Clojure, the Good Parts

#40
post #10

It's sad to see that STM is not recommended. It is hugely important in Haskell. I jump to believe that this is an artifact of type-driven STM, but it may be something else instead. I've recently moved from Component to Mount. It is less powerful but just a vast syntactic overhead reduction. Absolutely avoid metadata. The worst thing ever is using it like how Reagent does in ClojureScript where it actually provides im…

I don't think I've ever ran across a situation where I've needed STM. Most of the time you can just use pure functions, and when you absolutely need state, an atom is usually the best tool for the job. STM strikes me as a very specialised tool, at least in the context of Clojure.

I don't think metadata is a bad idea. It's data about data. As long as you don't assume something will have the same metadata as the data it's derived from, you should be fine. I don't necessarily like the way Reagent uses it, but it's not out of character for Clojure to use metadata semantically.

Post reply on HN