On Wed, Jul 1, 2026, at 5:02 PM, Rowan Tommins [IMSoP] wrote:
On 1 July 2026 20:02:27 BST, Seifeddine Gmati <azjezz@carthage.software> wrote:
Making `Closure` generic (e.g., say `Closure<Input, Output>` ) would
require `Input` to be variadic to stand in for a closure's parameter
list, and variadic generics are a different beast entirely.
FWIW, C# has a long list of overloads for expressing generic lambda
types, like Action<T1,T2>, Action<T1,T2,T3>, Func<T1,T2,TResult> and so
on and on and on
<https://github.com/dotnet/dotnet/blob/b0f34d51fccc69fd334253924abd8d6853fad7aa/src/runtime/src/libraries/System.Private.CoreLib/src/System/Action.cs>
<https://github.com/dotnet/dotnet/blob/b0f34d51fccc69fd334253924abd8d6853fad7aa/src/runtime/src/libraries/System.Private.CoreLib/src/System/Function.cs>
Ugly, and unlikely to even work in a PHP implementation of generics.
On the other hand, it has an elegant "delegate" syntax for what are
effectively named callable types:
Work with delegate types in C# - C# | Microsoft Learn
That *would* translate well to PHP. Translating one of their examples:
delegate ProcessBookCallback(Book $book): void;
public function processPaperbackBooks(ProcessBookCallback $processBook): void {
foreach ($this->list as $b) {
if ($b->paperback) {
$processBook($b);
}
}
}
Just an additional angle to throw into the mix.
Rowan Tommins
[IMSoP]
I am very much in favor of callable types, in concept. (Which should surprise no one.)
I don't have very strong feelings on the spelling at the moment yet; I suspect this is a case where the parser will dictate to us what is possible, and that will greatly limit our options or just make the decision for us. (That's how we ended up with fn() for short-closures. It was the shortest thing the parser would let us get away with.)
On the question of callable-vs-closure, I agree that today, between FCC and PFA a Closure is absolutely trivial to produce, so we don't need to support the variety of legacy callable formats. The one caveat to that is for compiled code; you cannot store a closure in a serialized form or in generated code; functions and static methods are easy enough to store that way (as a string and array, respectively), ugly as those formats are. Methods, anon functions, etc. however are much harder, and there's no globally standard way of cheating there. I suspect the best we can do without scope creeping ourselves to death is just support closures and leave it to implementers to turn other callable formats into a closure, which isn't that hard these days.
Generics syntax is the wrong format to use for this, full stop. Let's not even go down that pathway. Generics are orthogonal.
The main semantic question is whether callable/closure types should be defineable inline, or only as a reference (as in Rowan's C# example above.) In the past, there's been non-small pushback to defining them inline, as that can make for very hard to read function declarations and if the same signature is used in multiple places, you have to manually keep them in sync.
However, we don't have type aliases (yet), which would resolve that issue. And when proposals have been floated to allow separate definition of a function interface (as Nicolas and I proposed), it's been rejected as too complicated. So we're really at an impasse on this.
My own stance is that we should just define them inline for now, and if that leads to messy function signatures then that's just all the more impetus for us to get off our butts and decide on a way to do type aliases.
That's better than having a one-off syntax for function signature definition but something entirely different for complex union/intersection types, or restricted types (like "positive int"), etc. Let's have all types inline-able, and then a globally-defined alias/reuse mechanism that supports all of them consistently.
The one should not block the other, especially not given how PHP Internals works.
--Larry Garfield