DEV Community

Cover image for 10x Readability with Higher-order Predicates
Pierluigi Pesenti
Pierluigi Pesenti

Posted on

10x Readability with Higher-order Predicates

Predicates

A predicate is a pure function that accepts any arguments and returns a boolean.

const isEven = (n: number) => n % 2 === 0;

console.log(isEven(2)); // true
Enter fullscreen mode Exit fullscreen mode

Predicates are fundamental in programming, and their usage is widespread across various paradigms.

Higher-order Functions

A higher-order function is a function that accepts arguments and returns a specialized (partially-applied) function.

const times = (m: number) => (n: number) => n * m;

// e.g. inline:
const doubledInline = numbers.map(times(2));

// e.g.:
const double = times(2);
const doubled = numbers.map(double);
Enter fullscreen mode Exit fullscreen mode

When the outermost function is invoked, it returns a new function with the first argument "baked in," awaiting the second argument. This is known as currying.

Currying enables partially-applied functions to be stored in variables or used inline. In both cases, it simplifies extraction of functions that would otherwise require explicit inline parameters. Those functions can now be used point-free.

Point-free Style

Point-free programming has nothing to do with points in a geometric sense or with the dot-syntax. Here, a "point" refers to an argument.

const doubledAgain = numbers.map((n) => n * 2);
Enter fullscreen mode Exit fullscreen mode

In this example, n is the "point."

This style introduces some issues:

  1. The 2 is hardcoded. The only alternative without resorting to a higher-order function (HoF) would be to retrieve it from scope, compromising purity.
  2. It is less readable compared to previous examples.

Consider the earlier examples:

const doubledInline = numbers.map(times(2));
// ...
const doubled = numbers.map(double);
Enter fullscreen mode Exit fullscreen mode

In both cases, the .map() calls are point-free. Using a higher-order function (HoF) allows the 2 to be embedded directly into the function—either inline (times(2)) or pre-stored as a partially-applied function (double).

Higher-Order Predicates: The Key to 10x Readability and Composition

A higher-order predicate is a function that accepts arguments and returns a specialized predicate.

By using higher-order predicates, you can achieve the same level of readability and reusability as seen with mappers, but applied to filter functions (or similar methods like find, every, and some).

const isDivisibleBy = (divisor: number) => (dividend: number) => dividend % divisor === 0;

[1, 2, 3, 4].filter(isDivisibleBy(2)); // [2, 4]
Enter fullscreen mode Exit fullscreen mode

This approach encourages the creation and organization of reusable predicates and higher-order predicates into semantic groupings, enhancing discoverability and readability. Additionally, they are straightforward to unit test.

For a ready-to-use library, consider fp-filters, a curated collection of 130+ predicates. It is fully typed, 100% tested, and open for contributions.

AWS Q Developer image

What is MCP? No, Really!

See MCP in action and explore how MCP decouples agents from servers, allowing for seamless integration with cloud-based resources and remote functionality.

Watch the demo

Top comments (0)

Feature flag article image

Create a feature flag in your IDE in 5 minutes with LaunchDarkly’s MCP server ⏰

How to create, evaluate, and modify flags from within your IDE or AI client using natural language with LaunchDarkly's new MCP server. Follow along with this tutorial for step by step instructions.

Read full post

👋 Kindness is contagious

Sign in to DEV to enjoy its full potential—unlock a customized interface with dark mode, personal reading preferences, and more.

Okay