
Hello, I’m the author of the `MVar` implementation in Cats-Effect.
Recently I had an interesting discussion on MVars with cats-effect library designers. Cats-effect brings MVar synchronization primitive along with other IO stuff to the Scala programming language. I tried to persuade them to include some Control.Concurrent.MVar’s functions to the library but has failed completely. Moreover, now I think that MVar is a poor choice for basic synchronization primitive.
I believe `MVar` is a superb choice for synchronisation, because it behaves like a blocking queue, which in computer science is a pretty fundamental tool. It is true that in Cats-Effect an `MVar` might not be a primitive, but as I explained in that thread, on top of the JVM the real primitives are the low level building blocks like `AtomicReference`.
1. It’s complex. Each MVar has 2 state transitions, each may block.
Blocking is a feature. If you have to build that logic via `Ref` (`IORef`), you’d have to model the state machine by yourself and that’s complexity being pushed to the user.
2. It does not play well in presence of asynchronous exceptions. More specifically, `take` and `put` operations should be balanced (each `take` must be followed by `put`)
The `take` followed by a `put` rule is only for your “modify” use-case. The problem is that a `modify` function that’s described via `take` + `put` is not an atomic operation and this is a problem, but only if any of the actors accessing the `MVar` are doing so in a different order. This isn’t a problem for other use-cases tough.
this force programmer to mask asynchronous exceptions during the MVar acquisition and since `take` function may block, this will delay task cancelation.
I don’t have much experience with Haskell’s async exceptions, but if you mean that `take` must be executed as `uncancelable`, via `bracket`, then this is a problem that we can fix in Cats-Effect 2.x, as mentioned in that thread.
What could be the sensible alternative? Guy from the cats-effect suggested me IVar + atomic reference (IORef). This pattern separates concern of blocking (synchronization) from the atomic mutation. So everything can be represented as atomic reference with IVar inside. Just look at this beautiful mutex implementation https://github.com/ovotech/fs2-kafka/blob/master/src/main/scala/fs2/kafka/in...
As I said above, most things on top of the JVM can be implemented via an `AtomicReference` due to its memory model and this is no exception. But it’s not elegant, or simple ;-)
(By ”beautiful” I mean approach itself of course, but not the Scala’s syntax. Scala is one of most ugliest girls after C++ I was forced to date with by my employer for money. Thankfully he didn’t force me to do the same things with her grandma Java).
That’s unnecessary and TBH a turnoff. Cheers, -- Alexandru Nedelcu https://alexn.org