Hacker News new | ask | show | jobs
by twic 3926 days ago
So what are the M generic functions that we can apply to all monads?
1 comments

Control structures, generally.

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.