Stupid newbie question of the day: why is newMVar in the IO monad?

So, I've hit a problem, and I'm wondering why this is. I want to write a function which returns a global monotonically incrementing int. This *should* be easy- just put a MVar in a global name, then update it as necessary: globalCounter :: MVar Integer globalCounter = undefined genId :: IO Integer genId = modifyMVar (\i -> (i+1, i)) globalCounter The problem with this is defining globalCounter- and that is because newMVar returns IO (MVar a), and not just MVar a. Now, I can go: globalCounter = unsafePerformIO $ newMVar 0 but I hate using unsafePerformIO. And I don't want to pass around the reference itself- I need to be in the IO monad with a StateT transform on top for other reasons, I don't want to complicate things. And even if I were, I would just pass the counter around instead of the reference. But it just feels like Haskell is being gratuitously difficult here. It's not just the name, it's the fact that creating the MVar is *explicitly* modifying the state of the world, which implies there is something more going on here than just allocating some memory. As an example of what I mean, newSTRef returns ST s (STRef s a). This is explicitly saying that the created STRef is only visible in the given thread. This is necessary for the implementation of the STRef, which is a mutable variable with no transactional guarantees. If it were visible from another thread, then it could be accessed from the other thread, creating a potential race condition. That I understand. But that isn't the case here- MVar's are explicitly designed to be accessed from multiple threads. So then I thought that it was something specific with MVars- maybe they need to do an OS call to set them up or something. OK, so let's try some alternatives. Like STM. Nope. newTVar has return type STM (TVar a) and newTMVar returns STM (TMVar a). Throw these into atomically, and I'm right back to where I started. newIORef returns IO (IORef a). And that's just a pointer store (I thought). It's easier for me to believe that I'm missing something here, rather than that Haskell is just being gratuitously difficult. But I honestly don't see what it is I'm missing. Help? Brian

New `MVar` has to return a different memory location every time and this is noticeable, it's not referentially transparent. Consider what would happen if we made the transformation let a = newMVar 0 let b = newMVar 0 putMVar a 1 readMVar b to let a = newMVar 0 b = a ... If newMVar was referentially transparent, we can automatically share any of it's calls with same arguments since they're supposed to return the same thing everytime. Since it's not referentially transparent, back into the IO monad it goes. Also if you do that toplevel counter trick, you want NoInline otherwise GHC might just inline it at each occurrence and you'll end up with separate counters. Cheers, Danny Gratzer

Thanks. This helps. I was right to mistrust the unsafePerformIO
"solution". :-) What Haskell was telling me is that I need to think about
the scope of the identifiers I'm allocating, and what guarantees I'm making.
On Thu, Apr 17, 2014 at 10:26 AM, Danny Gratzer
New `MVar` has to return a different memory location every time and this is noticeable, it's not referentially transparent.
Consider what would happen if we made the transformation
let a = newMVar 0 let b = newMVar 0 putMVar a 1 readMVar b
to
let a = newMVar 0 b = a ...
If newMVar was referentially transparent, we can automatically share any of it's calls with same arguments since they're supposed to return the same thing everytime. Since it's not referentially transparent, back into the IO monad it goes.
Also if you do that toplevel counter trick, you want NoInline otherwise GHC might just inline it at each occurrence and you'll end up with separate counters.
Cheers, Danny Gratzer

In my (limited) experience, there are two main solutions to this kind of
problem:
- Dependency-injection, i.e. add the MVar as an explicit parameter to
every function you use it in. This is ideal, but it's often a little
cumbersome.
- unsafePerformIO, i.e. just initialize it globally. I've never really
had issues with this approach, if used sparingly and appropriately.
This might also be relevant:
http://www.haskell.org/haskellwiki/Global_variables
Hope that helps!
On Thu, Apr 17, 2014 at 10:34 AM, Brian Hurt
Thanks. This helps. I was right to mistrust the unsafePerformIO "solution". :-) What Haskell was telling me is that I need to think about the scope of the identifiers I'm allocating, and what guarantees I'm making.
On Thu, Apr 17, 2014 at 10:26 AM, Danny Gratzer
wrote: New `MVar` has to return a different memory location every time and this is noticeable, it's not referentially transparent.
Consider what would happen if we made the transformation
let a = newMVar 0 let b = newMVar 0 putMVar a 1 readMVar b
to
let a = newMVar 0 b = a ...
If newMVar was referentially transparent, we can automatically share any of it's calls with same arguments since they're supposed to return the same thing everytime. Since it's not referentially transparent, back into the IO monad it goes.
Also if you do that toplevel counter trick, you want NoInline otherwise GHC might just inline it at each occurrence and you'll end up with separate counters.
Cheers, Danny Gratzer
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

Also note linked from the global variables page on the wiki is:
http://www.haskell.org/haskellwiki/Top_level_mutable_state
Important for the `unsafePerformIO` option is the NOINLINE pragma to ensure
that only one global variable exists. In the STM case it is also important
to use `newTVarIO` rather then `unsafePerformIO $ atomically newTVar` which
does not work.
Ryan
On Thu, Apr 17, 2014 at 10:38 AM, Ben Foppa
In my (limited) experience, there are two main solutions to this kind of problem:
- Dependency-injection, i.e. add the MVar as an explicit parameter to every function you use it in. This is ideal, but it's often a little cumbersome. - unsafePerformIO, i.e. just initialize it globally. I've never really had issues with this approach, if used sparingly and appropriately.
This might also be relevant: http://www.haskell.org/haskellwiki/Global_variables
Hope that helps!
On Thu, Apr 17, 2014 at 10:34 AM, Brian Hurt
wrote: Thanks. This helps. I was right to mistrust the unsafePerformIO "solution". :-) What Haskell was telling me is that I need to think about the scope of the identifiers I'm allocating, and what guarantees I'm making.
On Thu, Apr 17, 2014 at 10:26 AM, Danny Gratzer
wrote: New `MVar` has to return a different memory location every time and this is noticeable, it's not referentially transparent.
Consider what would happen if we made the transformation
let a = newMVar 0 let b = newMVar 0 putMVar a 1 readMVar b
to
let a = newMVar 0 b = a ...
If newMVar was referentially transparent, we can automatically share any of it's calls with same arguments since they're supposed to return the same thing everytime. Since it's not referentially transparent, back into the IO monad it goes.
Also if you do that toplevel counter trick, you want NoInline otherwise GHC might just inline it at each occurrence and you'll end up with separate counters.
Cheers, Danny Gratzer
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

Also note that *if* you create top level mutable variables (which you
shouldn't, generally), you should make sure they're monomorphic.
Especially with newEmptyMVar it's very easy to create a polymorphic
one, which is just 'unsafeCoerce' in disguise:
import Control.Concurrent.MVar
import System.IO.Unsafe
global = unsafePerformIO newEmptyMVar
main = do
putMVar global 72
c <- takeMVar global
putStrLn [c]
On Thu, Apr 17, 2014 at 4:44 PM, Ryan Yates
Also note linked from the global variables page on the wiki is: http://www.haskell.org/haskellwiki/Top_level_mutable_state
Important for the `unsafePerformIO` option is the NOINLINE pragma to ensure that only one global variable exists. In the STM case it is also important to use `newTVarIO` rather then `unsafePerformIO $ atomically newTVar` which does not work.
Ryan
On Thu, Apr 17, 2014 at 10:38 AM, Ben Foppa
wrote: In my (limited) experience, there are two main solutions to this kind of problem:
Dependency-injection, i.e. add the MVar as an explicit parameter to every function you use it in. This is ideal, but it's often a little cumbersome. unsafePerformIO, i.e. just initialize it globally. I've never really had issues with this approach, if used sparingly and appropriately.
This might also be relevant: http://www.haskell.org/haskellwiki/Global_variables
Hope that helps!
On Thu, Apr 17, 2014 at 10:34 AM, Brian Hurt
wrote: Thanks. This helps. I was right to mistrust the unsafePerformIO "solution". :-) What Haskell was telling me is that I need to think about the scope of the identifiers I'm allocating, and what guarantees I'm making.
On Thu, Apr 17, 2014 at 10:26 AM, Danny Gratzer
wrote: New `MVar` has to return a different memory location every time and this is noticeable, it's not referentially transparent.
Consider what would happen if we made the transformation
let a = newMVar 0 let b = newMVar 0 putMVar a 1 readMVar b
to
let a = newMVar 0 b = a ...
If newMVar was referentially transparent, we can automatically share any of it's calls with same arguments since they're supposed to return the same thing everytime. Since it's not referentially transparent, back into the IO monad it goes.
Also if you do that toplevel counter trick, you want NoInline otherwise GHC might just inline it at each occurrence and you'll end up with separate counters.
Cheers, Danny Gratzer
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

There are at least two packages on Hackage that try to solve this problem once and for all: http://hackage.haskell.org/package/global-variables http://hackage.haskell.org/package/safe-globals Note that, for "global-variables", you may use an unexported newtype and an empty string if just using a global string smells bad for you. Cheers, -- Felipe.

On Thu, 17 Apr 2014 10:14:14 -0400, Brian Hurt
So, I've hit a problem, and I'm wondering why this is. I want to write a function which returns a global monotonically incrementing int. This *should* be easy- just put a MVar in a global name, then update it as necessary:
globalCounter :: MVar Integer globalCounter = undefined
genId :: IO Integer genId = modifyMVar (\i -> (i+1, i)) globalCounter
The problem with this is defining globalCounter- and that is because newMVar returns IO (MVar a), and not just MVar a. Now, I can go:
globalCounter = unsafePerformIO $ newMVar 0
but I hate using unsafePerformIO. And I don't want to pass around the reference itself- I need to be in the IO monad with a StateT transform on top for other reasons, I don't want to complicate things. And even if I were, I would just pass the counter around instead of the reference. But it just feels like Haskell is being gratuitously difficult here.
It's not just the name, it's the fact that creating the MVar is *explicitly* modifying the state of the world, which implies there is something more going on here than just allocating some memory. As an example of what I mean, newSTRef returns ST s (STRef s a). This is explicitly saying that the created STRef is only visible in the given thread. This is necessary for the implementation of the STRef, which is a mutable variable with no transactional guarantees. If it were visible from another thread, then it could be accessed from the other thread, creating a potential race condition. That I understand. But that isn't the case here- MVar's are explicitly designed to be accessed from multiple threads.
So then I thought that it was something specific with MVars- maybe they need to do an OS call to set them up or something. OK, so let's try some alternatives. Like STM.
Nope. newTVar has return type STM (TVar a) and newTMVar returns STM (TMVar a). Throw these into atomically, and I'm right back to where I started. newIORef returns IO (IORef a). And that's just a pointer store (I thought).
It's easier for me to believe that I'm missing something here, rather than that Haskell is just being gratuitously difficult. But I honestly don't see what it is I'm missing. Help?
Brian
You said you're already using a StateT for other reasons but there's no problem with nesting StateT, you just have to lift a bit. I would personally use that approach - define another monad transformer layer on top of your IO to handle around the int state-passing. Perhaps, if you desperately want to avoid using StateT Int, you could also use ReaderT (MVar a) to pass around the function automatically. Note: Going beyond the ReaderT approach, there are more approaches one could use, for example a (?var :: MVar Int) implicit parameter, or another constraint that is resolved at runtime like with edwardk's reflection library. And finally, unsafePerformIO on this is safe as long as it's monomorphic and you don't inline it, ie. globalCounter :: Mvar Integer globalCounter = unsafePerformIO $ newMVar 0 {-# NOINLINE globalCounter #-}
participants (7)
-
Ben Foppa
-
Brian Hurt
-
Danny Gratzer
-
Erik Hesselink
-
Felipe Lessa
-
Niklas Haas
-
Ryan Yates