GHC knows about quite a few flavours of Large Swathes of Bytes.
First, GHC distinguishes between primitive arrays of (boxed) Haskell objects (type Array# obj) and primitive arrays of bytes (type ByteArray#).
Second, it distinguishes between…
Arrays that do not change (as with ``standard'' Haskell arrays); you can only read from them. Obviously, they do not need the care and attention of the state-transformer monad.
Arrays that may be changed or ``mutated.'' All the operations on them live within the state-transformer monad and the updates happen in-place.
A C routine may pass an Addr# pointer back into Haskell land. There are then primitive operations with which you may merrily grab values over in C land, by indexing off the ``static'' pointer.
If, for some reason, you wish to hand a Haskell pointer (i.e., not an unboxed value) to a C routine, you first make the pointer ``stable,'' so that the garbage collector won't forget that it exists. That is, GHC provides a safe way to pass Haskell pointers to C.
Please see Section 6.4.4 for more details.
A ``foreign object'' is a safe way to pass an external object (a C-allocated pointer, say) to Haskell and have Haskell do the Right Thing when it no longer references the object. So, for example, C could pass a large bitmap over to Haskell and say ``please free this memory when you're done with it.''
Please see Section 6.4.5 for more details.
The libraries section gives more details on all these ``primitive array'' types and the operations on them, Chapter 8. Some of these extensions are also supported by Hugs, and the supporting libraries are described in the GHC/Hugs Extension Libraries document.