从runST返回随机生成器

时间:2016-05-16 12:13:40

标签: haskell monads

我试图在runST语句中使用随机生成器并在使用后返回生成器,因此可以在其他地方使用它。

如果我只返回一个向量,则代码会编译,但是当将生成器添加到return语句时,编译将失败。如果我正确理解了错误消息,它说传递给monadic fold的函数不会修改同一状态下的向量,但是我不能弄清楚为什么如果我从返回中省略随机生成器它会编译言。

编译:

import           Control.Monad
import           Control.Monad.ST
import qualified Data.Vector.Unboxed         as VU
import qualified Data.Vector.Unboxed.Mutable as VUM
import           System.Random

randVector :: (RandomGen g) => Int -> g -> VU.Vector Int
randVector n g = runST $ do
  vector <- VU.unsafeThaw (VU.enumFromN 1 n)
  let step g i = do let (j,g') = randomR (1,n) g
                    VUM.swap vector i j
                    return g'
  g' <- foldM step g [1..VUM.length vector-1]
  VU.unsafeFreeze vector

但这不是:

randVector' :: (RandomGen g) => Int -> g -> (VU.Vector Int, g)
randVector' n g = runST $ do
  vector <- VU.unsafeThaw (VU.enumFromN 1 n) :: ST s (VUM.MVector s Int)
  let step g i = do let (j,g') = randomR (1,n) g
                    VUM.swap vector i j
                    return g'
  g' <- foldM step g [1..VUM.length vector-1]
  (VU.unsafeFreeze vector, g')

带有冻结向量和随机生成器的return语句会产生以下错误:

  

无法将预期类型ST s (VU.Vector Int, g)与实际匹配   输入(m0 (VU.Vector Int), g)

2 个答案:

答案 0 :(得分:2)

VU.unsafeFreeze vector在某些monad中返回一个向量(在本例中为ST s)。请记住,每个动作都必须位于do表达式中的同一个monad中。由于(VU.unsafeFreeze vector, g')不属于ST s <something>类型,因此不适用于runST

相反,绑定冻结的向量并使用return返回类型为ST s (VU.Vector Int, g)的单对:

  g' <- ...
  v' <- VU.unsafeFreeze vector
  return (v', g')

答案 1 :(得分:2)

查看unsafeFreeze的类型。

unsafeFreeze :: (Unbox a, PrimMonad m) => MVector (PrimState m) a -> m (Vector a)

这是一个返回m (Vector a)的monadic计算。因此,表达式(VU.unsafeFreeze vector, g')的类型为(m (Vector Int), g)。类型检查器告诉您它无法将此类型与您声明为do的{​​{1}}块的类型统一起来。

为了使这些类型统一起来,我们需要将ST s (Vector Int, g)从元组内部分发到外部;然后类型检查器将推断m。您需要从其monadic计算中提取m ~ ST s,然后使用生成器将其打包。

Vector