# IO and Exceptions — Haskell

Source: https://www.geekswithgeeks.com/en/haskell/e-io

> Perform input and output, handle exceptions and manage resources.

## Effects at the edges

An `IO a` value is a **description of an action** that, when run by the runtime, may interact with the world and produce an `a`. The program's `main :: IO ()` is the root action; IO actions are combined with `do` notation, and pure functions are called from inside them. A pure function cannot run an IO action, which is what keeps purity: effects live at the **edges**, and the core logic stays pure and easily testable. Common actions: `putStrLn`, `getLine`, `readFile`, `writeFile`, `getArgs` from `System.Environment`, and `hFlush stdout`. Use **`readMaybe`** instead of `read` for parsing input, since `read` crashes on bad input. **Exceptions** exist in IO (and can be thrown from pure code by partial functions such as `head` and `div` by zero). Handle them with **`Control.Exception`**: `try` returns an `Either`, `catch` installs a handler, `throwIO` raises, and custom exception types derive `Show` and declare `instance Exception`. Use **`bracket`** or the `with...` functions (`withFile`) to guarantee resource cleanup. Prefer `Maybe` and `Either` for expected failures and exceptions for I/O and truly unexpected errors.

## Pure core, IO shell

Pure functions sit inside; IO actions at the boundary talk to the world.

![A central circle containing small function boxes, surrounded by a ring with keyboard, file and network icons connected to it.](assets/figures/haskell/section-5-map.svg) — Figure 5.1 — Functional core, imperative shell.

## Reading input, handling exceptions and using files safely

readMaybe for input, try for exceptions, withFile for resources.

```haskell
import Control.Exception (Exception, IOException, throwIO, try)
import System.IO (IOMode (AppendMode), hFlush, hPutStrLn, stdout, withFile)
import Text.Read (readMaybe)

newtype InvalidQuantity = InvalidQuantity String
  deriving Show

instance Exception InvalidQuantity

askQuantity :: IO Int
askQuantity = do
  putStr "Quantity: "
  hFlush stdout
  line <- getLine
  case readMaybe line of
    Just n | n > 0 -> pure n
    _              -> throwIO (InvalidQuantity line)

main :: IO ()
main = do
  result <- try askQuantity
  case result of
    Left (InvalidQuantity raw) -> putStrLn ("Not a valid quantity: " ++ show raw)
    Right n                    -> putStrLn ("Ordering " ++ show n ++ " items")

  contents <- try (readFile "prices.txt") :: IO (Either IOException String)
  putStrLn $ either (\e -> "cannot read prices: " ++ show e)
                    (\c -> "read " ++ show (length (lines c)) ++ " price lines")
                    contents

  withFile "orders.log" AppendMode $ \h ->      -- the handle is closed even on exceptions
    hPutStrLn h "order placed"
```

## read is partial

`read "abc" :: Int` throws an exception at run time. For user input, files and network data, always use `readMaybe` (or a proper parser) and handle the `Nothing` case.

**Quiz:** How does Haskell keep functions pure while still doing I/O?

- [x] I/O is described by IO actions in the type, which pure functions cannot run
- [ ] It forbids I/O
- [ ] It uses global variables
- [ ] All functions are impure

*Answer:* I/O is described by IO actions in the type, which pure functions cannot run. Effects are values of type IO, run only by the runtime from main.
