Skip to content

Latest commit

 

History

History
81 lines (69 loc) · 3.16 KB

22.md

File metadata and controls

81 lines (69 loc) · 3.16 KB

Input / Output

Let's revisit the functions in the example from yesterday.

readName :: IO String
readName = do
  putStrLn "What is your name?"
  getLine

The readName function has to return an action that will produce a string. The definition starts with the do keyword. The do keyword allows us to write IO code that looks very similar to imperative programs. We will see how to write IO code without do notation. getLine in the last line of the definition will return with a value of type IO String. That is the same as the readName function's return value. When using do notation the last line's value is going to be returned from the function.

Exercise

  • What is the type of putStrLn "What is your name?
  • Delete the last line of readName and try to compile the code.
  • The return function let's you construct IO actions that produce the value that is passed to it as a parameter. The type of return True is IO Boolean. It will produce the value True when evaluated. Try to fix the readName function using return.
main :: IO ()
main = do
  name <- readName
  putStrLn ("It is nice to meet you " ++ name ++ "!")

The type of main is IO (). It has the same type as the last line in the do block (putStrLn ...). The second line is more interesting. Inside a do block you can extract the value of other IO actions. The type of readName is IO String and the type of name is String. The left arrow notation can be used only to extract values from actions. If you would like to define values that are constructed by plain functions without any IO action, you can use let.

import Data.Char

main :: IO ()
main = do
  name <- readName
  let capitalizedName = capitalize name
  putStrLn ("It is nice to meet you " ++ capitalizedName ++ "!")
  where capitalize :: String -> String
        capitalize = map toUpper

capitalizedName is a simple String just as capitalize name.

Exercise

  • The readMaybe function can be used to parse values of types that implement the Read typeclass. It returns with Maybe. If it fails to parse the value it returns Nothing. Try out the following lines in ghci.
import Text.Read

:t readMaybe
readMaybe :: Read a => String -> Maybe a
readMaybe "10" :: Maybe Int
readMaybe "10" :: Maybe Double
readMaybe "foo" :: Maybe Int
  • Write a function that reads a string from the user and inside the function it converts it to an Int using readMaybe. (hint: The type should reflect the possibility of failure. Don't forget to import the Text.Read module.)
  • Modify the function to keep asking for a number until the user enters a valid input. The type signature should be IO Int. (hint: Case expressions are allowed in do notation. Use recursion. You can construct IO actions from arbitrary values using return.)
  • Is it possible to write a function that prints to the screen or reads some input whose return type does not contain IO?
  • Is it possible to call a function whose return type does not contain IO, but deep in the stack, it calls a function with a return type containing IO?