31

I've read many research papers on this topic, and they usually argue that arrays are implemented using Monads. But none of these papers gave a clear definition of how the "type" Array itself should be defined, they only gave definitions for the functions using monads to access or modify this type. How are arrays, having O(1) time to access or modify an indexed element, implemented in Haskell ?! (such as STUArray and MArray)

3
  • Usually they are implemented by appealing to an external library or RTS support. Commented Apr 24, 2011 at 20:38
  • I wonder what "arrays are implemented using Monads" could mean... Sounds like "bread is baked using tables". Commented Aug 1, 2017 at 11:37
  • @Alexey "implemented" in English means "put into effect". So there's nothing wrong about saying that mutability of arrays in Haskell is implemented ("put into effect" or "achieved") using Monads. The same result can be achieved using infinite streams, but it's much less intuitive. Commented Aug 1, 2017 at 16:49

3 Answers 3

33

How are arrays, having O(1) time to access or modify an indexed element, implemented in Haskell

They are implemented via primitive operations in the runtime system for memory reads and writes.

The safety of the side effecting action of destructively writing to memory is ensured via the use of monads to linearize access to the mutable state.

Looking at the primitive package for Haskell arrays (in IO or ST), you can see that the implementations is in terms of GHC's primops:

-- | Create a new mutable array of the specified size and initialise all
-- elements with the given value.
newArray :: PrimMonad m => Int -> a -> m (MutableArray (PrimState m) a)
newArray (I# n#) x = primitive
   (\s# -> case newArray# n# x s# of
             (# s'#, arr# #) -> (# s'#, MutableArray arr# #))

-- | Read a value from the array at the given index.
readArray :: PrimMonad m => MutableArray (PrimState m) a -> Int -> m a
readArray (MutableArray arr#) (I# i#) = primitive (readArray# arr# i#)

-- | Write a value to the array at the given index.
writeArray :: PrimMonad m => MutableArray (PrimState m) a -> Int -> a -> m ()
writeArray (MutableArray arr#) (I# i#) x = primitive_ (writeArray# arr# i# x)

That is, in terms of:

  • newArray#
  • readArray#
  • writeArray#

which are primitive (hardware accelerated ;) services for operating on memory provided by the language runtime.

Mechanisms for giving type safety to destructive memory effects were introduced to Haskell by the Launchbury and Peyton-Jones paper, Lazy Functional State Threads, which introduces the ST monad and primitives for mutable arrays.

Sign up to request clarification or add additional context in comments.

Comments

9

As something of an aside, please keep in mind that "implemented using monads", as can be done for various control structures, is not really the same thing as "side effects isolated by monadic operations on an opaque type", as with IO or ST, where the properties of the monad merely ensure that pure code remains so.

The mutable data is provided as a runtime primitive, as Don Stewart explains; the only thing "implemented with monads" here is type safety.

2 Comments

Type safe encapsulation and sequencing (the s parameter that gets threaded automatically, giving a dependency order on the effects).
@Don: Ah yes, I suppose I was thinking that part was obvious enough, but you're right, I should have been more explicit.
8

They are implemented in the same way as in an imperative language; i.e., in-place update. The type system will guarantee that you can't do anything "bad" with them.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.