
Hello Vladimir In general it is better to avoid the try combinator and left factor the grammar instead. Because parsers are functions you can abuse left factoring a bit, parse the prefix in a top level combinator and supply the prefix to the sub-parsers: leftFactoredTime :: Parser TimeOfDay leftFactoredTime = do hh <- width2Num sep <- optionMaybe (oneOf ";,.") case sep of Nothing -> tTimeHourMin hh Just _ -> t24hrClock hh tTimeHourMin :: Int -> Parser TimeOfDay tTimeHourMin hh = do mm <- width2Num return (TimeOfDay hh mm 0) t24hrClock :: Int -> Parser TimeOfDay t24hrClock hh = do mm <- width2Num return (TimeOfDay hh mm 0) However in this case, the 24 hour clock and TimeHourMin are identical functions, the separator is a McGuffin [*] so: betterTime :: Parser TimeOfDay betterTime = do hh <- rangeP 0 23 width2Num _sep <- optionMaybe (oneOf ";,.") mm <- rangeP 0 59 width2Num return (TimeOfDay hh mm 0) To parse ranges I would make a new combinator that takes a range plus a number parser and returns a new number parser: rangeP :: Int -> Int -> Parser Int -> Parser Int rangeP hi lo p = do a <- p if (lo <= a && a <= hi) then return a else fail "out-of-range" Finally avoiding using read is good when using Parsec. Parsec has token parsers to read numbers, but here read can be avoided with this one: width2Num :: Parser Int width2Num = do a <- digit b <- digit return $ (10*digitToInt a) + digitToInt b digitToInt is in the Data.Char module. [*] A plot device used by Alfred Hitchcock films to throw the viewer off the scent.