For example, consider a pattern for reading length-prefixed lists: read a number N, then read N numbers, then sum them.
main :: IO ()
main = do
n <- readLn
xs <- replicateM n readLn
print (sum xs)
“replicateM” (replicate + M for monadic) does an action N times and accumulates the results. Above we used it in IO, but we can use it in any monad, for example a parser: import Text.Parsec
import Text.Parsec.String
import Control.Monad
main :: IO ()
main = do
line <- getLine
parseTest sumParser line
sumParser :: Parser Int
sumParser = do
n <- number
xs <- replicateM n number
return (sum xs)
number :: Parser Int
number = spaces *> (read <$> many1 digit)
And if we wanted to abstract over this pattern, we could do so trivially: lengthPrefixedSum :: (Monad m) => m Int -> m Int
lengthPrefixedSum get = do
n <- get
xs <- replicateM n get
return (sum xs)
Now our first “main” becomes: main = print =<< lengthPrefixedSum readLn
And “sumParser” becomes: sumParser = lengthPrefixedSum number
There are many such functions in the standard library, such as “forM” which implements “for each” loops, and “when” which implements conditionals. If your data type is a monad, all of these control structures are available to you for free, so you can easily implement very expressive DSLs.