Haskell IO Guide — Input/Output, File Handling, and Pure vs Impure Separation
In this tutorial, you will learn about Haskell IO Guide. We cover key concepts, practical examples, and best practices to help you master this topic.
Haskell IO operations have the IO type, separating pure functions from side effects -- console I/O with getLine/putStrLn, file I/O with readFile/writeFile, and handle-based I/O with hGetLine for fine-grained control.
Console I/O
main :: IO ()
main = do
putStrLn "What is your name?"
name <- getLine
putStrLn $ "Hello, " ++ name ++ "!"
-- Print without newline
putStr "Enter age: "
age <- readLn :: IO Int
print (age + 1)
File I/O
-- Simple file operations
main = do
-- Read entire file
content <- readFile "input.txt"
putStrLn content
-- Write file
writeFile "output.txt" "Hello, Haskell!"
-- Append
appendFile "log.txt" "New log entry\n"
-- Read and process
contents <- readFile "data.txt"
let lines' = lines contents
count = length lines'
writeFile "stats.txt" $ show count ++ " lines\n"
Handle-Based I/O
import System.IO
main = do
-- Open file handle
handle <- openFile "data.txt" ReadMode
-- Read line by line
line <- hGetLine handle
putStrLn line
-- Read rest
rest <- hGetContents handle
putStrLn rest
hClose handle
-- WithFile (automatic close)
withFile "data.txt" ReadMode $ \handle -> do
content <- hGetContents handle
putStrLn content
Lazy I/O
-- readFile is lazy -- reads as needed
main = do
content <- readFile "large.txt"
-- Only first line is read from disk
let firstLine = head (lines content)
putStrLn firstLine
-- Rest is read on demand
-- Process line by line without loading all
main = do
content <- readFile "log.txt"
mapM_ putStrLn $ filter ("ERROR" `isPrefixOf`) (lines content)
Common Mistakes
1. Mixing pure and impure code
Pure functions can't call IO functions. Pass IO results as arguments to pure functions.
2. Not closing file handles
Use withFile or readFile for automatic resource management. openFile requires explicit hClose.
3. Lazy I/O surprises
Lazy I/O reads file as content is used. If the file handle closes before content is evaluated, you get empty results.
Practice Questions
1. How do you read a file line by line?
Use readFile and lines to split, or hGetLine with handle-based I/O for step-by-step reading.
2. What is the difference between putStr and putStrLn? putStr prints a string without a trailing newline. putStrLn adds a newline.
3. How do you read a number from input?
readLn :: IO Int reads a line and parses it as an Int. Use readMaybe from Text.Read for safe Parsing.
FAQ
{{< faq question="What is the IO type?" >}} IO a is an action that, when executed, may perform I/O and returns a value of type a. It separates pure from impure code. {{< /faq >}}
{{< faq question="How do I safely close file handles?" >}}
Use withFile which automatically closes the handle after the action completes, even on exceptions.
{{< /faq >}}
{{< faq question="What is hGetContents?" >}} Reads the entire file from a handle, but lazily -- it reads as the content is consumed. Works with standard functions like lines. {{< /faq >}}
What's Next
Now learn about record syntax in Haskell.
| Topic | Description | Link |
|---|---|---|
| Records | Data record syntax | {{< ref "13-records" >}} |
| Typeclasses | Typeclass basics | {{< ref "14-typeclasses" >}} |
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro