Skip to content

Latest commit

 

History

History
81 lines (60 loc) · 2.15 KB

24.md

File metadata and controls

81 lines (60 loc) · 2.15 KB

Input / Output

Let's revisit some code from the previous lecture.

import Text.Read

parseInt :: String -> IO (Maybe Int)
parseInt str = return (readMaybe str)

readAge :: IO (Maybe Int)
readAge = putStrLn "What's your age?" >> getLine >>= parseInt

In this example we do not really need the bind operator. What we really need is a way to map IO String values to IO (Maybe Int) values. There is a way to write a function that maps between these types without using the bind operator. Let's write down the generic version of the type we need.

f :: IO a -> IO b

IO implements the Functor typeclass. This means that there is an implementation of the fmap function that has the following signature.

fmap :: (a -> b) -> IO a -> IO b

-- substituting the types used in our example
fmap :: (String -> Maybe Int) -> IO String -> IO (Maybe Int)

Using the Functor instance of the IO type we can rewrite the example without using the bind operator.

import Text.Read

readAge :: IO (Maybe Int)
readAge = do
  putStrLn "What's your age?"
  fmap readMaybe getLine

Exercise

  • Write out the types of every expression of the last line in readAge.

There are many functions in Haskell that has an infix operator alternative. They all seem strange at first like >> and >>=, but they are used so frequently that it is worth getting used to them. fmap is not an exception.

import Text.Read

readAge :: IO (Maybe Int)
readAge = do
  putStrLn "What's your age?"
  readMaybe <$> getLine

Using fmap you can separate code that interacts with the outside world from the code that maps the inner values of IO actions to each other. In the previous example we were able to extract the readMaybe function that maps from String to Maybe Int from parseInt.

Exercise

  • Extract the String transformation logic from printCapitalLine with the help of the <$> operator.
import Data.Char

printCapitalLine :: String -> IO ()
printCapitalLine msg = putStrLn (toUpper <$> msg)

capitalizeInput :: IO ()
capitalizeInput = do
  putStrLn "Enter a line."
  getLine >>= printCapitalLine