编程

PHP 8.6 版本将引入偏函数应用程序 (Partial Function Application)。

8 2025-12-15 03:40:00

你是否曾经想写一个简单的回调函数,结果却写出了一篇长篇大论——一个塞满了类型、参数顺序混乱和大量样板代码的箭头函数,仅仅为了传递一个值?

好消息是,PHP 8.6 引入了偏函数应用(Partial Function Application),有望让我们的编程工作变得更加轻松。

什么是偏函数应用?

PHP 8.6 中的偏函数应用 (Partial Function Application) 允许你通过调用一个带有部分参数的函数并使用占位符来表示其余参数,从而编写一个“预配置”的可调用对象。PHP 不会直接执行该函数,而是返回一个闭包,其参数列表会根据缺失的部分自动生成。

占位符包括:

  • ? 表示“此处只有一个参数”
  • 表示“传递所有剩余参数”

以下是一个基本示例。

function add4(int $a, int $b, int $c, int $d): int 
{
    return $a + $b + $c + $d;
}

// Fill some now, leave one for later:
$f = add4(1, ?, 3, 4);
// Equivalent to:
$f = static fn(int $b): int => add4(1, $b, 3, 4);

echo $f(2); // 1+2+3+4 = 10

上例所示,我们通过部分应用 add4 函数并传入一些参数,同时使用占位符代替缺失的参数,创建了一个新的可调用对象 $f。然后,我们可以用剩余的参数调用 $f 来获得最终结果。

你也可以将 PFA 称为一等可调用对象的扩展。

你也可以留下多个占位符。

$f = add4(1, ?, 3, ?);
// Equivalent:
$f = static fn(int $b, int $d): int => add4(1, $b, 3, $d);

echo $f(5, 7); // 1+5+3+7 = 16

以及“其他一切”

$f = add4(1, ...);
// Equivalent:
$f = static fn(int $b, int $c, int $d): int => add4(1, $b, $c, $d);

echo $f(2, 3, 4); // 10

有了 PFA,回调函数变得简洁明了,意图清晰。无需再编写重复的箭头函数来重新排列或修改参数。只需在需要的地方插入 ?,PHP 就会自动完成剩下的工作。

$strings = ['hello world', 'hello there'];

// Without PFA (verbose):
$result = array_map(static fn(string $s): string => str_replace('hello', 'hi', $s), $strings);

// With PFA:
$result = array_map(str_replace('hello', 'hi', ?), $strings);
// Each element is fed into the ? at the $subject position.

它对管道操作符也很友好。

$foo
  |> array_map(strtoupper(...), ?)
  |> array_filter(?, is_numeric(...));
// Right side of the pipe needs a unary callable; PFA supplies it concisely.

命名参数和顺序。

function stuff(int $i, string $s, float $f, Point $p, int $m = 0): string { /* ... */ }

// Named values out of order still work:
$c = stuff(?, ?, f: 3.5, p: $point);
// Closure expects (int $i, string $s)

// Named placeholders define their own parameter order:
$c = stuff(s: ?, i: ?, p: ?, f: 3.5);
// Closure expects (string $s, int $i, Point $p)

可变参数函数。

function things(int $i, ?float $f = null, Point ...$points) { /* ... */ }

// Keep variadic open:
$c = things(1, 3.14, ...);
// Closure expects (Point ...$points)

// Force exact count (variadic becomes required slots):
$c = things(?, ?, ?, ?);
// Closure expects (int $i, ?float $f, Point $points0, Point $points1)

你也可以使用 PFA 轻松实现 Thunk 函数

function expensive(int $a, int $b, Point $c) { /* heavy work */ }

// Prefill all, delay execution:
$thunk = expensive(3, 4, $pt, ...); // Closure with zero required params

// Later:
$result = $thunk();

你不能部分应用构造函数(new)。你可以使用静态方法或工厂函数。

$maker = Widget::make(?, size: 10); // OK
$new = new Widget(?, 10);           // Compile error

一个实际示例

这里有一个使用 PFA 的更实际的例子,我们需要向 HTTP 请求添加标头。我们可以预先填充标头名称和值,请求数组稍后再提供。

function addHeader(array $req, string $name, string $value): array 
{
  $req['headers'][$name] = $value; 
  
  return $req;
}

// Hole for the request; prefill header name/value
$withAuth = addHeader(?, 'Authorization', 'Bearer TOKEN');

$req = ['url' => '/me', 'headers' => []];
$req = $withAuth($req);

这样,我们就创建了一个可重用的可调用对象 `$withAuth`,它会将 Authorization 标头添加到我们之后提供的任何请求数组中。

常见的 PFA 模式

以下是一些可以与 PFA 关联的快速模式。

  • 一元回调:array_map(in_array(?, $allowed, strict: true), $input)
  • 从左侧填充,其余部分留空:stuff(1, 'two', …)
  • 命名设置,其余部分留空:stuff(f: 3.14, s: 'two', …)
  • 一等可调用对象(退化情况):func(…)

总结

部分函数应用 (PFA) 将是 PHP 8.6 的一个强大补充,它可以显著减少样板代码,并在使用回调时提高代码清晰度。通过允许您使用占位符预配置函数,PFA 可以轻松创建简洁、意图明确的可调用对象,而无需冗长的箭头函数。

下一篇