No, it's not possible; ST
doesn't have those semantics. The monad is ST s
, and not ST s (STUArray s a)
. ST s
is just a monad for keeping track of mutable state; which structures you choose to allocate and use inside a single ST
region are up to you. If you have a bunch of computations which all operate on the same STUArray
, you can use 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
The Reader r
monad is just a wrapper around r ->
; a value of type Reader r a
is just a function r -> a
. This is essentially a way to compute a
while having access to a value of type r
. The ReaderT r
monad transformer just allows you to provide access to a variable of type r
to an arbitrary monadic computation; thus, ReaderT (STUArray s Int Word32) (ST s)
is an ST s
computation which has access to some array. Note that you don't need to return the array from padFixed
; the monad bind will handle all of that.
This'll be a little bit of a pain to write, since we'll have to keep ask
ing for the array. Luckily, we can write some combinators to handle this for us:
{-# 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
(Note that I couldn't inline hasherToST
inside of runHasher
, probably because of the higher-rank types blocking inference.)
Basically, we wrap the ReaderT (STUArray s Int Word32) (ST s)
into a newtype
instead of a type synonym, and lift some basic array primitives up to work on the always-available block. You don't even need to derive MonadReader
for the Hasher
type if you don't want, as long as you lift all the necessary functions. But once you've done this, your hashing code can talk about the array implicitly.