Input/Output (continued)

IO is a Monad. The type definition lives in Prelude / GHC.Types...

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

... and the instance definition lives in GHC.Base:

instance Monad IO where
  ...
  (>>=) = bindIO

bindIO :: IO a -> (a -> IO b) -> IO b
bindIO (IO m) k = ... unIO ..

unIO :: IO a -> (... RealWorld -> ( ... RealWorld, a ...))
unIO (IO a) = 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_ :: (a -> Bool) -> IO a -> IO ()
mDoWhile_ :: (Monad m) => (a -> Bool) -> m a -> m ()
mDoWhile_ f action = do
    x <- action
    if f x
      then mDoWhile_ f action
      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) => (a -> Bool) -> m a -> m [a]
mDoWhile f action = do
    x <- action
    if f x then do
        xs <- mDoWhile f action
        pure (x : xs)
    else
        pure []

mDoWhile_ :: (Monad m) => (a -> Bool) -> m a -> m ()
mDoWhile_ f action = mDoWhile f action >> 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 uses 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

results matching ""

    No results matching ""