2016-05-16 47 views
2

我试图在runST语句中使用随机生成器,并在使用后返回生成器,以便可以在别处使用它。从运行器返回随机发生器

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

这编译:

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)

回答

2

看的unsafeFreeze类型。

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

这是一个monadic计算,它返回m (Vector a)。所以表达式(VU.unsafeFreeze vector, g')的类型为(m (Vector Int), g)。类型检查器告诉您,它不能将此类型与do块的类型统一,您声明该块为ST s (Vector Int, g)

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

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

VU.unsafeFreeze vector返回一些monad中的向量(在这种情况下为ST s) 。请记住,每个动作必须位于do表达式中的相同单子中。由于(VU.unsafeFreeze vector, g')不是ST s <something>类型,因此它不适用于runST

相反,结合冷冻矢量,并使用return返回一对与ST s (VU.Vector Int, g)类型:

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