从 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')
我正在尝试在 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')