Input/Output (continued)

IO is in Monad. The type definition lives in GHC.Types...

newtype IO a = IO ( ... RealWorld -> ( ... RealWorld, a ... ))

In Control.Monad.ST:

data RealWorld :: *

"RealWorld is deeply magical. It is primitive... We never manipulate values of type RealWorld; it's only used in the type system..."

So, we can't implement this instance ourselves. (After all, it performs side-effects, for which we have no mechanisms.) But the built-in instance satisfies the Monad interface and laws.

Iteration via Monad helpers

Loop until empty line.

getLinesUntilEmpty :: IO ()
getLinesUntilEmpty = do
    putStrLn "Something to say?"
    s <- getLine
    if s /= ""
      then getLinesUntilEmpty
      else putStrLn "Goodbye."

Factor out common pattern:

-- mDoWhile_ :: IO a -> (a -> Bool) -> IO ()
mDoWhile_ :: (Monad m) => m a -> (a -> Bool) -> m ()
mDoWhile_ f action = do
    x <- action
    if f x
      then mDoWhile_ action f
      else pure ()

getLinesUntilEmpty_ :: IO ()
getLinesUntilEmpty_ =
    mDoWhile_ (putStrLn "More?" >> getLine) (/= "")

Now let's loop until empty line, and then reverse each line and print them in reverse order. Let's start with:

mDoWhile :: (Monad m) => m a -> (a -> Bool) -> m [a]
mDoWhile action f = do
    x <- action
    if f x then do
        xs <- mDoWhile action f
        pure (x : xs)
    else
        pure []

mDoWhile_ :: (Monad m) => m a -> (a -> Bool) -> m ()
mDoWhile_ action f = mDoWhile action f >> pure ()

repeatBackwards :: IO ()
repeatBackwards =
    let getLinesUntilEmpty = mDoWhile getLine (/= "") in
 -- do {lines <- getLinesUntilEmpty; putStrLn $ unlines $ reverse $ map reverse lines}
 -- getLinesUntilEmpty >>= \lines -> putStrLn $ unlines $ reverse $ map reverse lines
 -- getLinesUntilEmpty >>= putStrLn . unlines . reverse . map reverse
    putStrLn . unlines . reverse . map reverse =<< getLinesUntilEmpty

The last step uses (=<<) (i.e. reverse bind) to keep the "pipeline" flowing in one direction.

Alternatively, could use reverse (i.e. left-to-right) composition to write the pipeline left-to-right:

(>>>) = flip (.)

repeatBackwards =
    getLinesUntilEmpty >>= map reverse >>> reverse >>> unlines >>> putStrLn

Note: The (>>>) operator is defined in Control.Category, but with a different precedence level which requires writing more parentheses in the definition above.

Source Files