不,这是不可能的;ST
没有那些语义。monad 是ST s
,而不是ST s (STUArray s a)
。 ST s
只是一个用于跟踪可变状态的单子;您选择在单个ST
区域内分配和使用哪些结构取决于您。如果你有一堆计算都在同一个上运行STUArray
,你可以使用ReaderT
:
type Hasher s = ReaderT (STUArray s Int Word32) (ST s)
padFixed :: Hasher ()
padFixed = do
block <- ask
unsafeWrite block 5 0x80000000
unsafeWrite block 15 160
Reader r
monad 只是一个包装器r ->
;类型的值Reader r a
只是一个函数r -> a
。这本质上是一种a
在访问类型值的同时进行计算的方法r
。ReaderT r
monad 转换器只允许您提供对r
任意 monad 计算的类型变量的访问;因此,ReaderT (STUArray s Int Word32) (ST s)
是一个ST s
可以访问某个数组的计算。请注意,您不需要从padFixed
;返回数组。monad 绑定将处理所有这些。
这写起来会有点麻烦,因为我们必须继续ask
为数组做准备。幸运的是,我们可以编写一些组合器来为我们处理这个问题:
{-# LANGUAGE RankNTypes, GeneralizedNewtypeDeriving #-}
import Data.Word
import Control.Applicative
import Control.Monad.Reader
import Control.Monad.ST
import Data.Array.ST (STUArray, runSTUArray)
import qualified Data.Array.Base as A
import Data.Array.Unboxed (UArray)
newtype Hasher s a =
Hasher { getHasher :: ReaderT (STUArray s Int Word32) (ST s) a }
deriving (Functor, Applicative, Monad, MonadReader (A.STUArray s Int Word32))
hasherToST :: Hasher s () -> (Int,Int) -> ST s (STUArray s Int Word32)
hasherToST (Hasher r) bounds = do
block <- A.newArray bounds 0
runReaderT r block
return block
runHasher :: (forall s. Hasher s ()) -> (Int,Int) -> UArray Int Word32
runHasher h bounds = runSTUArray $ hasherToST h bounds
-- Perhaps private to this module, perhaps not
liftST :: ST s a -> Hasher s a
liftST = Hasher . lift
----- We can lift the functions which act on an STUArray -----
getBounds :: Hasher s (Int,Int)
getBounds = liftST . A.getBounds =<< ask
-- I'd recommend against removing the `unsafe` from the name; this function
-- could segfault, after all.
unsafeReadBlock :: Int -> Hasher s Word32
unsafeReadBlock i = do
block <- ask
liftST $ A.unsafeRead block i
unsafeWriteBlock :: Int -> Word32 -> Hasher s ()
unsafeWriteBlock i x = do
block <- ask
liftST $ A.unsafeWrite block i x
----- And then, perhaps in a separate module: -----
padFixed :: Hasher s ()
padFixed = do
unsafeWriteBlock 5 0x80000000
unsafeWriteBlock 15 160
(请注意,我无法在 内hasherToST
内联runHasher
,可能是因为更高级别的类型阻塞了推理。)
基本上,我们将 a 包装ReaderT (STUArray s Int Word32) (ST s)
成 anewtype
而不是类型同义词,并提升一些基本的数组原语以在始终可用的块上工作。如果您不想要,您甚至不需要MonadReader
为Hasher
类型派生,只要您解除所有必要的功能。但是一旦你这样做了,你的散列代码就可以隐式地讨论数组。