Monads & IO in Haskell
Monads are a powerful abstraction for sequencing computations in Haskell. They provide a way to handle side effects, manage state, and compose operations in a pure functional language.
The Problem with Purity
Haskell is a pure language, meaning functions can't have side effects. This creates a challenge for I/O operations. Monads provide a solution by sequencing operations in a controlled way.
-- This wouldn't work in pure Haskell
-- main = print (readLine()) -- Can't do this!
-- Instead, we use IO monad
main :: IO ()
main = do
input <- getLine
putStrLn ("You said: " ++ input)
The Monad Typeclass
The Monad typeclass defines two main operations:
class Monad m where
(>>=) :: m a -> (a -> m b) -> m b -- bind
return :: a -> m a
-- Derived operations
(>>) :: m a -> m b -> m b
fail :: String -> m a
Maybe Monad
The Maybe monad handles computations that might fail:
-- Maybe definition
data Maybe a = Nothing | Just a
instance Monad Maybe where
return x = Just x
Nothing >>= _ = Nothing
Just x >>= f = f x
-- Example usage
safeDivide :: Double -> Double -> Maybe Double
safeDivide _ 0 = Nothing
safeDivide x y = Just (x / y)
calculate :: Double -> Double -> Maybe Double
calculate x y = do
a <- safeDivide x y
b <- safeDivide (a + 10) y
return (b * 2)
Either Monad
Either is similar to Maybe but can carry error information:
data Either a b = Left a | Right b
instance Monad (Either e) where
return = Right
Left e >>= _ = Left e
Right x >>= f = f x
-- Example usage
safeDivide :: Double -> Double -> Either String Double
safeDivide _ 0 = Left "Division by zero"
safeDivide x y = Right (x / y)
calculate :: Double -> Double -> Either String Double
calculate x y = do
a <- safeDivide x y
b <- safeDivide (a + 10) y
return (b * 2)
List Monad
The List monad models nondeterministic computations:
instance Monad [] where
return x = [x]
xs >>= f = concat (map f xs)
-- Example usage
pairs :: [Int] -> [Int] -> [(Int, Int)]
pairs xs ys = do
x <- xs
y <- ys
return (x, y)
-- Equivalent to list comprehension:
-- pairs xs ys = [(x, y) | x <- xs, y <- ys]
IO Monad
The IO monad handles input/output operations:
-- Simple IO actions
main :: IO ()
main = do
putStrLn "Enter your name:"
name <- getLine
putStrLn ("Hello, " ++ name ++ "!")
-- Sequencing IO actions
greet :: IO ()
greet =
putStr "Hello " >>
putStr "world!" >>
putStrLn ""
-- Reading and writing files
copyFile :: FilePath -> FilePath -> IO ()
copyFile src dest = do
contents <- readFile src
writeFile dest contents
do Notation
The do notation provides imperative-style syntax for monadic code:
-- Without do notation
main :: IO ()
main =
putStrLn "Enter a number:" >>
getLine >>= \input ->
let n = read input :: Int in
putStrLn ("The square is: " ++ show (n * n))
-- With do notation
main :: IO ()
main = do
putStrLn "Enter a number:"
input <- getLine
let n = read input :: Int
putStrLn ("The square is: " ++ show (n * n))
Monad Laws
All monads must satisfy three laws:
-- Left identity
return x >>= f ≡ f x
-- Right identity
m >>= return ≡ m
-- Associativity
(m >>= f) >>= g ≡ m >>= (\x -> f x >>= g)
Common Monad Functions
Useful functions for working with monads:
-- Lift a function into a monad
liftM :: Monad m => (a -> b) -> m a -> m b
liftM f m = m >>= \x -> return (f x)
-- Sequence monadic actions
sequence :: Monad m => [m a] -> m [a]
sequence [] = return []
sequence (m:ms) = do
x <- m
xs <- sequence ms
return (x:xs)
-- Map over a list with a monadic function
mapM :: Monad m => (a -> m b) -> [a] -> m [b]
mapM f xs = sequence (map f xs)
-- Filter with a monadic predicate
filterM :: Monad m => (a -> m Bool) -> [a] -> m [a]
filterM _ [] = return []
filterM p (x:xs) = do
b <- p x
ys <- filterM p xs
return (if b then x:ys else ys)
Creating Custom Monads
You can define your own monads for specific purposes:
-- State monad example
newtype State s a = State { runState :: s -> (a, s) }
instance Monad (State s) where
return x = State $ \s -> (x, s)
m >>= f = State $ \s ->
let (a, s') = runState m s
in runState (f a) s'
-- Reader monad example
newtype Reader r a = Reader { runReader :: r -> a }
instance Monad (Reader r) where
return x = Reader $ \_ -> x
m >>= f = Reader $ \r ->
let a = runReader m r
in runReader (f a) r
← Previous: Higher-Order Functions |
Next: Lazy Evaluation →