从 runST 返回随机生成器

Returning random generator from runST

我正在尝试在 runST 语句中使用随机生成器,并在使用后 return 生成器,以便可以在其他地方使用。

如果我 return 只有一个向量,代码会编译,但是当将生成器添加到 return 语句时,编译会失败。如果我正确理解错误消息,它表示传递给 monadic fold 的函数不会在相同状态下修改向量,但我无法弄清楚为什么如果我从 [=26 中省略随机生成器,它会编译=]声明。

这样编译:

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 语句产生以下错误:

Couldn't match expected type ST s (VU.Vector Int, g) with actual type (m0 (VU.Vector Int), g)

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

相反,绑定冻结向量并使用 return 到 return 一对 ST s (VU.Vector Int, g):

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

unsafeFreeze的类型。

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

这是 returns m (Vector a) 的一元计算。所以表达式 (VU.unsafeFreeze vector, g') 的类型是 (m (Vector Int), g)。类型检查器告诉您它无法将此类型与您声明为 ST s (Vector Int, g).

do 块的类型统一

为了统一这些类型,我们需要将元组内部的m分布到外部;然后类型检查器将推断 m ~ ST s。您需要从其单子计算中提取 Vector,然后将其与生成器一起打包。

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