Dataslope logoDataslope

Higher-Order Functions

Functions that take functions, return functions, or both — and why this single idea is the engine of LINQ

A higher-order function (HOF) is a function that does at least one of these two things:

  1. Takes another function as an argument.
  2. Returns a function as its result.

That's the whole definition. It sounds abstract, but you have already been using HOFs every time you wrote .Where(n => n % 2 == 0). Where is a higher-order function: it takes another function (the predicate) as a parameter.

This page is about why that idea is so powerful, and how to wield it in everyday C#.

The pattern before the name

Look at three "search" methods written in classic imperative style:

Code Block
C# 13

These three methods are the same method except for one expression: the condition inside the if. That's a code smell. The behavior is what differs, but we've duplicated everything else.

A higher-order function lets us extract behavior as an argument:

Code Block
C# 13

One method, three different conditions, zero duplication. That is the LINQ idea in miniature — and it's exactly what Enumerable.First is, just slightly more polished.

The two function shapes you'll see everywhere

Almost every higher-order function in C# uses one of these two generic delegates:

DelegateShapeUsed for
Func<TIn, TOut>A function that returns a valueMapping, projecting, computing
Action<T>A function that returns voidSide effects (printing, writing)

There's a special case for predicates:

DelegateShapeUsed for
Func<T, bool>A function returning boolFiltering — also written Predicate<T>
Code Block
C# 13

These are values, just like int or string. You can store them in variables, pass them around, return them, put them in lists.

Returning a function

This is the other half of "higher-order". A function that returns a function is sometimes called a function factory.

Code Block
C# 13

Multiplier(2) returns a new function that remembers factor=2. Multiplier(3) returns a different function. The captured factor is part of the returned function's "personality".

A practical example — a predicate factory:

Code Block
C# 13

You can build a library of predicates this way without writing each one from scratch.

Composing functions

Functions are values, so you can write a function that composes two other functions: feed the output of one into the other.

Code Block
C# 13

Compose is the operation in functional programming. Composing small pure functions is how you build large transformations without ever writing a for loop.

Why this is the engine of LINQ

Every LINQ operator is a higher-order function:

Each box accepts a function and uses it to drive its behavior over a sequence. The sequence itself is just data; the behavior is parameterized.

This is the cleanest possible separation of:

  • Structure — the operator (iterate, branch, accumulate)
  • Policy — the function you pass in

You can change the policy without rewriting the structure, and you can reuse the structure across hundreds of policies.

A practical pattern: configurable transformations

A real example: a configurable text "pipeline" built from small named functions.

Code Block
C# 13

The same Pipeline function builds different cleaners depending on which steps you pass in. The steps themselves are pure, tiny, and testable in isolation.

A multi-file challenge

Implement a small reusable HOF utility.

Challenge
C# 13
Build a reusable filter

Implement Filters.Build, a higher-order function that takes a predicate (Func<int, bool>) and returns a function that, given a list of ints, returns only those that match the predicate (in order).

Signature:

public static Func<IEnumerable<int>, IEnumerable<int>> Build(Func<int, bool> predicate)

Program.cs uses it to build an "is positive" filter and prints the matching numbers, one per line.

QuestionSelect one

Which of the following is not a higher-order function?

Enumerable.Where — takes a Func<T, bool> predicate.

Enumerable.Select — takes a Func<TSource, TResult> selector.

A "Multiplier" method that returns Func<int, int> for a given factor.

static int Add(int x, int y) => x + y;

On this page