Search code examples
arrayshaskellfunctional-programmingstatemutable

How can I convert indexes into a mutable array into state references in Haskell?


I'm recoding the game I asked a question, "How can I iterate over a quadruple linked 2-dimensional grid of data as if it were a 2-dimensional array?", for in Haskell.

In order to create the grid of data in the first place I used a very imperative style algorithm, shown below. The key feature it relies upon is that I can take an index into an array and create a reference out of it. An example, "&array[x][y]".

I need to be able to take an index into a mutable array and make a state reference out of it in Haskell. The type signature might therefore be

convertToSTRef :: i -> STArray s i a -> ST s (STRef s a)

I have looked over the documentation, tried both hoogle and hayoo, and not found a way to accomplish this.

P.S. Alternatively, if someone had a different algorithm that I could use, that would be great.

P.S.S. The simple imperative algorithm.

const size_t rows = 20;
const size_t columns = 59;

block tiles[columns][rows];
block * const start = &tiles[columns/2][rows/2];

for (size_t x = 0; x < columns; ++x)
 for (size_t y = 0; y < rows; ++y)
 {
  tiles[x][y].floor = '^';
  tiles[x][y].inhabitant = WALL;
  tiles[x][y].side_block[EAST] = (x + 1 < columns) ? &tiles[x + 1][y] : NULL;
  tiles[x][y].side_block[SOUTH] = (y + 1 < rows) ? &tiles[x][y + 1] : NULL;
  tiles[x][y].side_block[WEST] = (x > 0) ? &tiles[x - 1][y] : NULL;
  tiles[x][y].side_block[NORTH] = (y > 0) ? &tiles[x][y - 1] : NULL;
 }

Solution

  • You can represent a "pointer" by a cursor, that is, a data structure containing an array reference and an offset.

    data Cursor t i a = Cursor (t i a) i
    
    makeCursor :: STArray s i a -> i -> Cursor (STArray s) i a
    makeCursor = Cursor
    
    readCursor :: Ix i => Cursor (STArray s) i a -> ST s a
    readCursor (Cursor arr i) = readArray arr i
    
    writeCursor :: Ix i => a -> Cursor (STArray s) i a -> ST s ()
    writeCursor x (Cursor arr i) = writeArray arr i x
    

    It is not possible to point to the interior of a garbage-collected object in GHC. The garbage collector cannot understand such pointers. If an array is moved by the garbage collector, the garbage collector cannot update such pointers appropriately. If the garbage collector is given a pointer to the middle of an array, it cannot scan the entire array because it can't find the beginning of the array.