100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Programming

Higher-Order Functions in Haskell

Learn how Haskell treats functions as first-class values, and how map, filter, foldr, currying, and composition let you build programs out of small, reusable pieces.

Functional AbstractionsBeginner8 min readJul 10, 2026
Analogies

What Makes a Function 'Higher-Order'?

In Haskell, functions are first-class values: they can be bound to names, stored in data structures, passed as arguments, and returned as results, exactly like an Int or a String. A higher-order function is simply a function that takes one or more functions as parameters, returns a function, or both. For example, twice :: (a -> a) -> a -> a; twice f x = f (f x) takes a function f and applies it twice, so twice (+3) 10 evaluates to 16 without twice needing to know anything about addition specifically -- the caller supplies the behavior.

🏏

Cricket analogy: Think of a bowling coach who hands different bowlers -- say Jasprit Bumrah for pace, Ravindra Jadeja for spin -- the same over-strategy card; the strategy itself is a first-class object being passed around, just like twice takes any function f and doesn't care whether it's addition or string-reversal.

Passing Functions as Arguments: map, filter, and foldr

Haskell's standard library leans heavily on higher-order functions to replace hand-written loops. map :: (a -> b) -> [a] -> [b] applies a function to every element of a list, filter :: (a -> Bool) -> [a] -> [a] keeps only elements satisfying a predicate, and foldr :: (a -> b -> b) -> b -> [a] -> b collapses a list into a single value by combining elements from the right with a supplied combining function. Because the traversal logic is fixed inside map, filter, and foldr, only the per-element behavior changes between call sites, which is exactly what a higher-order function is for.

🏏

Cricket analogy: A team analyst applying filter isSixHitter to the squad list to shortlist power hitters like Rohit Sharma mirrors Haskell's filter: the traversal of the squad never changes, only the predicate deciding who stays.

haskell
-- Squares of even numbers, then their sum
processNumbers :: [Int] -> Int
processNumbers xs = foldr (+) 0 (map (^2) (filter even xs))

main :: IO ()
main = print (processNumbers [1..10])
-- 220  (2^2 + 4^2 + 6^2 + 8^2 + 10^2)

Because map's first argument is a function, you can hand it a partially applied function like map (*2) or a composed one like map (show . (*2)) -- the traversal code in map never changes, only what you plug into its function slot.

Currying and Partial Application

Every function in Haskell that appears to take multiple arguments is actually curried: add :: Int -> Int -> Int is sugar for Int -> (Int -> Int), a function that takes one Int and returns another function expecting the second Int. This lets you partially apply a function by supplying fewer arguments than its full arity, producing a new, more specific function -- addFive = add 5 is a function Int -> Int that adds five to whatever it's given, built without writing a lambda.

🏏

Cricket analogy: A net bowler practicing with a fixed run-up (partially set) but varying only the delivery type each ball is like addFive = add 5 -- the run-up (first argument) is locked in, producing a specialized function that only needs the final delivery choice.

Function Composition and Point-Free Style

The (.) operator composes two functions, (f . g) x = f (g x), letting you build a new function by chaining existing ones right-to-left without ever naming the intermediate argument -- this is called point-free (or 'pointless') style. countUpper = length . filter isUpper reads as 'filter for uppercase, then take the length,' and because it never mentions the input string explicitly, it stays entirely at the level of the transformation being performed.

🏏

Cricket analogy: Composing a batting drill of 'first play forward defense, then rotate strike' into one combined routine, like (rotateStrike . forwardDefense), chains two specific skills into a single point-free routine a coach can call without re-explaining each step.

Point-free style is elegant for two- or three-stage pipelines like sum . map (^2) . filter even, but chaining five or six compositions with no named intermediate values can make code hard to debug -- when something in the pipeline misbehaves there's no named variable to inspect. Most Haskell style guides recommend switching back to named arguments once a composition chain stops being immediately readable.

  • Functions in Haskell are first-class values that can be passed, returned, and stored like any other data.
  • A higher-order function takes a function as an argument, returns one as a result, or both.
  • map, filter, and foldr are the core list-processing HOFs and replace most manual loops and recursion.
  • All Haskell functions are curried by default, which is what makes partial application possible.
  • Partial application creates a new, more specific function by supplying fewer arguments than the full arity.
  • The (.) operator composes functions right-to-left and enables point-free style.
  • Overusing point-free composition can hurt readability; balance conciseness with clarity.

Practice what you learned

Was this page helpful?

Topics covered

#Programming#HaskellStudyNotes#HigherOrderFunctionsInHaskell#Higher#Order#Functions#Haskell#StudyNotes#SkillVeris#ExamPrep