2016-05-16 7 views
2

私はrunST文の中でランダムジェネレータを使い、使用後にジェネレータを返すようにしていますので、別の場所で使用することができます。runSTからランダムジェネレータを返す

ベクトルだけを返した場合、コードはコンパイルされますが、ジェネレータをreturn文に追加するとコンパイルが失敗します。エラーメッセージが正しく理解されていれば、モナドフォールドに渡された関数は同じ状態のベクタを変更しないと言いますが、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 

をしかし、これはしていません::

これはコンパイル

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) 

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') 
2

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) 。すべてのアクションはdoの式で同じモナドでなければならないことに注意してください。 (VU.unsafeFreeze vector, g')ST s <something>ではないため、runSTでは機能しません。

代わりに、凍結されたベクトルを結合して、タイプST s (VU.Vector Int, g)で単一のペアを返すためにreturnを使用します。

g' <- ... 
    v' <- VU.unsafeFreeze vector 
    return (v', g') 
関連する問題