I am struggling to understand why the following attempts to find a minimum element in STArray lead to stack space overflow when compiled with ghc (7.4.1, regardless of -O level), but work fine in ghci:
import Control.Monad
import Control.Monad.ST
import Control.Applicative
import Data.Array.ST
n = 1000 :: Int
minElem = runST $ do
  arr <- newArray ((1,1),(n,n)) 0 :: ST s (STArray s (Int,Int) Int)
  let ixs = [(i,j) | i <- [1..n], j <- [1..n]]
  forM_ ixs $ \(i,j) -> writeArray arr (i,j) (i*j `mod` 7927)
--  readArray arr (34,56)  -- this works OK
--  findMin1 arr           -- stackoverflows when compiled
  findMin2 arr           -- stackoverflows when compiled
findMin1 arr = do
  es <- getElems arr
  return $ minimum es
findMin2 arr = do
  e11 <- readArray arr (1,1) 
  foldM (\m ij -> min m <$> readArray arr ij) e11 ixs
      where ixs = [(i,j) | i <- [1..n], j <- [1..n]]
main = print minElem
Note: switching to STUArray or ST.Lazy doesn't seem to have any effect.  
The main question though: What would be the proper way to implement such "fold-like" operation over big STArray while inside ST?
 
                        
The big problem in
findMin1isgetElems:Using
sequenceon a long list is a common cause for stack overflows in monads whose(>>=)isn't lazy enough, sincethen it has to build a thunk of size proportional to the length of the list.
getElemswould work with theControl.Monad.ST.Lazy, but then the filling of the array withcreates a huge thunk that overflows the stack. For the strict
STvariant, you need to replacegetElemswith something that builds the list withoutsequenceor - much better - compute the minimum without creating a list of elements at all. For the lazySTvariant, you need to ensure that the filling of the array doesn't build a huge thunk e.g. by forcing the result of thewriteArraycallsThe problem in
findMin2is thatis lazy in
m, so it builds a huge thunk to compute the minimum. You can easily fix that by usingseq(or a bang-pattern) to make it strict inm.Usually, you'll use the strict
STvariant (and for types likeInt, you should almost certainly useSTUArrays instead ofSTArrays). Then the most important rule is that your functions be strict enough. The structure offindMin2is okay, the implementation is just too lazy.If performance matters, you will often have to avoid the generic higher order functions like
foldMand write your own loops to avoid allocating lists and control strictness exactly as the problem at hand requires.