Parsing 2D matrix: "[[1,2][3,4]]" I feel I am over-complicating things, and that there should be a succinct way to achieve this - perhaps using concepts I have not learnt - or some prelude function I have missed out on.

How can I improve this?

data Mat2x2 = MkMat2x2 Integer Integer Integer Integer
  deriving (Show, Eq)


-- modified versions of preludes readParanthesis
readBrackets :: ReadS a -&gt; ReadS a
readBrackets g = mandatory
                 where mandatory r = do
                              ("[", s) &lt;- lex r
                              (x,t)    &lt;- g s
                              ("]", u) &lt;- lex t
                              return (x,u)

readInt :: String -&gt; Maybe (Integer, String)
readInt s = listToMaybe ((reads :: String -&gt; [(Integer, String)]) s)

-- greedy consumption of comma separated values and returns them as list and remaining string, ex:
-- parseCsv readInt "2,3,5,2"
-- ([2,3,5,2],"")
parseCsv :: (String -&gt; Maybe (a, String)) -&gt; String -&gt; ([a], String)
parseCsv f s = case (f s) of
                        Nothing          -&gt; ([], s)
                        Just (x,(',':r)) -&gt; (x : (nextXs r), (nextR r))
                        Just (x,r)       -&gt; ([x], r)
                      where nextXs ns = fst (parseCsv f ns)
                            nextR  ns = snd (parseCsv f ns)

-- reads an arbitrary length vector "[1,2,3,...N]" -&gt; Just [1,2,3...,N]
readVec :: String -&gt; Maybe ([Integer], String)
readVec s = listToMaybe (readBrackets ((:[]) . parseCsv readInt) s) -- note: (:[]) is operator making a list of one element

readVec2 :: String -&gt; Maybe ((Integer, Integer), String)
readVec2 s = case readVec s of
                Just ((a:[b]), r)  -&gt; Just ((a,b), r)
                Just (_, _)        -&gt; Nothing
                Nothing            -&gt; Nothing

read2Vec2 :: String -&gt; Maybe (((Integer, Integer),(Integer,Integer)), String)
read2Vec2 s = case readVec2 s of
                    Nothing          -&gt; Nothing
                    Just ((a,b), r1) -&gt; case readVec2 r1 of
                                            Nothing          -&gt; Nothing
                                            Just ((c,d), r2) -&gt; Just (((a,b),(c,d)), r2)

readMat2x2 :: String -&gt; Maybe (Mat2x2, String)
readMat2x2 s = case listToMaybe (readBrackets (maybeToList . read2Vec2) s) of
                    Nothing                 -&gt; Nothing
                    Just (((a,b),(c,d)), r) -&gt; Just ((MkMat2x2 a b c d), r)
Many improvements are possible. 

First, you shouldn't use ReadS and other rudumentary Prelude parsing function for anything but exercises. Use parsing libraries from Hackage instead. parsec is a good library to start with, if you are not sure of which one to use.

Second, if it is an exersise, you can avoid long chains of Nothing matches by using Maybe monad. I reimplemented read2Vec2 for example:

read2Vec2 :: String -&gt; Maybe (((Integer, Integer),(Integer,Integer)), String)
read2Vec2 s = do
    ((a,b), r1) &lt;- readVec2 s
    ((c,d), r2) &lt;- readVec2 r1
    Just (((a,b),(c,d)), r2) 


Third, you can avoid passing unparsed input around by keeping it in the State monad. At this point our code will do what parsec does :) except parsec does it better.

If you want to stay with your current approach then readBrackets can be shortened:

readBrackets :: ReadS a -&gt; ReadS a
readBrackets g r = do
    ("[", s) &lt;- lex r
    (x,t)    &lt;- g s
    ("]", u) &lt;- lex t
    return (x,u)


Here is full parser using parsec:

{-# LANGUAGE NoMonomorphismRestriction #-}
import Text.ParserCombinators.Parsec
import Control.Applicative

data Mat2x2 = MkMat2x2 Integer Integer Integer Integer
  deriving (Show, Eq)

parseMat2x2 = parse mat2x2 "(unknown)" 

nat = read &lt;$&gt; many1 digit 

mat2x2 = do
    string "[["
    d1 &lt;- nat
    char ','
    d2 &lt;- nat
    string "]["
    d3 &lt;- nat
    char ','
    d4 &lt;- nat
    string "]]"
    return $ MkMat2x2 d1 d2 d3 d4

main = print $ parseMat2x2 "[[2,2][3,4]]"


The parser can be made less strict if necessary. For example, current parser doesn't accept any whitespace. And for a better performance you could try parsec with a lexer stage (current code avoids lexer altogether).

For ultimate performance you should use alex and happy tools, which are haskell's lex and yacc respectively.