Use partial application to turn generic filter conditions into named, intention-revealing callables — "active users", "recent orders", "high-value transactions" — then reuse them across the app instead of repeating the predicate every time.
Every team accumulates domain concepts — “active users”, “high-value orders”, “this month’s signups”. In imperative code, those concepts show up as inline predicates sprinkled across controllers, services, and tests — and they drift over time (“oh, that endpoint uses status !== 'inactive', this one uses active === true”).
With partial application, the concept becomes a named first-class callable. One definition, reused everywhere.
use PinkCrab\FunctionConstructors\GeneralFunctions as F;
use PinkCrab\FunctionConstructors\Arrays;
use PinkCrab\FunctionConstructors\Comparisons as C;
// "Active" means active=true AND email_verified_at is not null.
$isActive = C\groupAnd(
F\propertyEquals('active', true),
fn($user) => $user['email_verified_at'] !== null
);
// "High value" means lifetime_spend >= 10,000.
$isHighValue = fn($user) => ($user['lifetime_spend'] ?? 0) >= 10_000;
// "Recent" means created_at within the last 30 days.
$isRecent = fn($thing) =>
strtotime($thing['created_at']) >= strtotime('-30 days');
// And composite ones.
$isActiveHighValue = C\groupAnd($isActive, $isHighValue);Each is a one-line callable. Each is testable on its own:
var_dump($isActive(['active' => true, 'email_verified_at' => '2024-01-01'])); // true
var_dump($isActive(['active' => true, 'email_verified_at' => null])); // false$activeUsers = array_filter($users, $isActive);
$topCustomers = array_filter($users, $isActiveHighValue);
$recentOrders = array_filter($orders, $isRecent);
$inactiveOverdue = array_filter($users, C\not($isActive));Any reader scanning that code knows exactly what’s being filtered without jumping to the definition. If the definition changes — say “active” now also requires deleted_at === null — every consumer picks it up.
For a CRM-style app where end users can combine filters, store the predicates by name and assemble them at runtime:
$filters = [
'active' => $isActive,
'high_value' => $isHighValue,
'recent' => $isRecent,
];
// Build an AND of whichever filters the user selected.
$selected = ['active', 'high_value'];
$predicate = C\groupAnd(...array_map(
fn($name) => $filters[$name],
$selected
));
$results = array_filter($users, $predicate);C\not($isActive), C\groupOr($isRecent, $isHighValue), etc.