Here are some pragmas and some imports:
{-# LANGUAGE ScopedTypeVariables #-}
import Control.Monad.ST
import Data.Array.ST
import Data.Array
<
Stefan beat me to the answer -- the tricky bit is that it's not the $ vs . between elems and runSTArray that's the issue -- it's the $ following runSTArray. Since something $ rankNthing is so common, there's a clever bit (I forget the details) that tries to let you do that as a corner case. But somehow using the composition earlier on prevents this. The location of the issue is demonstrated by the fact that the following will typecheck:
foo x = (elems . runSTArray) (
(newListArray (1,10) (replicate 10 x) :: ST s (STArray s Int String)))
I'm not sure this is a bug per se, but its certainly an unexpected behavior worth creating a ticket about, since there might still be a better algorithm to catch cases like the one you provided.