IO and purity

Contents
IO is a type
Actions, not statements
Combining actions
Keeping the middle pure

IO is a type

An action that talks to the world has a type that says so. IO a means a program fragment that, when run, produces an a and may have done something outside:

getLine :: IO String putStrLn :: String -> IO () readFile :: FilePath -> IO String

A value of type IO String is a description of an action, not the string itself. Nothing happens until the runtime runs it, which is why a pure function cannot perform IO by accident.

Actions, not statements

The do block from the last lesson is how actions are sequenced. Each line is a value, and <- takes the result out:

main :: IO () main = do putStrLn "Name?" name <- getLine putStrLn ("Hello, " ++ name)

main is special: it is the action the runtime executes. Every other IO value is inert until something runs it.

Combining actions

Two combinators cover most loops and conditionals over actions. mapM_ runs an action for each element and discards the results, and when runs one conditionally:

mapM_ putStrLn ["a", "b"] import Control.Monad (when) main = do let debug = True when debug (putStrLn "starting")

mapM keeps the results in a list, and forM_ is the same loop with the arguments flipped so that the body comes last.

Keeping the middle pure

The useful structure of an IO program is a thin impure shell around a pure core. Read the input, hand it to pure functions, print the result:

-- pure, testable without a terminal shout :: String -> String shout = map toUpper -- impure, three lines, no logic main = do line <- getLine putStrLn (shout line)

Every hour spent moving logic out of do blocks pays back in tests that need no mocking. The next lesson reads and writes real files with the same split.

Next: Files and IO

Article author: Arthur Isaev

Related articles
Haskell
Haskell course: zero to hero in 50 lessons
What is Haskell and why it matters
Install the toolchain with GHCup
GHCi: the interactive shell
Your first program
Values with let and where
Types and type signatures
Functions and application
Currying and partial application
Operators, sections, and fixity
if, guards, and pattern guards
case expressions
Pattern matching
Tuples
Lists
Ranges and enumeration
List comprehensions
Recursion replaces loops
map, filter, and foldr
Lambdas and composition
Type classes
Parametric polymorphism
Maybe and null safety
Either and recoverable errors
data: product types
data: sum types
Records
newtype and type synonyms
Recursive data: binary trees
Functor
Applicative
Monad
do notation
IO and purity
Files and IO
Modules and imports
Cabal projects
Dependencies
Testing with HUnit
Property testing with QuickCheck
Laziness and infinite lists
Strictness and space leaks
Folds
Monoids
Traversable
Monad transformers
Errors and exceptions
Concurrency
Parsing with Megaparsec
Capstone: a todo CLI
Hero roadmap: the whole course on one page

Search this site

Channel @aofeed Chat @aofeedchat

Contacts and cooperation:
I recommend our hosting beget.com
Write to info@aredel.com if you:
1. Want to write an article for our site or translate an article into your native language.
2. Want to place thematically relevant ads on the site.
3. Ads on my site pass maximum censorship. If you see an ad block unsuitable for school-age children, shocking or misleading - please contact us by e-mail
4. Found a mistake, inaccuracy, bug, etc. on the site. ... .......
5. Articles can be shared on social media by clicking a network icon: