2014-05-12 41 views
1

我想在Haskell中使用IORef来创建一个简单的随机数生成器来存储可变变量。我的想法是,我可以初始化种子,然后根据种子生成数字,并为下一个随机int存储新种子。Simple Haskell IORef - “无法与`Int'匹配'IO Int'类型” - 看不到它有什么不同

完整的错误,我得到的是:

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. 

我不明白怎么就不能匹配的类型 - 我明确了randomGen采取/返回一个int。这里是我的代码:

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) 

任何想法这里发生了什么?

感谢,

更新代码:

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))) 
+1

'randomGen'是纯粹的,但'readIORef'返回'IO a'。你将在do子句中的'randomGen'前使用'liftM'。另外'getRandom :: IO Int'。一旦它是IO,你无法摆脱它。 – dumb0

+0

@ dumb0所以我添加了导入Control.Monad,将'getRandom :: Int'的def更改为'getRandom :: IO Int',并将最后一行从'randomGen(readIORef seed)'改为'liftM(randomGen(readIORef种子))' - 这是你所描述的吗?因为这会引发另一个错误。 –

+1

我的评论有一个错误它的:'getRandom :: IO(Int,Int)' – dumb0

回答

3

类型IO IntInt在Haskell中完全不同。这适用于任何其他类型的表单,如Maybe IntEither String Int。这是Haskell的类型系统设计的一部分,使它非常强大。你可以将这种形式的任何东西都想象成一种容器,它是通过这种类型进行参数化的。因此,你可以这样做

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 

然而,由于种子每次调用后丢弃,这将始终返回相同的值。我很好奇你为什么要采用这种方法生成随机数字,因为MonadRandom包中有这么好的API。请参阅this answer我写了一段关于如何使用Rand monad和this回答一个更深入的解释它如何工作的例子。

+0

我采取这种做法,因为我想我明白了单子,被告知,试图编写一个应用程序来测试这个载有我可以调用基于种子来得到一个随机数的函数,我最初提供了(即防止用户不得不更新他们的种子)。我的例子显然是微不足道的(随机数是种子,种子只是递增)。 –

+0

那么是否有办法阻止每次调用都抛弃IORef种子,因此它在函数调用之间仍然存在? –

+0

我在这里读到,有一个全局名称空间,是否有可能我可以将这个变量存储在我的应用程序中? http://hackage.haskell.org/package/global-variables-1.0.1.1/docs/Data-Global.html –

1

尝试:

module Main where 
import Data.IORef 
import Control.Monad 
import Data.Tuple(fst,snd) 

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

getRandom :: IO Int -> IO (Int,Int) 
getRandom x = do 
    y <- x 
    seed <- newIORef y 
    liftM randomGen $ readIORef seed 

在这一点上,使用liftM fst上getRandom的输出,以获得随机数和liftM snd得到种子为下一个电话...哦,顺便说一句System.Randomrandoms生成一个无限的随机数列表(或其他任何东西Random实例)。没有重新发明轮子的意义。

+0

真棒,这适用于上述。但是,因为它现在使用liftM,我无法将第二个值保存在元组中吗?我想将它保存回IORef,所以下一次调用getRandom将返回一个不同的随机数,但对于当前结构来说这是不可能的?将要做updateIORef –

+0

@SamHeather你不能避免返回种子并传递给下一个电话。 'getRandom'中的'IORef'对函数来说是局部的,所以你必须以任何方式返回种子,所以使用'IORef'使得你的发生器不纯,并且不会给你带来任何东西。如果你想让种子传递隐含的更好方法是使用“状态”monad。 – duplode

+0

@ dumb0我不想重新发明轮子,只学习。我试图想出哈斯克尔(这个)有状态的程序的一些非常简单的例子,但即使这样的实施似乎并没有因为每次运行时,它会返回7,从来没有改变(增加),因为我想要的状态 - 我的误会我认为。任何你能想到得到它的方式,或者我可以看到的一个不同的简单有状态问题的想法? –

相关问题