If you plan to update your array only a few times, you can use the (//) :: Ix i => Array i e -> [(i, e)] -> Array i e operator, like @JosephSible says.
As is however discussed in the question "How fast is Data.Array?", this is not very effective if you want to update your array frequently:
Note that // is probably O(n) though because it has to traverse the list (just like an imperative program would). If you need a lot of mutation you can use MArray or MVector.
This thus means that for large arrays, updating the array might take some time, since each update results in making a copy of the original array, and modifying that specific value.
The IOArray is a specific type of such MArray. We can thus define the modifyArray with writeArray :: (MArray a e m, Ix i) => a i e -> i -> e -> m ():
modifyArray :: IOArray Int Char -> Int -> IO (IOArray Int Char)
modifyArray arr idx | i < 0 = return arr
| mod i 102 == 1 = writeArray arr idx ' ' >> return arr
| otherwise = modifyarr arr idx
Note that here returning the array is not necessary, in essence the IOArray can be seen as a reference to a mutable array, so after the writeArray operation, arr refers to the modified array. If you use this in a larger IO operation, then after the modifyArray, that array is modified.
You here use a loop to obtain the largest index i that has div i 102 == 1 and is less than or equal to the initial index. You can however improve that, by subtracting the result of the modulo:
modifyArray :: IOArray Int Char -> Int -> IO (IOArray Int Char)
modifyArray arr idx | i < 0 = return arr
| otherwise = writeArray arr (idx - mod (idx-1) 102) ' ' >> return arr
MArray(mutable array), instead of anArray, since theArraywill result in a copy.