Parsing 2d arrays from text?

Conventional wisdom crudely expressed: never use Prelude.foldl; use foldl' with a strict accumulator, otherwise foldr.

There are two questions, I think, one his how to parse the incoming String or ByteString or whatever; the other is what sort of structure to read it into. Of course these aren't completely independent, but I wonder if you couldn't separate concerns a bit more. As I guess you would know, a really barbaric Prelude way of getting a String into the right shape might be like so:

parseAsLists :: String -> ((Int,Int), [[Int]])
parseAsLists = arrange . map (map read . words) . lines 
  where arrange ((n:m:_):rows) = ((n,m), rows)

mais = do str <- getContents
          print $ parseAsLists str

This is barbaric in a number of ways: it just fails on bad strings; it uses String instead of Text or ByteString; it parses into Haskell lists which are good for some purposes bad for others. The other stringy types have a similar interface, so things would go similarly.

But I think the main trouble was finding a data type to read into? Here I wonder if you might not find the (builtin) array package natural:

https://hackage.haskell.org/package/array-0.5.0.0/docs/Data-Array.html

There are several radically different array types with the same interface. The simple Data.Array.Array type would be fairly close to the lazy ((Int,Int),[[Int]]) type we had, but with e.g. more convenient lookup and so on. Since you are making a finite array of Int, an unboxed type, you might do well to use the strict, unboxed kind of arrays. (For one dimensional arrays, the vector package is more convenient; in either case there is a corresponding mutable type.)

https://hackage.haskell.org/package/array-0.5.0.0/docs/Data-Array-Unboxed.html

We can recycle our crude parseAsLists as a start:

import Data.Array.Unboxed

parseArraySimple :: String -> UArray (Int,Int) Int
parseArraySimple str = listArray ((0,0),(x',y')) arr
 where
   ((x,y),arrs) = parseAsLists str
   (x',y') = (x-1,y-1)  -- to use them as indices
   arr  = concat arrs 

Then we could write, say:

main = do str <- readFile "arr.txt"
          let arr = parseArraySimple str
              loop = do pt <- readLn :: IO (Int,Int)
                        print $ arr ! pt
                        loop
          loop 

With the following highly error prone result:

   -- $ ghc -O2 lineparser2.hs 
   -- $ ./lineparser2
   -- Index?
   -- (0,0)
   -- 1
   -- Index?
   -- (1,2)
   -- 6
   -- Index?
   -- (20,20)
   -- lineparser2.hs: Error in array index
/r/haskell Thread