I've been following the MVar debate with some interest. As I've
mentioned in other mail, M structures were an Id thing, and so we here
at MIT are in some sense responsible for their peculiar
non-uniformity.
It should be noted that M-structures were defined by analogy to
I-structures---which are write-once, read-many. Thus, putMVar signals
an error on a full location primarily because the I-structure write
did as well. This non-uniformity stems from the fact that we can
easily cook up an MVar implementation which stores queued reads in the
same storage which it uses for the data (which is what I-structures
do). It's an implementation hack. I have no idea whether this is
true in GHC (I don't have the source handy right now), but I suspect
it isn't quite.
Really, it does seem to me that MVar behavior _ought_ to be
symmetric. In this case, there are two options:
* M-structure operations block. This shouldn't be a problem in
practice, as M-structures mostly get used in a producer-consumer
fashion, where this is what we want, or they get incrementally
updated, in which case we should be able to prove that suspension
on write is impossible (though the necessary compiler logic may
not be worth the bother).
* M-structure operations don't block. This breaks one of the
important early M-structure ideas: That the presence state of the
M-structure was invisible and enforced by the underlying
synchronization. On the other hand, when we're using M-structures
for e.g. caching we end up wasting a lot of effort coding up
presence state explicitly in another MVar.
Alas, my instinct is that Claus Reinke's "tryButDoNotSuspend" probably
isn't the right way to go here. Effectively what it does is "change
the mode" of the IO monad, altering the behavior of M-structures. It
seems to me that blocking and non-blocking operations really are
fundamentally different and have fundamentally different underlying
implementations, and there should simply be a separate set of
operations for each. I don't see an obvious way to code up the
suspensive operations using the non-suspensive ones, since you want to
associate the suspension state with the MVar (and do so atomically, in
case someone else is trying to change the state).
My conclusions:
The suspensive behaviors are necessary. At least one, and
preferably both of takeMVar/putMVar must be suspensive.
The non-suspensive behaviors are nice, but should be captured using
separate operations. Cooking them up using multiple MVars will
work, but is arguably only necessary because the implementation
isn't flexible enough.
-Jan-Willem Maessen