Haskell Functional Programming Patterns

Haskell's functional style is built on a few powerful patterns that compose to create complex behaviour from simple pieces.

Function composition

HASKELL
-- The (.) operator composes functions right to left
-- (f . g) x = f (g x)

isEvenSum :: [Int] -> Bool
isEvenSum = even . sum

-- Reading right to left: sum the list, then check if even
isEvenSum [1, 2, 3]  -- True (1+2+3=6, 6 is even)

Currying

All Haskell functions are curried — they take one argument and return a function that takes the next:

HASKELL
-- add :: Int -> Int -> Int
-- is actually: add :: Int -> (Int -> Int)
-- add 5 returns a function that adds 5 to its argument

add5 = add 5
add5 3  -- 8

Partial application

HASKELL
-- apply a function to some arguments, get a new function
take2 = take 2
take2 [1, 2, 3]  -- [1, 2]

-- Very common pattern
filter (> 5) [1..10]   -- [6, 7, 8, 9, 10]
map (* 2) [1, 2, 3]    -- [2, 4, 6]

Higher-order functions

HASKELL
-- Apply a function twice
twice :: (a -> a) -> a -> a
twice f x = f (f x)

twice (*2) 3    -- 12
twice reverse "ab"  -- "ab" (reverse is its own inverse)

-- Curry a two-argument function
uncurry :: (a -> b -> c) -> (a, b) -> c
uncurry f (a, b) = f a b

Algebraic data types as data

HASKELL
data Expr
    = Lit Double
    | Add Expr Expr
    | Mul Expr Expr
    | Neg Expr

eval :: Expr -> Double
eval (Lit x)     = x
eval (Add a b)   = eval a + eval b
eval (Mul a b)   = eval a * eval b
eval (Neg a)     = negate (eval a)

-- (3 + 4) * 2
example :: Expr
example = Mul (Add (Lit 3) (Lit 4)) (Lit 2)

eval example  -- 14.0

Folds

HASKELL
-- foldl: left fold (accumulator on the left)
-- foldr: right fold (accumulator on the right)

sum :: Num a => [a] -> a
sum = foldl (+) 0

product :: Num a => [a] -> a
product = foldl (*) 1

-- foldr is natural for building lists
filter' :: (a -> Bool) -> [a] -> [a]
filter' pred = foldr (\x acc -> if pred x then x : acc else acc) []

Tips

  • Think of functions as transformations, not procedures.
  • Use . (composition) to chain small functions into pipelines.
  • Partial application is everywhere — embrace it.
  • Use algebraic data types to model your domain precisely.

Next: monads.