On Wed, Jul 1, 2026 at 5:43 PM Matheus Martins <[email protected]> wrote:

> Hello internals,
>
> I would like to revisit the idea of giving closures a typed call signature.
>
> e.g. Closure(int, string): array -- enforced at the point a value crosses a
> type boundary (an argument, a return, a property), the same places any
> other
> type is checked.
>
> Today a closure can only be typed as Closure or callable, neither of which
> says anything about its parameters or return, even though that information
> is
> right there.
>
> I know this is not new ground: Callable Prototypes was declined in 2016,
> and
> Garfield and Grekas shared two further RFCs in 2023 -- Structural Typing
> for
> Closures, and Allow Closures to Declare Interfaces they Implement -- both
> still
> in draft.
>
> Before taking it further, I would like to know whether closure typing
> is still considered worth pursuing -- or whether the topic is now regarded
> as
> settled.
>
> References:
>
> - https://wiki.php.net/rfc/callable-types
> - https://wiki.php.net/rfc/structural-typing-for-closures
> -
> https://wiki.php.net/rfc/allow-closures-to-declare-interfaces-they-implement
>
> Thanks.
>

Hi Matheus,

This is something I care about and took a real run at recently, but I came
at it from a different angle, an `Invokable` marker interface PR
<https://github.com/php/php-src/pull/21574> in which
Gina pointed me at function types as the proper solution to what I was
trying to address.
I was convinced, withdrew the PR, and started exploring the same thing you
are exploring now.

So let me hand you what I ran into, in case it helps...

Two things to put in your bag before you invest:

   1. The syntax collides at the lexer. `Closure(int): array` can't be
   tokenized cleanly, because `(int)` is a cast token (same for `(string)`,
   `(array)`, and so on).
   It's been that way for years, and the one attempt to fix it (PR
   https://github.com/php/php-src/pull/1667) was rejected as a token-stream
   BC break.
   So the natural spelling is, unfortunately, the engine-hostile one.
   2. There's a single `Closure` class (Ilija's point here:
   https://externals.io/message/120083#120099), so `Closure(int, string):
   array` isn't a subtype you can instanceof...
   it has to be checked another way at the boundary, which is where the
   usual per-boundary runtime-cost concern comes in.

Hope you find that useful.

Best,
Osama

Reply via email to