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/internal/Synchronized.scala

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