IO Maybe Either a

Types are theorems.
Programs are proofs.


Purity

In Haskell, a function is a mathematical function. Given the same inputs, it will always produce the same output. There are no hidden dependencies, no ambient state mutations, no temporal coupling. This constraint, which appears limiting at first, turns out to be the most liberating design decision in the history of programming language design. When effects are tracked in the type system, the compiler becomes your most trusted collaborator.

1module Purity where
2
3import Data.List (foldl')
4
5-- A pure function: no side effects, total, referentially transparent
6fibonacci :: Integer -> Integer
7fibonacci 0 = 0
8fibonacci 1 = 1
9fibonacci n = fibonacci (n - 1) + fibonacci (n - 2)
10
11-- Laziness makes infinite structures finite in practice
12fibs :: [Integer]
13fibs = 0 : 1 : zipWith (+) fibs (tail fibs)
14
15-- Every value is immutable. Every computation is a declaration.
16golden :: Double
17golden = (1 + sqrt 5) / 2
18
19-- Equational reasoning: we can substitute equals for equals
20sumSquares :: [Int] -> Int
21sumSquares = foldl' (+) 0 . map (^ 2)

Laziness

Haskell evaluates nothing until it must. This is not indolence; it is strategic patience. Lazy evaluation means that data structures are promises, not values -- they materialize only at the moment of consumption. This permits the programmer to define infinite structures, to separate the description of a computation from its execution, and to compose producers and consumers without worrying about intermediate allocation.

1module Laziness where
2
3-- The natural numbers: all of them
4nats :: [Integer]
5nats = [0..]
6
7-- The Sieve of Eratosthenes, rendered as a one-liner
8primes :: [Integer]
9primes = sieve [2..]
10 where
11 sieve (p:xs) = p : sieve [x | x <- xs, x `mod` p /= 0]
12 sieve [] = []
13
14-- Streams that generate themselves
15data Stream a = Cons a (Stream a)
16
17headS :: Stream a -> a
18headS (Cons x _) = x
19
20tailS :: Stream a -> Stream a
21tailS (Cons _ xs) = xs
22
23iterateS :: (a -> a) -> a -> Stream a
24iterateS f x = Cons x (iterateS f (f x))

Type Classes

Where other languages reach for interfaces or abstract classes, Haskell offers type classes -- a mechanism for ad-hoc polymorphism that is simultaneously more principled and more expressive than anything in the object-oriented tradition. A type class is a contract written in the language of types, and an instance is a proof that a specific type satisfies that contract. The hierarchy from Functor through Applicative to Monad is not an arbitrary taxonomy but a ladder of increasing power, each rung precisely defined.

1class Functor f where
2 fmap :: (a -> b) -> f a -> f b
3
4class Functor f => Applicative f where
5 pure :: a -> f a
6 (<*>) :: f (a -> b) -> f a -> f b
7
8class Applicative m => Monad m where
9 (>>=) :: m a -> (a -> m b) -> m b
10 return = pure
11
12-- Laws that every instance must obey:
13-- fmap id = id (identity)
14-- fmap (f . g) = fmap f . fmap g (composition)
15
16-- A concrete instance: the list functor
17instance Functor [] where
18 fmap = map
19
20instance Applicative [] where
21 pure x = [x]
22 fs <*> xs = [f x | f <- fs, x <- xs]
A B F f g h

Monads

A monad is not a burrito. A monad is not a space suit. A monad is a design pattern for composing computations that carry context. In Haskell, monads are how we sequence operations, handle errors gracefully, manage state explicitly, and interact with the outside world -- all while maintaining the purity guarantees that make our programs trustworthy. The bind operator threads context through a computation the way gravity threads mass through spacetime: invisibly, inevitably, and with mathematical precision.

1module Monads where
2
3import Control.Monad ((>=>))
4
5-- The Maybe monad: computation that might fail
6safeDivide :: Double -> Double -> Maybe Double
7safeDivide _ 0 = Nothing
8safeDivide x y = Just (x / y)
9
10-- Composing fallible computations with do-notation
11compute :: Double -> Double -> Double -> Maybe Double
12compute x y z = do
13 a <- safeDivide x y
14 b <- safeDivide a z
15 pure (a + b)
16
17-- IO: the monad that touches the world
18greet :: IO ()
19greet = do
20 putStrLn "What is your name?"
21 name <- getLine
22 putStrLn ("Hello, " <> name <> ".")
23
24-- State: explicit mutation without impurity
25newtype State s a = State { runState :: s -> (a, s) }
26
27get :: State s s
28get = State (\s -> (s, s))
29
30put :: s -> State s ()
31put s = State (\_ -> ((), s))
A B C f g g . f

Composition

The deepest insight of functional programming is that complex systems are built from simple, composable parts. In Haskell, function composition is not a technique -- it is the fundamental operation. The dot operator joins two functions into one. The fish operator composes monadic functions. At every level of abstraction, the same principle holds: build small things that work, then compose them into larger things that also work. This is not optimism; it is a theorem.

1module Composition where
2
3import Data.List (sort, nub)
4import Text.Read (readMaybe)
5import Control.Monad ((>=>))
6
7-- Function composition: the essence of modularity
8process :: [String] -> String
9process = unlines
10 . map (" " <>)
11 . filter (not . null)
12 . sort
13 . nub
14
15-- Kleisli composition: composing monadic functions
16pipeline :: String -> Maybe Int
17pipeline = readMaybe >=> validate >=> transform
18 where
19 validate n
20 | n > 0 = Just n
21 | otherwise = Nothing
22 transform = Just . (* 2) . (+ 1)
23
24-- Higher-order composition: functions that build functions
25on :: (b -> b -> c) -> (a -> b) -> a -> a -> c
26on f g x y = f (g x) (g y)
27
28-- The ultimate composition: a program is a value
29main :: IO ()
30main = interact (unlines . map process . lines)

-- end of module