Search code examples
variableshaskellrandomioref

Simple Haskell IORef - "Couldn't match type `IO Int' with `Int'" - can't see how it's different


I'm trying to make a simple random number generator in Haskell using IORef now to store mutable variables. The idea is that I can initialise the seed, and then generate numbers based on the seed, and store the new seed for the next random int.

The full error I'm getting is:

random2.hs:9:17:
    Couldn't match type `IO Int' with `Int'
    Expected type: IO (IORef Integer)
                   -> (IORef Integer -> IO Int) -> Int
      Actual type: IO (IORef Integer)
                   -> (IORef Integer -> IO Int) -> IO Int
    In a stmt of a 'do' block: seed <- newIORef 7
    In the expression:
      do { seed <- newIORef 7;
           randomGen (readIORef seed) }
    In an equation for `getRandom':
        getRandom
          = do { seed <- newIORef 7;
                 randomGen (readIORef seed) }

random2.hs:10:17:
    Couldn't match type `(,) Int' with `IO'
    Expected type: IO Int
      Actual type: (Int, Int)
    In the return type of a call of `randomGen'
    In a stmt of a 'do' block: randomGen (readIORef seed)
    In the expression:
      do { seed <- newIORef 7;
           randomGen (readIORef seed) }

random2.hs:10:28:
    Couldn't match expected type `Int' with actual type `IO Integer'
    In the return type of a call of `readIORef'
    In the first argument of `randomGen', namely `(readIORef seed)'
    In a stmt of a 'do' block: randomGen (readIORef seed)
Failed, modules loaded: none.

I don't understand how it can not be matching the type - I'm explicit that the randomGen takes/returns an Int. Here's my code:

module Main where
    import Data.IORef

    randomGen :: Int -> (Int, Int)
    randomGen x = (x,x+1)

    getRandom :: Int
    getRandom = do
        seed <- newIORef 7
        randomGen (readIORef seed)

Any idea what's going on here?

Thanks,

Updated code:

module Main where
    import Data.IORef
    import Control.Monad

    randomGen :: Int -> (Int, Int)
    randomGen x = (x,x+1)

    getRandom :: IO Int
    getRandom = do
        seed <- newIORef 7
        liftM (fst (randomGen (readIORef seed)))

Solution

  • The types IO Int and Int are entirely different in Haskell. This applies to any other type of that form, like Maybe Int or Either String Int. This is part of Haskell's type system design that makes it so powerful. You can think of anything in this form as a sort of container, it's parametrized over that type. Therefore you can do something like

    getRandom :: IO Int
    getRandom = do
        seed <- newIORef 7           -- IO (IORef Int)
        g <- readIORef seed          -- IO Int
        let (x, newG) = randomGen g  -- (Int, Int)
        writeIORef seed newG         -- IO ()
        return x                     -- IO Int
    

    However, this will always return the same value since the seed is discarded after every call. I'm curious as to why you want to take this approach to generating random numbers at all, since there is such a nice API in the MonadRandom package. See this answer I wrote a while back for an example of how to use the Rand monad, and this answer for a bit more of an in depth explanation of how it works.